@staff0rd/assist 0.660.2 → 0.662.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 +6 -1
- package/allowed.cli-reads +1 -0
- package/dist/allowed.cli-reads +1 -0
- package/dist/commands/sessions/web/bundle.js +379 -379
- package/dist/index.js +829 -327
- 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.662.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -651,8 +651,7 @@ var DEFAULT_BACKUP_DIR = "~/.assist/backups";
|
|
|
651
651
|
var DEFAULT_CLONE_DIR = "~/git";
|
|
652
652
|
var assistConfigShape = {
|
|
653
653
|
advice: z3.strictObject({
|
|
654
|
-
|
|
655
|
-
exclude: z3.array(z3.enum(adviceFragmentNames)).default([]),
|
|
654
|
+
fragments: z3.partialRecord(z3.enum(adviceFragmentNames), z3.boolean()).default({}),
|
|
656
655
|
extra: z3.string().optional(),
|
|
657
656
|
verify: z3.string().optional()
|
|
658
657
|
}).optional(),
|
|
@@ -717,7 +716,12 @@ var assistConfigShape = {
|
|
|
717
716
|
readingWordsPerMinute: z3.number().int().positive().optional()
|
|
718
717
|
}).optional(),
|
|
719
718
|
review: z3.strictObject({
|
|
720
|
-
codexModel: z3.string().optional()
|
|
719
|
+
codexModel: z3.string().optional(),
|
|
720
|
+
highLevel: z3.strictObject({
|
|
721
|
+
criticalPaths: z3.array(z3.string()).default([]),
|
|
722
|
+
uiPaths: z3.array(z3.string()).default([]),
|
|
723
|
+
descriptionWordCap: z3.number().int().positive().default(300)
|
|
724
|
+
}).optional()
|
|
721
725
|
}).optional(),
|
|
722
726
|
readTime: z3.strictObject({
|
|
723
727
|
wordsPerMinute: z3.number().int().positive().default(200)
|
|
@@ -1941,10 +1945,10 @@ import fs11 from "fs";
|
|
|
1941
1945
|
|
|
1942
1946
|
// src/commands/lint/shared.ts
|
|
1943
1947
|
import chalk24 from "chalk";
|
|
1944
|
-
function reportViolations2(violations,
|
|
1948
|
+
function reportViolations2(violations, checkName2, errorMessage, successMessage) {
|
|
1945
1949
|
if (violations.length > 0) {
|
|
1946
1950
|
console.error(chalk24.red(`
|
|
1947
|
-
${
|
|
1951
|
+
${checkName2} failed:
|
|
1948
1952
|
`));
|
|
1949
1953
|
console.error(chalk24.red(` ${errorMessage}
|
|
1950
1954
|
`));
|
|
@@ -2865,8 +2869,7 @@ function configHelp(command, entries, preamble) {
|
|
|
2865
2869
|
|
|
2866
2870
|
// src/shared/configEnumDescriptions.ts
|
|
2867
2871
|
var byKey = {
|
|
2868
|
-
"advice.
|
|
2869
|
-
"advice.exclude": adviceFragmentTitles
|
|
2872
|
+
"advice.fragments": adviceFragmentTitles
|
|
2870
2873
|
};
|
|
2871
2874
|
function configEnumDescriptions(key) {
|
|
2872
2875
|
return byKey[key];
|
|
@@ -2997,11 +3000,24 @@ function unionOfObjectsConfigNode(inner, base, build2) {
|
|
|
2997
3000
|
}
|
|
2998
3001
|
|
|
2999
3002
|
// src/shared/recordConfigNode.ts
|
|
3003
|
+
function enumKeyValues(keyType, base, build2) {
|
|
3004
|
+
if (!keyType) return void 0;
|
|
3005
|
+
const key = build2(keyType, base.path);
|
|
3006
|
+
return key.kind === "scalar" ? key.enumValues : void 0;
|
|
3007
|
+
}
|
|
3000
3008
|
function recordConfigNode(inner, base, build2) {
|
|
3001
3009
|
const valueType = inner.def?.valueType;
|
|
3002
3010
|
if (inner.def?.type !== "record" || !valueType) return void 0;
|
|
3003
3011
|
const value = build2(valueType, [...base.path, { kind: "entry" }]);
|
|
3004
|
-
|
|
3012
|
+
const keyValues = enumKeyValues(inner.def?.keyType, base, build2);
|
|
3013
|
+
const keyDescriptions = keyValues ? configEnumDescriptions(formatConfigPath(base.path)) : void 0;
|
|
3014
|
+
return {
|
|
3015
|
+
...base,
|
|
3016
|
+
kind: "record",
|
|
3017
|
+
value,
|
|
3018
|
+
...keyValues ? { keyValues } : {},
|
|
3019
|
+
...keyDescriptions ? { keyDescriptions } : {}
|
|
3020
|
+
};
|
|
3005
3021
|
}
|
|
3006
3022
|
|
|
3007
3023
|
// src/shared/scalarConfigNode.ts
|
|
@@ -3096,14 +3112,9 @@ function enumerateConfigLeafKeys(schema2) {
|
|
|
3096
3112
|
// src/commands/advise/adviceConfigHelp.ts
|
|
3097
3113
|
var adviceConfigHelp = [
|
|
3098
3114
|
{
|
|
3099
|
-
key: "advice.
|
|
3100
|
-
setter:
|
|
3101
|
-
note: "fragment
|
|
3102
|
-
},
|
|
3103
|
-
{
|
|
3104
|
-
key: "advice.exclude",
|
|
3105
|
-
setter: 'assist config set advice.exclude "jira-context"',
|
|
3106
|
-
note: "fragment names dropped even when their condition matches; 'assist advise --explain' lists every name"
|
|
3115
|
+
key: "advice.fragments",
|
|
3116
|
+
setter: "assist config set advice.fragments.verify false",
|
|
3117
|
+
note: "force a fragment on (true) or off (false) whatever its condition says; 'assist advise --explain' lists every name"
|
|
3107
3118
|
},
|
|
3108
3119
|
{
|
|
3109
3120
|
key: "advice.extra",
|
|
@@ -3363,6 +3374,21 @@ var reviewConfigHelp = [
|
|
|
3363
3374
|
key: "review.codexModel",
|
|
3364
3375
|
setter: "assist config set review.codexModel gpt-5-codex",
|
|
3365
3376
|
note: "optional; runs the codex reviewer on this model via the LiteLLM proxy (needs litellm.baseUrl and litellm.apiKey)"
|
|
3377
|
+
},
|
|
3378
|
+
{
|
|
3379
|
+
key: "review.highLevel.criticalPaths",
|
|
3380
|
+
setter: 'assist config set review.highLevel.criticalPaths "**/*.graphql,en-AU/translation.json"',
|
|
3381
|
+
note: "comma-separated globs whose full diffs the high-level review shows; unset shows none"
|
|
3382
|
+
},
|
|
3383
|
+
{
|
|
3384
|
+
key: "review.highLevel.uiPaths",
|
|
3385
|
+
setter: 'assist config set review.highLevel.uiPaths "src/ui/**"',
|
|
3386
|
+
note: "comma-separated globs that make a change a UI change, so --high-level requires a screenshot or video; unset requires none"
|
|
3387
|
+
},
|
|
3388
|
+
{
|
|
3389
|
+
key: "review.highLevel.descriptionWordCap",
|
|
3390
|
+
setter: "assist config set review.highLevel.descriptionWordCap 300",
|
|
3391
|
+
note: "word cap --high-level holds the PR description to (default 300)"
|
|
3366
3392
|
}
|
|
3367
3393
|
];
|
|
3368
3394
|
|
|
@@ -5010,12 +5036,12 @@ function addViteBaseConfig() {
|
|
|
5010
5036
|
console.log("vite.config.ts already has base config");
|
|
5011
5037
|
return;
|
|
5012
5038
|
}
|
|
5013
|
-
const
|
|
5039
|
+
const updated2 = content.replace(
|
|
5014
5040
|
/defineConfig\(\{/,
|
|
5015
5041
|
'defineConfig({\n base: "./",'
|
|
5016
5042
|
);
|
|
5017
|
-
if (
|
|
5018
|
-
writeFileSync13(viteConfigPath,
|
|
5043
|
+
if (updated2 !== content) {
|
|
5044
|
+
writeFileSync13(viteConfigPath, updated2);
|
|
5019
5045
|
console.log('Added base: "./" to vite.config.ts');
|
|
5020
5046
|
}
|
|
5021
5047
|
}
|
|
@@ -5349,13 +5375,14 @@ var adviceConditions = {
|
|
|
5349
5375
|
|
|
5350
5376
|
// src/commands/advise/selectAdvice.ts
|
|
5351
5377
|
function decide(fragment, context) {
|
|
5352
|
-
const
|
|
5353
|
-
const
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5378
|
+
const fragments = context.config.advice?.fragments ?? {};
|
|
5379
|
+
const override = fragments[fragment.name];
|
|
5380
|
+
if (override !== void 0)
|
|
5381
|
+
return {
|
|
5382
|
+
fragment,
|
|
5383
|
+
included: override,
|
|
5384
|
+
reason: `advice.fragments.${fragment.name} is ${override}`
|
|
5385
|
+
};
|
|
5359
5386
|
const condition = adviceConditions[fragment.when];
|
|
5360
5387
|
if (!condition)
|
|
5361
5388
|
return {
|
|
@@ -10716,16 +10743,16 @@ async function patchItemStatus(req, res, id) {
|
|
|
10716
10743
|
const result = await findItemOr404(res, id);
|
|
10717
10744
|
if (!result) return;
|
|
10718
10745
|
await updateStatus(result.orm, id, status3);
|
|
10719
|
-
const
|
|
10720
|
-
respondJson(res, 200,
|
|
10746
|
+
const updated2 = { ...result.item, status: status3 };
|
|
10747
|
+
respondJson(res, 200, updated2);
|
|
10721
10748
|
}
|
|
10722
10749
|
async function patchItemStar(req, res, id) {
|
|
10723
10750
|
const { starred } = await parseStarBody(req);
|
|
10724
10751
|
const result = await findItemOr404(res, id);
|
|
10725
10752
|
if (!result) return;
|
|
10726
10753
|
await updateStarred(result.orm, id, starred);
|
|
10727
|
-
const
|
|
10728
|
-
respondJson(res, 200,
|
|
10754
|
+
const updated2 = { ...result.item, starred };
|
|
10755
|
+
respondJson(res, 200, updated2);
|
|
10729
10756
|
}
|
|
10730
10757
|
|
|
10731
10758
|
// src/commands/backlog/web/deleteItemComment.ts
|
|
@@ -13045,15 +13072,15 @@ function setNestedValue(obj, path91, value) {
|
|
|
13045
13072
|
}
|
|
13046
13073
|
|
|
13047
13074
|
// src/commands/config/validateConfig.ts
|
|
13048
|
-
function validateConfig(
|
|
13049
|
-
const result = schema2.safeParse(stripLegacyConfigKeys(
|
|
13075
|
+
function validateConfig(updated2, key, schema2 = assistConfigSchema) {
|
|
13076
|
+
const result = schema2.safeParse(stripLegacyConfigKeys(updated2));
|
|
13050
13077
|
if (result.success) return { ok: true };
|
|
13051
13078
|
const errors = result.error.issues.flatMap(
|
|
13052
13079
|
(issue) => formatIssue2(issue, key)
|
|
13053
13080
|
);
|
|
13054
13081
|
return {
|
|
13055
13082
|
ok: false,
|
|
13056
|
-
errors: scrubConfigSecrets(errors,
|
|
13083
|
+
errors: scrubConfigSecrets(errors, updated2, describeConfigNode(schema2))
|
|
13057
13084
|
};
|
|
13058
13085
|
}
|
|
13059
13086
|
function formatIssue2(issue, key) {
|
|
@@ -13076,14 +13103,14 @@ function applyConfigSet(key, coerced, global, cwd = process.cwd(), globalConfigP
|
|
|
13076
13103
|
};
|
|
13077
13104
|
}
|
|
13078
13105
|
const raw = global ? loadGlobalConfigRaw(globalConfigPath) : loadProjectConfig(cwd);
|
|
13079
|
-
const
|
|
13080
|
-
const validation = validateConfig(
|
|
13106
|
+
const updated2 = setNestedValue(raw, key, coerced);
|
|
13107
|
+
const validation = validateConfig(updated2, key);
|
|
13081
13108
|
if (!validation.ok) return validation;
|
|
13082
13109
|
if (global) {
|
|
13083
|
-
saveGlobalConfig(
|
|
13110
|
+
saveGlobalConfig(updated2, globalConfigPath);
|
|
13084
13111
|
return { ok: true, target: "global" };
|
|
13085
13112
|
}
|
|
13086
|
-
saveConfig(
|
|
13113
|
+
saveConfig(updated2, cwd);
|
|
13087
13114
|
return { ok: true, target: "project" };
|
|
13088
13115
|
}
|
|
13089
13116
|
|
|
@@ -16467,20 +16494,20 @@ function hasListMutations(options2) {
|
|
|
16467
16494
|
options2.add && options2.add.length > 0 || options2.edit || options2.remove
|
|
16468
16495
|
);
|
|
16469
16496
|
}
|
|
16470
|
-
function applyListMutations(current, options2,
|
|
16497
|
+
function applyListMutations(current, options2, flags2) {
|
|
16471
16498
|
let items2 = [...current];
|
|
16472
16499
|
if (options2.edit) {
|
|
16473
16500
|
const [rawIndex, ...textParts] = options2.edit;
|
|
16474
16501
|
if (rawIndex === void 0 || textParts.length === 0) {
|
|
16475
16502
|
return {
|
|
16476
16503
|
ok: false,
|
|
16477
|
-
error: `${
|
|
16504
|
+
error: `${flags2.edit} requires an index and replacement text.`
|
|
16478
16505
|
};
|
|
16479
16506
|
}
|
|
16480
16507
|
const parsed = parseListIndex(
|
|
16481
16508
|
rawIndex,
|
|
16482
16509
|
items2.length,
|
|
16483
|
-
`${
|
|
16510
|
+
`${flags2.edit} index`
|
|
16484
16511
|
);
|
|
16485
16512
|
if (!parsed.ok) return parsed;
|
|
16486
16513
|
items2[parsed.index - 1] = textParts.join(" ");
|
|
@@ -16489,7 +16516,7 @@ function applyListMutations(current, options2, flags) {
|
|
|
16489
16516
|
const parsed = parseListIndex(
|
|
16490
16517
|
options2.remove,
|
|
16491
16518
|
items2.length,
|
|
16492
|
-
`${
|
|
16519
|
+
`${flags2.remove} index`
|
|
16493
16520
|
);
|
|
16494
16521
|
if (!parsed.ok) return parsed;
|
|
16495
16522
|
items2 = items2.filter((_, i) => i !== parsed.index - 1);
|
|
@@ -16501,15 +16528,15 @@ function applyListMutations(current, options2, flags) {
|
|
|
16501
16528
|
}
|
|
16502
16529
|
|
|
16503
16530
|
// src/commands/backlog/update/resolveListUpdate.ts
|
|
16504
|
-
function resolveListUpdate(whole, current, mutations, wholeFlag,
|
|
16531
|
+
function resolveListUpdate(whole, current, mutations, wholeFlag, flags2) {
|
|
16505
16532
|
if (!hasListMutations(mutations)) return { ok: true, items: whole };
|
|
16506
16533
|
if (whole) {
|
|
16507
16534
|
return {
|
|
16508
16535
|
ok: false,
|
|
16509
|
-
error: `Cannot combine ${wholeFlag} with ${
|
|
16536
|
+
error: `Cannot combine ${wholeFlag} with ${flags2.add}/${flags2.edit}/${flags2.remove}.`
|
|
16510
16537
|
};
|
|
16511
16538
|
}
|
|
16512
|
-
return applyListMutations(current, mutations,
|
|
16539
|
+
return applyListMutations(current, mutations, flags2);
|
|
16513
16540
|
}
|
|
16514
16541
|
|
|
16515
16542
|
// src/commands/backlog/resolvePhaseFields.ts
|
|
@@ -18428,8 +18455,8 @@ function loadDenyConfig(global) {
|
|
|
18428
18455
|
const deny = config.deny ?? [];
|
|
18429
18456
|
return {
|
|
18430
18457
|
deny,
|
|
18431
|
-
saveDeny: (
|
|
18432
|
-
config.deny =
|
|
18458
|
+
saveDeny: (updated2) => {
|
|
18459
|
+
config.deny = updated2;
|
|
18433
18460
|
save(config);
|
|
18434
18461
|
}
|
|
18435
18462
|
};
|
|
@@ -27471,8 +27498,218 @@ async function reply(commentId, body) {
|
|
|
27471
27498
|
}
|
|
27472
27499
|
}
|
|
27473
27500
|
|
|
27474
|
-
// src/commands/prs/
|
|
27501
|
+
// src/commands/prs/status/describeFetchError.ts
|
|
27502
|
+
function firstLine2(text18) {
|
|
27503
|
+
const line = text18.split("\n").map((candidate) => candidate.trim()).find((candidate) => candidate.length > 0);
|
|
27504
|
+
return line ?? null;
|
|
27505
|
+
}
|
|
27506
|
+
function stderrText(error) {
|
|
27507
|
+
const stderr = error?.stderr;
|
|
27508
|
+
if (typeof stderr === "string") return firstLine2(stderr);
|
|
27509
|
+
if (stderr instanceof Uint8Array) {
|
|
27510
|
+
return firstLine2(Buffer.from(stderr).toString("utf8"));
|
|
27511
|
+
}
|
|
27512
|
+
return null;
|
|
27513
|
+
}
|
|
27514
|
+
function describeFetchError(error) {
|
|
27515
|
+
if (isGhNotInstalled(error)) {
|
|
27516
|
+
return "GitHub CLI (gh) is not installed \u2014 see https://cli.github.com/";
|
|
27517
|
+
}
|
|
27518
|
+
const stderr = stderrText(error);
|
|
27519
|
+
if (stderr) return stderr;
|
|
27520
|
+
if (error instanceof Error) return firstLine2(error.message) ?? error.message;
|
|
27521
|
+
return String(error);
|
|
27522
|
+
}
|
|
27523
|
+
|
|
27524
|
+
// src/commands/prs/status/fetchRepoPullRequests.ts
|
|
27475
27525
|
import { execSync as execSync51 } from "child_process";
|
|
27526
|
+
var FIELDS = "number,title,url,author,isDraft,createdAt,updatedAt,reviewDecision,latestReviews,statusCheckRollup,mergeable";
|
|
27527
|
+
function fetchRepoPullRequests(org, repo) {
|
|
27528
|
+
const output = execSync51(
|
|
27529
|
+
`gh pr list --state open --json ${FIELDS} --limit 100 -R ${org}/${repo}`,
|
|
27530
|
+
{ encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }
|
|
27531
|
+
);
|
|
27532
|
+
const parsed = JSON.parse(output);
|
|
27533
|
+
if (!Array.isArray(parsed)) {
|
|
27534
|
+
throw new Error("unexpected response from gh pr list");
|
|
27535
|
+
}
|
|
27536
|
+
return parsed;
|
|
27537
|
+
}
|
|
27538
|
+
|
|
27539
|
+
// src/commands/prs/status/parseRepoArgument.ts
|
|
27540
|
+
var REPO_ARGUMENT = /^([A-Za-z0-9._-]+)\/([A-Za-z0-9._-]+)$/;
|
|
27541
|
+
function parseRepoArgument(value) {
|
|
27542
|
+
const match = REPO_ARGUMENT.exec(value.trim());
|
|
27543
|
+
if (!match) return null;
|
|
27544
|
+
return { org: match[1], repo: match[2] };
|
|
27545
|
+
}
|
|
27546
|
+
|
|
27547
|
+
// src/commands/prs/status/describeAge.ts
|
|
27548
|
+
function describeAge(timestamp6, now = Date.now()) {
|
|
27549
|
+
const parsed = new Date(timestamp6).getTime();
|
|
27550
|
+
if (Number.isNaN(parsed)) return { hours: null, label: "unknown" };
|
|
27551
|
+
const hours = Math.max(0, Math.floor((now - parsed) / 36e5));
|
|
27552
|
+
if (hours < 1) return { hours, label: "<1h" };
|
|
27553
|
+
if (hours < 24) return { hours, label: `${hours}h` };
|
|
27554
|
+
return { hours, label: `${Math.floor(hours / 24)}d` };
|
|
27555
|
+
}
|
|
27556
|
+
|
|
27557
|
+
// src/commands/prs/status/summariseChecks.ts
|
|
27558
|
+
var PASSING_CONCLUSIONS = /* @__PURE__ */ new Set(["SUCCESS", "NEUTRAL", "SKIPPED"]);
|
|
27559
|
+
var PENDING_STATES = /* @__PURE__ */ new Set(["PENDING", "EXPECTED"]);
|
|
27560
|
+
function checkName(check2) {
|
|
27561
|
+
return check2.name?.trim() || check2.context?.trim() || "unnamed check";
|
|
27562
|
+
}
|
|
27563
|
+
function isPending(check2) {
|
|
27564
|
+
if (check2.status) return check2.status !== "COMPLETED";
|
|
27565
|
+
return PENDING_STATES.has(check2.state ?? "");
|
|
27566
|
+
}
|
|
27567
|
+
function isFailing(check2) {
|
|
27568
|
+
if (check2.status) return !PASSING_CONCLUSIONS.has(check2.conclusion ?? "");
|
|
27569
|
+
return !PASSING_CONCLUSIONS.has(check2.state ?? "");
|
|
27570
|
+
}
|
|
27571
|
+
function summariseChecks(rollup) {
|
|
27572
|
+
const summary = { failing: [], pending: [] };
|
|
27573
|
+
for (const check2 of rollup ?? []) {
|
|
27574
|
+
if (isPending(check2)) summary.pending.push(checkName(check2));
|
|
27575
|
+
else if (isFailing(check2)) summary.failing.push(checkName(check2));
|
|
27576
|
+
}
|
|
27577
|
+
return summary;
|
|
27578
|
+
}
|
|
27579
|
+
|
|
27580
|
+
// src/commands/prs/status/toPrStatus.ts
|
|
27581
|
+
function toReviews(pr) {
|
|
27582
|
+
return (pr.latestReviews ?? []).map((review2) => ({
|
|
27583
|
+
reviewer: review2.author?.login ?? "unknown",
|
|
27584
|
+
state: review2.state ?? "UNKNOWN"
|
|
27585
|
+
}));
|
|
27586
|
+
}
|
|
27587
|
+
function toPrStatus(pr, now = Date.now()) {
|
|
27588
|
+
const age = describeAge(pr.updatedAt, now);
|
|
27589
|
+
return {
|
|
27590
|
+
number: pr.number,
|
|
27591
|
+
title: pr.title,
|
|
27592
|
+
url: pr.url,
|
|
27593
|
+
author: pr.author?.login ?? "unknown",
|
|
27594
|
+
isBot: pr.author?.is_bot === true,
|
|
27595
|
+
isDraft: pr.isDraft === true,
|
|
27596
|
+
createdAt: pr.createdAt,
|
|
27597
|
+
updatedAt: pr.updatedAt,
|
|
27598
|
+
age: age.label,
|
|
27599
|
+
ageHours: age.hours,
|
|
27600
|
+
reviewDecision: pr.reviewDecision || null,
|
|
27601
|
+
reviews: toReviews(pr),
|
|
27602
|
+
checks: summariseChecks(pr.statusCheckRollup),
|
|
27603
|
+
mergeable: pr.mergeable || "UNKNOWN"
|
|
27604
|
+
};
|
|
27605
|
+
}
|
|
27606
|
+
|
|
27607
|
+
// src/commands/prs/status/buildPrsStatusReport.ts
|
|
27608
|
+
function buildPrsStatusReport(repoArguments, now = Date.now()) {
|
|
27609
|
+
const report2 = { repos: [], errors: [] };
|
|
27610
|
+
for (const argument of repoArguments) {
|
|
27611
|
+
const parsed = parseRepoArgument(argument);
|
|
27612
|
+
if (!parsed) {
|
|
27613
|
+
report2.errors.push({
|
|
27614
|
+
repo: argument,
|
|
27615
|
+
error: "not an owner/repo argument"
|
|
27616
|
+
});
|
|
27617
|
+
continue;
|
|
27618
|
+
}
|
|
27619
|
+
const repo = `${parsed.org}/${parsed.repo}`;
|
|
27620
|
+
try {
|
|
27621
|
+
const pullRequests = fetchRepoPullRequests(parsed.org, parsed.repo).map(
|
|
27622
|
+
(pr) => toPrStatus(pr, now)
|
|
27623
|
+
);
|
|
27624
|
+
report2.repos.push({ repo, pullRequests });
|
|
27625
|
+
} catch (error) {
|
|
27626
|
+
report2.errors.push({ repo, error: describeFetchError(error) });
|
|
27627
|
+
}
|
|
27628
|
+
}
|
|
27629
|
+
return report2;
|
|
27630
|
+
}
|
|
27631
|
+
|
|
27632
|
+
// src/commands/prs/status/printPrsStatus.ts
|
|
27633
|
+
import chalk187 from "chalk";
|
|
27634
|
+
|
|
27635
|
+
// src/commands/prs/status/printPrStatus.ts
|
|
27636
|
+
import chalk186 from "chalk";
|
|
27637
|
+
function flags(pr) {
|
|
27638
|
+
const markers = [];
|
|
27639
|
+
if (pr.isDraft) markers.push("draft");
|
|
27640
|
+
if (pr.isBot) markers.push("bot");
|
|
27641
|
+
return markers.length ? ` ${chalk186.yellow(`[${markers.join(", ")}]`)}` : "";
|
|
27642
|
+
}
|
|
27643
|
+
function updated(pr) {
|
|
27644
|
+
return pr.ageHours === null ? "updated unknown" : `updated ${pr.age} ago`;
|
|
27645
|
+
}
|
|
27646
|
+
function reviewLine(pr) {
|
|
27647
|
+
if (!pr.reviewDecision && pr.reviews.length === 0) return null;
|
|
27648
|
+
const reviewers = pr.reviews.map((review2) => `${review2.reviewer}: ${review2.state}`).join(", ");
|
|
27649
|
+
const decision = pr.reviewDecision ?? "no decision";
|
|
27650
|
+
return reviewers ? `review: ${decision} | ${reviewers}` : `review: ${decision}`;
|
|
27651
|
+
}
|
|
27652
|
+
function checkLines(pr) {
|
|
27653
|
+
const lines2 = [];
|
|
27654
|
+
if (pr.checks.failing.length) {
|
|
27655
|
+
lines2.push(chalk186.red(`failing: ${pr.checks.failing.join(", ")}`));
|
|
27656
|
+
}
|
|
27657
|
+
if (pr.checks.pending.length) {
|
|
27658
|
+
lines2.push(chalk186.yellow(`pending: ${pr.checks.pending.join(", ")}`));
|
|
27659
|
+
}
|
|
27660
|
+
return lines2;
|
|
27661
|
+
}
|
|
27662
|
+
function mergeableLine(pr) {
|
|
27663
|
+
if (pr.mergeable === "CONFLICTING") return chalk186.red("conflicting");
|
|
27664
|
+
if (pr.mergeable === "MERGEABLE") return null;
|
|
27665
|
+
return chalk186.dim(`mergeable: ${pr.mergeable.toLowerCase()}`);
|
|
27666
|
+
}
|
|
27667
|
+
function printPrStatus(pr) {
|
|
27668
|
+
console.log(
|
|
27669
|
+
` ${chalk186.cyan(`#${pr.number}`)} ${pr.title} ${chalk186.dim(`(${pr.author}, ${updated(pr)})`)}${flags(pr)}`
|
|
27670
|
+
);
|
|
27671
|
+
const details = [
|
|
27672
|
+
reviewLine(pr),
|
|
27673
|
+
...checkLines(pr),
|
|
27674
|
+
mergeableLine(pr),
|
|
27675
|
+
chalk186.dim(pr.url)
|
|
27676
|
+
];
|
|
27677
|
+
for (const detail of details) {
|
|
27678
|
+
if (detail) console.log(` ${detail}`);
|
|
27679
|
+
}
|
|
27680
|
+
}
|
|
27681
|
+
|
|
27682
|
+
// src/commands/prs/status/printPrsStatus.ts
|
|
27683
|
+
function printPrsStatus(report2) {
|
|
27684
|
+
for (const repo of report2.repos) {
|
|
27685
|
+
const count8 = repo.pullRequests.length;
|
|
27686
|
+
console.log(`${chalk187.bold(repo.repo)} ${chalk187.dim(`(${count8} open)`)}`);
|
|
27687
|
+
for (const pr of repo.pullRequests) {
|
|
27688
|
+
printPrStatus(pr);
|
|
27689
|
+
}
|
|
27690
|
+
if (count8 === 0) console.log(chalk187.dim(" no open pull requests"));
|
|
27691
|
+
console.log();
|
|
27692
|
+
}
|
|
27693
|
+
if (report2.errors.length === 0) return;
|
|
27694
|
+
console.log(chalk187.bold("Errors"));
|
|
27695
|
+
for (const failure of report2.errors) {
|
|
27696
|
+
console.log(` ${chalk187.red(failure.repo)}: ${failure.error}`);
|
|
27697
|
+
}
|
|
27698
|
+
console.log();
|
|
27699
|
+
}
|
|
27700
|
+
|
|
27701
|
+
// src/commands/prs/status/index.ts
|
|
27702
|
+
function prsStatus(repos2, options2) {
|
|
27703
|
+
const report2 = buildPrsStatusReport(repos2);
|
|
27704
|
+
if (options2.json) {
|
|
27705
|
+
console.log(JSON.stringify(report2, null, 2));
|
|
27706
|
+
return;
|
|
27707
|
+
}
|
|
27708
|
+
printPrsStatus(report2);
|
|
27709
|
+
}
|
|
27710
|
+
|
|
27711
|
+
// src/commands/prs/wontfix.ts
|
|
27712
|
+
import { execSync as execSync52 } from "child_process";
|
|
27476
27713
|
function validateReason(reason4) {
|
|
27477
27714
|
const lowerReason = reason4.toLowerCase();
|
|
27478
27715
|
if (lowerReason.includes("claude") || lowerReason.includes("opus")) {
|
|
@@ -27489,7 +27726,7 @@ function validateShaReferences(reason4) {
|
|
|
27489
27726
|
const invalidShas = [];
|
|
27490
27727
|
for (const sha of shas) {
|
|
27491
27728
|
try {
|
|
27492
|
-
|
|
27729
|
+
execSync52(`git cat-file -t ${sha}`, { stdio: "pipe" });
|
|
27493
27730
|
} catch {
|
|
27494
27731
|
invalidShas.push(sha);
|
|
27495
27732
|
}
|
|
@@ -27782,6 +28019,13 @@ function registerPrsRaise(prsCommand) {
|
|
|
27782
28019
|
configHelp(raiseCommand, prsRaiseConfigHelp);
|
|
27783
28020
|
}
|
|
27784
28021
|
|
|
28022
|
+
// src/commands/registerPrsStatus.ts
|
|
28023
|
+
function registerPrsStatus(prsCommand) {
|
|
28024
|
+
prsCommand.command("status <repo...>").description(
|
|
28025
|
+
"Report every open pull request in each owner/repo, grouped by repo"
|
|
28026
|
+
).option("--json", "Output as JSON").action(prsStatus);
|
|
28027
|
+
}
|
|
28028
|
+
|
|
27785
28029
|
// src/commands/readTime/readTime.ts
|
|
27786
28030
|
import { readFileSync as readFileSync46 } from "fs";
|
|
27787
28031
|
|
|
@@ -27871,7 +28115,7 @@ function resolveReadTimeTarget(target) {
|
|
|
27871
28115
|
}
|
|
27872
28116
|
|
|
27873
28117
|
// src/commands/prs/fetchPrBody.ts
|
|
27874
|
-
import { execSync as
|
|
28118
|
+
import { execSync as execSync53 } from "child_process";
|
|
27875
28119
|
function exitGhNotInstalled() {
|
|
27876
28120
|
console.error("Error: GitHub CLI (gh) is not installed.");
|
|
27877
28121
|
console.error("Install it from https://cli.github.com/");
|
|
@@ -27892,7 +28136,7 @@ function currentRepo() {
|
|
|
27892
28136
|
function fetchPrBody(number, repo) {
|
|
27893
28137
|
const { org, repo: name } = repo ?? currentRepo();
|
|
27894
28138
|
try {
|
|
27895
|
-
const raw =
|
|
28139
|
+
const raw = execSync53(`gh pr view ${number} --json body -R ${org}/${name}`, {
|
|
27896
28140
|
encoding: "utf8",
|
|
27897
28141
|
stdio: ["pipe", "pipe", "pipe"]
|
|
27898
28142
|
});
|
|
@@ -27971,6 +28215,7 @@ function registerReadTime(parent) {
|
|
|
27971
28215
|
function registerPrs(program2) {
|
|
27972
28216
|
const prsCommand = program2.command("prs").description("Pull request utilities").option("--open", "List only open pull requests").option("--closed", "List only closed pull requests").action(prs);
|
|
27973
28217
|
registerPrsRaise(prsCommand);
|
|
28218
|
+
registerPrsStatus(prsCommand);
|
|
27974
28219
|
registerPrsEdit(prsCommand);
|
|
27975
28220
|
registerPrsComments(prsCommand);
|
|
27976
28221
|
registerReadTime(prsCommand);
|
|
@@ -27978,10 +28223,10 @@ function registerPrs(program2) {
|
|
|
27978
28223
|
}
|
|
27979
28224
|
|
|
27980
28225
|
// src/commands/ravendb/ravendbAuth.ts
|
|
27981
|
-
import
|
|
28226
|
+
import chalk193 from "chalk";
|
|
27982
28227
|
|
|
27983
28228
|
// src/shared/createConnectionAuth.ts
|
|
27984
|
-
import
|
|
28229
|
+
import chalk188 from "chalk";
|
|
27985
28230
|
function listConnections(connections, format2) {
|
|
27986
28231
|
if (connections.length === 0) {
|
|
27987
28232
|
console.log("No connections configured.");
|
|
@@ -27994,7 +28239,7 @@ function listConnections(connections, format2) {
|
|
|
27994
28239
|
function removeConnection(connections, name, save) {
|
|
27995
28240
|
const filtered = connections.filter((c) => c.name !== name);
|
|
27996
28241
|
if (filtered.length === connections.length) {
|
|
27997
|
-
console.error(
|
|
28242
|
+
console.error(chalk188.red(`Connection "${name}" not found.`));
|
|
27998
28243
|
process.exit(1);
|
|
27999
28244
|
}
|
|
28000
28245
|
save(filtered);
|
|
@@ -28040,17 +28285,17 @@ function saveConnections(connections) {
|
|
|
28040
28285
|
}
|
|
28041
28286
|
|
|
28042
28287
|
// src/commands/ravendb/promptConnection.ts
|
|
28043
|
-
import
|
|
28288
|
+
import chalk191 from "chalk";
|
|
28044
28289
|
|
|
28045
28290
|
// src/commands/ravendb/selectOpSecret.ts
|
|
28046
|
-
import
|
|
28291
|
+
import chalk190 from "chalk";
|
|
28047
28292
|
import Enquirer2 from "enquirer";
|
|
28048
28293
|
|
|
28049
28294
|
// src/commands/ravendb/searchItems.ts
|
|
28050
|
-
import { execSync as
|
|
28051
|
-
import
|
|
28295
|
+
import { execSync as execSync54 } from "child_process";
|
|
28296
|
+
import chalk189 from "chalk";
|
|
28052
28297
|
function opExec(args) {
|
|
28053
|
-
return
|
|
28298
|
+
return execSync54(`op ${args}`, {
|
|
28054
28299
|
encoding: "utf8",
|
|
28055
28300
|
stdio: ["pipe", "pipe", "pipe"]
|
|
28056
28301
|
}).trim();
|
|
@@ -28061,7 +28306,7 @@ function searchItems(search2) {
|
|
|
28061
28306
|
items2 = JSON.parse(opExec("item list --format=json"));
|
|
28062
28307
|
} catch {
|
|
28063
28308
|
console.error(
|
|
28064
|
-
|
|
28309
|
+
chalk189.red(
|
|
28065
28310
|
"Failed to search 1Password. Ensure the CLI is installed and you are signed in."
|
|
28066
28311
|
)
|
|
28067
28312
|
);
|
|
@@ -28075,7 +28320,7 @@ function getItemFields(itemId2) {
|
|
|
28075
28320
|
const item = JSON.parse(opExec(`item get "${itemId2}" --format=json`));
|
|
28076
28321
|
return item.fields.filter((f) => f.reference && f.label);
|
|
28077
28322
|
} catch {
|
|
28078
|
-
console.error(
|
|
28323
|
+
console.error(chalk189.red("Failed to get item details from 1Password."));
|
|
28079
28324
|
process.exit(1);
|
|
28080
28325
|
}
|
|
28081
28326
|
}
|
|
@@ -28094,7 +28339,7 @@ async function selectOpSecret(searchTerm) {
|
|
|
28094
28339
|
}).run();
|
|
28095
28340
|
const items2 = searchItems(search2);
|
|
28096
28341
|
if (items2.length === 0) {
|
|
28097
|
-
console.error(
|
|
28342
|
+
console.error(chalk190.red(`No items found matching "${search2}".`));
|
|
28098
28343
|
process.exit(1);
|
|
28099
28344
|
}
|
|
28100
28345
|
const itemId2 = await selectOne(
|
|
@@ -28103,7 +28348,7 @@ async function selectOpSecret(searchTerm) {
|
|
|
28103
28348
|
);
|
|
28104
28349
|
const fields = getItemFields(itemId2);
|
|
28105
28350
|
if (fields.length === 0) {
|
|
28106
|
-
console.error(
|
|
28351
|
+
console.error(chalk190.red("No fields with references found on this item."));
|
|
28107
28352
|
process.exit(1);
|
|
28108
28353
|
}
|
|
28109
28354
|
const ref = await selectOne(
|
|
@@ -28117,7 +28362,7 @@ async function selectOpSecret(searchTerm) {
|
|
|
28117
28362
|
async function promptConnection(existingNames) {
|
|
28118
28363
|
const name = await promptInput("name", "Connection name:");
|
|
28119
28364
|
if (existingNames.includes(name)) {
|
|
28120
|
-
console.error(
|
|
28365
|
+
console.error(chalk191.red(`Connection "${name}" already exists.`));
|
|
28121
28366
|
process.exit(1);
|
|
28122
28367
|
}
|
|
28123
28368
|
const url = await promptInput(
|
|
@@ -28126,22 +28371,22 @@ async function promptConnection(existingNames) {
|
|
|
28126
28371
|
);
|
|
28127
28372
|
const database = await promptInput("database", "Database name:");
|
|
28128
28373
|
if (!name || !url || !database) {
|
|
28129
|
-
console.error(
|
|
28374
|
+
console.error(chalk191.red("All fields are required."));
|
|
28130
28375
|
process.exit(1);
|
|
28131
28376
|
}
|
|
28132
28377
|
const apiKeyRef = await selectOpSecret();
|
|
28133
|
-
console.log(
|
|
28378
|
+
console.log(chalk191.dim(`Using: ${apiKeyRef}`));
|
|
28134
28379
|
return { name, url, database, apiKeyRef };
|
|
28135
28380
|
}
|
|
28136
28381
|
|
|
28137
28382
|
// src/commands/ravendb/ravendbSetConnection.ts
|
|
28138
|
-
import
|
|
28383
|
+
import chalk192 from "chalk";
|
|
28139
28384
|
function ravendbSetConnection(name) {
|
|
28140
28385
|
const raw = loadGlobalConfigRaw();
|
|
28141
28386
|
const ravendb = raw.ravendb ?? {};
|
|
28142
28387
|
const connections = ravendb.connections ?? [];
|
|
28143
28388
|
if (!connections.some((c) => c.name === name)) {
|
|
28144
|
-
console.error(
|
|
28389
|
+
console.error(chalk192.red(`Connection "${name}" not found.`));
|
|
28145
28390
|
console.error(
|
|
28146
28391
|
`Available: ${connections.map((c) => c.name).join(", ") || "(none)"}`
|
|
28147
28392
|
);
|
|
@@ -28157,16 +28402,16 @@ function ravendbSetConnection(name) {
|
|
|
28157
28402
|
var ravendbAuth = createConnectionAuth({
|
|
28158
28403
|
load: loadConnections,
|
|
28159
28404
|
save: saveConnections,
|
|
28160
|
-
format: (c) => `${
|
|
28405
|
+
format: (c) => `${chalk193.bold(c.name)} ${c.url} db=${c.database} key=${c.apiKeyRef}`,
|
|
28161
28406
|
promptNew: promptConnection,
|
|
28162
28407
|
onFirst: (c) => ravendbSetConnection(c.name)
|
|
28163
28408
|
});
|
|
28164
28409
|
|
|
28165
28410
|
// src/commands/ravendb/ravendbCollections.ts
|
|
28166
|
-
import
|
|
28411
|
+
import chalk197 from "chalk";
|
|
28167
28412
|
|
|
28168
28413
|
// src/commands/ravendb/ravenFetch.ts
|
|
28169
|
-
import
|
|
28414
|
+
import chalk195 from "chalk";
|
|
28170
28415
|
|
|
28171
28416
|
// src/commands/ravendb/getAccessToken.ts
|
|
28172
28417
|
var OAUTH_URL = "https://amazon-useast-1-oauth.ravenhq.com/ApiKeys/OAuth/AccessToken";
|
|
@@ -28202,21 +28447,21 @@ ${errorText}`
|
|
|
28202
28447
|
}
|
|
28203
28448
|
|
|
28204
28449
|
// src/commands/ravendb/resolveOpSecret.ts
|
|
28205
|
-
import { execSync as
|
|
28206
|
-
import
|
|
28450
|
+
import { execSync as execSync55 } from "child_process";
|
|
28451
|
+
import chalk194 from "chalk";
|
|
28207
28452
|
function resolveOpSecret(reference) {
|
|
28208
28453
|
if (!reference.startsWith("op://")) {
|
|
28209
|
-
console.error(
|
|
28454
|
+
console.error(chalk194.red(`Invalid secret reference: must start with op://`));
|
|
28210
28455
|
process.exit(1);
|
|
28211
28456
|
}
|
|
28212
28457
|
try {
|
|
28213
|
-
return
|
|
28458
|
+
return execSync55(`op read "${reference}"`, {
|
|
28214
28459
|
encoding: "utf8",
|
|
28215
28460
|
stdio: ["pipe", "pipe", "pipe"]
|
|
28216
28461
|
}).trim();
|
|
28217
28462
|
} catch {
|
|
28218
28463
|
console.error(
|
|
28219
|
-
|
|
28464
|
+
chalk194.red(
|
|
28220
28465
|
"Failed to resolve secret reference. Ensure 1Password CLI is installed and you are signed in."
|
|
28221
28466
|
)
|
|
28222
28467
|
);
|
|
@@ -28243,7 +28488,7 @@ async function ravenFetch(connection, path91) {
|
|
|
28243
28488
|
if (!response.ok) {
|
|
28244
28489
|
const body = await response.text();
|
|
28245
28490
|
console.error(
|
|
28246
|
-
|
|
28491
|
+
chalk195.red(`RavenDB error: ${response.status} ${response.statusText}`)
|
|
28247
28492
|
);
|
|
28248
28493
|
console.error(body.substring(0, 500));
|
|
28249
28494
|
process.exit(1);
|
|
@@ -28252,7 +28497,7 @@ async function ravenFetch(connection, path91) {
|
|
|
28252
28497
|
}
|
|
28253
28498
|
|
|
28254
28499
|
// src/commands/ravendb/resolveConnection.ts
|
|
28255
|
-
import
|
|
28500
|
+
import chalk196 from "chalk";
|
|
28256
28501
|
function loadRavendb() {
|
|
28257
28502
|
const raw = loadGlobalConfigRaw();
|
|
28258
28503
|
const ravendb = raw.ravendb;
|
|
@@ -28266,7 +28511,7 @@ function resolveConnection(name) {
|
|
|
28266
28511
|
const connectionName = name ?? defaultConnection;
|
|
28267
28512
|
if (!connectionName) {
|
|
28268
28513
|
console.error(
|
|
28269
|
-
|
|
28514
|
+
chalk196.red(
|
|
28270
28515
|
"No connection specified and no default set. Use assist ravendb set-connection <name> or pass a connection name."
|
|
28271
28516
|
)
|
|
28272
28517
|
);
|
|
@@ -28274,7 +28519,7 @@ function resolveConnection(name) {
|
|
|
28274
28519
|
}
|
|
28275
28520
|
const connection = connections.find((c) => c.name === connectionName);
|
|
28276
28521
|
if (!connection) {
|
|
28277
|
-
console.error(
|
|
28522
|
+
console.error(chalk196.red(`Connection "${connectionName}" not found.`));
|
|
28278
28523
|
console.error(
|
|
28279
28524
|
`Available: ${connections.map((c) => c.name).join(", ") || "(none)"}`
|
|
28280
28525
|
);
|
|
@@ -28305,15 +28550,15 @@ async function ravendbCollections(connectionName) {
|
|
|
28305
28550
|
return;
|
|
28306
28551
|
}
|
|
28307
28552
|
for (const c of collections) {
|
|
28308
|
-
console.log(`${
|
|
28553
|
+
console.log(`${chalk197.bold(c.Name)} ${c.CountOfDocuments} docs`);
|
|
28309
28554
|
}
|
|
28310
28555
|
}
|
|
28311
28556
|
|
|
28312
28557
|
// src/commands/ravendb/ravendbQuery.ts
|
|
28313
|
-
import
|
|
28558
|
+
import chalk199 from "chalk";
|
|
28314
28559
|
|
|
28315
28560
|
// src/commands/ravendb/fetchAllPages.ts
|
|
28316
|
-
import
|
|
28561
|
+
import chalk198 from "chalk";
|
|
28317
28562
|
|
|
28318
28563
|
// src/commands/ravendb/buildQueryPath.ts
|
|
28319
28564
|
function buildQueryPath(opts) {
|
|
@@ -28351,7 +28596,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
28351
28596
|
allResults.push(...results);
|
|
28352
28597
|
start3 += results.length;
|
|
28353
28598
|
process.stderr.write(
|
|
28354
|
-
`\r${
|
|
28599
|
+
`\r${chalk198.dim(`Fetched ${allResults.length}/${totalResults}`)}`
|
|
28355
28600
|
);
|
|
28356
28601
|
if (start3 >= totalResults) break;
|
|
28357
28602
|
if (opts.limit !== void 0 && allResults.length >= opts.limit) break;
|
|
@@ -28366,7 +28611,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
28366
28611
|
async function ravendbQuery(connectionName, collection, options2) {
|
|
28367
28612
|
const resolved = resolveArgs(connectionName, collection);
|
|
28368
28613
|
if (!resolved.collection && !options2.query) {
|
|
28369
|
-
console.error(
|
|
28614
|
+
console.error(chalk199.red("Provide a collection name or --query filter."));
|
|
28370
28615
|
process.exit(1);
|
|
28371
28616
|
}
|
|
28372
28617
|
const { collection: col } = resolved;
|
|
@@ -28405,7 +28650,7 @@ import { spawn as spawn6 } from "child_process";
|
|
|
28405
28650
|
import * as path46 from "path";
|
|
28406
28651
|
|
|
28407
28652
|
// src/commands/refactor/logViolations.ts
|
|
28408
|
-
import
|
|
28653
|
+
import chalk200 from "chalk";
|
|
28409
28654
|
var DEFAULT_MAX_LINES2 = 100;
|
|
28410
28655
|
function logViolations(violations, maxLines = DEFAULT_MAX_LINES2) {
|
|
28411
28656
|
if (violations.length === 0) {
|
|
@@ -28414,43 +28659,43 @@ function logViolations(violations, maxLines = DEFAULT_MAX_LINES2) {
|
|
|
28414
28659
|
}
|
|
28415
28660
|
return;
|
|
28416
28661
|
}
|
|
28417
|
-
console.error(
|
|
28662
|
+
console.error(chalk200.red(`
|
|
28418
28663
|
Refactor check failed:
|
|
28419
28664
|
`));
|
|
28420
|
-
console.error(
|
|
28665
|
+
console.error(chalk200.red(` The following files exceed ${maxLines} lines:
|
|
28421
28666
|
`));
|
|
28422
28667
|
for (const violation of violations) {
|
|
28423
|
-
console.error(
|
|
28668
|
+
console.error(chalk200.red(` ${violation.file} (${violation.lines} lines)`));
|
|
28424
28669
|
}
|
|
28425
28670
|
console.error(
|
|
28426
|
-
|
|
28671
|
+
chalk200.yellow(
|
|
28427
28672
|
`
|
|
28428
28673
|
Each file needs to be sensibly refactored, or if there is no sensible
|
|
28429
28674
|
way to refactor it, ignore it with:
|
|
28430
28675
|
`
|
|
28431
28676
|
)
|
|
28432
28677
|
);
|
|
28433
|
-
console.error(
|
|
28678
|
+
console.error(chalk200.gray(` assist refactor ignore <file>
|
|
28434
28679
|
`));
|
|
28435
28680
|
if (process.env.CLAUDECODE) {
|
|
28436
|
-
console.error(
|
|
28681
|
+
console.error(chalk200.cyan(`
|
|
28437
28682
|
## Extracting Code to New Files
|
|
28438
28683
|
`));
|
|
28439
28684
|
console.error(
|
|
28440
|
-
|
|
28685
|
+
chalk200.cyan(
|
|
28441
28686
|
` When extracting logic from one file to another, consider where the extracted code belongs:
|
|
28442
28687
|
`
|
|
28443
28688
|
)
|
|
28444
28689
|
);
|
|
28445
28690
|
console.error(
|
|
28446
|
-
|
|
28691
|
+
chalk200.cyan(
|
|
28447
28692
|
` 1. Keep related logic together: If the extracted code is tightly coupled to the
|
|
28448
28693
|
original file's domain, create a new folder containing both the original and extracted files.
|
|
28449
28694
|
`
|
|
28450
28695
|
)
|
|
28451
28696
|
);
|
|
28452
28697
|
console.error(
|
|
28453
|
-
|
|
28698
|
+
chalk200.cyan(
|
|
28454
28699
|
` 2. Share common utilities: If the extracted code can be reused across multiple
|
|
28455
28700
|
domains, move it to a common/shared folder.
|
|
28456
28701
|
`
|
|
@@ -28460,7 +28705,7 @@ Refactor check failed:
|
|
|
28460
28705
|
}
|
|
28461
28706
|
|
|
28462
28707
|
// src/commands/refactor/check/getViolations/index.ts
|
|
28463
|
-
import { execSync as
|
|
28708
|
+
import { execSync as execSync56 } from "child_process";
|
|
28464
28709
|
import fs31 from "fs";
|
|
28465
28710
|
import { minimatch as minimatch6 } from "minimatch";
|
|
28466
28711
|
|
|
@@ -28510,7 +28755,7 @@ function getGitFiles(options2) {
|
|
|
28510
28755
|
}
|
|
28511
28756
|
const files = /* @__PURE__ */ new Set();
|
|
28512
28757
|
if (options2.staged || options2.modified) {
|
|
28513
|
-
const staged =
|
|
28758
|
+
const staged = execSync56("git diff --cached --name-only", {
|
|
28514
28759
|
encoding: "utf8"
|
|
28515
28760
|
});
|
|
28516
28761
|
for (const file of staged.trim().split("\n").filter(Boolean)) {
|
|
@@ -28518,7 +28763,7 @@ function getGitFiles(options2) {
|
|
|
28518
28763
|
}
|
|
28519
28764
|
}
|
|
28520
28765
|
if (options2.unstaged || options2.modified) {
|
|
28521
|
-
const unstaged =
|
|
28766
|
+
const unstaged = execSync56("git diff --name-only", { encoding: "utf8" });
|
|
28522
28767
|
for (const file of unstaged.trim().split("\n").filter(Boolean)) {
|
|
28523
28768
|
files.add(file);
|
|
28524
28769
|
}
|
|
@@ -28606,7 +28851,7 @@ async function check(pattern2, options2) {
|
|
|
28606
28851
|
|
|
28607
28852
|
// src/commands/refactor/extract/index.ts
|
|
28608
28853
|
import path54 from "path";
|
|
28609
|
-
import
|
|
28854
|
+
import chalk203 from "chalk";
|
|
28610
28855
|
|
|
28611
28856
|
// src/commands/refactor/extract/applyExtraction.ts
|
|
28612
28857
|
import { SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
@@ -29205,23 +29450,23 @@ function buildPlan2(functionName, sourceFile, sourcePath, destPath, project) {
|
|
|
29205
29450
|
|
|
29206
29451
|
// src/commands/refactor/extract/displayPlan.ts
|
|
29207
29452
|
import path50 from "path";
|
|
29208
|
-
import
|
|
29453
|
+
import chalk201 from "chalk";
|
|
29209
29454
|
function section(title) {
|
|
29210
29455
|
return `
|
|
29211
|
-
${
|
|
29456
|
+
${chalk201.cyan(title)}`;
|
|
29212
29457
|
}
|
|
29213
29458
|
function displayImporters(plan2, cwd) {
|
|
29214
29459
|
if (plan2.importersToUpdate.length === 0) return;
|
|
29215
29460
|
console.log(section("Update importers:"));
|
|
29216
29461
|
for (const imp of plan2.importersToUpdate) {
|
|
29217
29462
|
const rel = path50.relative(cwd, imp.file.getFilePath());
|
|
29218
|
-
console.log(` ${
|
|
29463
|
+
console.log(` ${chalk201.dim(rel)}: \u2192 import from "${imp.relPath}"`);
|
|
29219
29464
|
}
|
|
29220
29465
|
}
|
|
29221
29466
|
function displayPlan(functionName, relDest, plan2, cwd) {
|
|
29222
|
-
console.log(
|
|
29467
|
+
console.log(chalk201.bold(`Extract: ${functionName} \u2192 ${relDest}
|
|
29223
29468
|
`));
|
|
29224
|
-
console.log(` ${
|
|
29469
|
+
console.log(` ${chalk201.cyan("Functions to move:")}`);
|
|
29225
29470
|
for (const name of plan2.extractedNames) {
|
|
29226
29471
|
console.log(` ${name}`);
|
|
29227
29472
|
}
|
|
@@ -29255,7 +29500,7 @@ function displayPlan(functionName, relDest, plan2, cwd) {
|
|
|
29255
29500
|
|
|
29256
29501
|
// src/commands/refactor/extract/loadProjectFile.ts
|
|
29257
29502
|
import path53 from "path";
|
|
29258
|
-
import
|
|
29503
|
+
import chalk202 from "chalk";
|
|
29259
29504
|
import { Project as Project4 } from "ts-morph";
|
|
29260
29505
|
|
|
29261
29506
|
// src/commands/refactor/extract/findTsConfig.ts
|
|
@@ -29347,7 +29592,7 @@ function loadProjectFile(file) {
|
|
|
29347
29592
|
});
|
|
29348
29593
|
const sourceFile = project.getSourceFile(sourcePath);
|
|
29349
29594
|
if (!sourceFile) {
|
|
29350
|
-
console.log(
|
|
29595
|
+
console.log(chalk202.red(`File not found in project: ${file}`));
|
|
29351
29596
|
process.exit(1);
|
|
29352
29597
|
}
|
|
29353
29598
|
return { project, sourceFile };
|
|
@@ -29370,19 +29615,19 @@ async function extract(file, functionName, destination, options2 = {}) {
|
|
|
29370
29615
|
displayPlan(functionName, relDest, plan2, cwd);
|
|
29371
29616
|
if (options2.apply) {
|
|
29372
29617
|
await applyExtraction(functionName, sourceFile, destPath, plan2, project);
|
|
29373
|
-
console.log(
|
|
29618
|
+
console.log(chalk203.green("\nExtraction complete"));
|
|
29374
29619
|
} else {
|
|
29375
|
-
console.log(
|
|
29620
|
+
console.log(chalk203.dim("\nDry run. Use --apply to execute."));
|
|
29376
29621
|
}
|
|
29377
29622
|
}
|
|
29378
29623
|
|
|
29379
29624
|
// src/commands/refactor/ignore.ts
|
|
29380
29625
|
import fs34 from "fs";
|
|
29381
|
-
import
|
|
29626
|
+
import chalk204 from "chalk";
|
|
29382
29627
|
var REFACTOR_YML_PATH2 = "refactor.yml";
|
|
29383
29628
|
function ignore2(file) {
|
|
29384
29629
|
if (!fs34.existsSync(file)) {
|
|
29385
|
-
console.error(
|
|
29630
|
+
console.error(chalk204.red(`Error: File does not exist: ${file}`));
|
|
29386
29631
|
process.exit(1);
|
|
29387
29632
|
}
|
|
29388
29633
|
const content = fs34.readFileSync(file, "utf8");
|
|
@@ -29398,7 +29643,7 @@ function ignore2(file) {
|
|
|
29398
29643
|
fs34.writeFileSync(REFACTOR_YML_PATH2, entry);
|
|
29399
29644
|
}
|
|
29400
29645
|
console.log(
|
|
29401
|
-
|
|
29646
|
+
chalk204.green(
|
|
29402
29647
|
`Added ${file} to refactor ignore list (max ${maxLines} lines)`
|
|
29403
29648
|
)
|
|
29404
29649
|
);
|
|
@@ -29407,12 +29652,12 @@ function ignore2(file) {
|
|
|
29407
29652
|
// src/commands/refactor/rename/index.ts
|
|
29408
29653
|
import fs37 from "fs";
|
|
29409
29654
|
import path59 from "path";
|
|
29410
|
-
import
|
|
29655
|
+
import chalk207 from "chalk";
|
|
29411
29656
|
|
|
29412
29657
|
// src/commands/refactor/rename/applyRename.ts
|
|
29413
29658
|
import fs36 from "fs";
|
|
29414
29659
|
import path56 from "path";
|
|
29415
|
-
import
|
|
29660
|
+
import chalk205 from "chalk";
|
|
29416
29661
|
|
|
29417
29662
|
// src/commands/refactor/restructure/computeRewrites/index.ts
|
|
29418
29663
|
import path55 from "path";
|
|
@@ -29517,13 +29762,13 @@ function applyRename(rewrites, sourcePath, destPath, cwd) {
|
|
|
29517
29762
|
const updatedContents = applyRewrites(rewrites);
|
|
29518
29763
|
for (const [file, content] of updatedContents) {
|
|
29519
29764
|
fs36.writeFileSync(file, content, "utf8");
|
|
29520
|
-
console.log(
|
|
29765
|
+
console.log(chalk205.cyan(` Updated imports in ${path56.relative(cwd, file)}`));
|
|
29521
29766
|
}
|
|
29522
29767
|
const destDir = path56.dirname(destPath);
|
|
29523
29768
|
if (!fs36.existsSync(destDir)) fs36.mkdirSync(destDir, { recursive: true });
|
|
29524
29769
|
fs36.renameSync(sourcePath, destPath);
|
|
29525
29770
|
console.log(
|
|
29526
|
-
|
|
29771
|
+
chalk205.white(
|
|
29527
29772
|
` Moved ${path56.relative(cwd, sourcePath)} \u2192 ${path56.relative(cwd, destPath)}`
|
|
29528
29773
|
)
|
|
29529
29774
|
);
|
|
@@ -29610,16 +29855,16 @@ function computeRenameRewrites(sourcePath, destPath) {
|
|
|
29610
29855
|
|
|
29611
29856
|
// src/commands/refactor/rename/printRenamePreview.ts
|
|
29612
29857
|
import path58 from "path";
|
|
29613
|
-
import
|
|
29858
|
+
import chalk206 from "chalk";
|
|
29614
29859
|
function printRenamePreview(rewrites, cwd) {
|
|
29615
29860
|
for (const rewrite of rewrites) {
|
|
29616
29861
|
console.log(
|
|
29617
|
-
|
|
29862
|
+
chalk206.dim(
|
|
29618
29863
|
` ${path58.relative(cwd, rewrite.file)}: ${rewrite.oldSpecifier} \u2192 ${rewrite.newSpecifier}`
|
|
29619
29864
|
)
|
|
29620
29865
|
);
|
|
29621
29866
|
}
|
|
29622
|
-
console.log(
|
|
29867
|
+
console.log(chalk206.dim("Dry run. Use --apply to execute."));
|
|
29623
29868
|
}
|
|
29624
29869
|
|
|
29625
29870
|
// src/commands/refactor/rename/index.ts
|
|
@@ -29630,20 +29875,20 @@ async function rename(source, destination, options2 = {}) {
|
|
|
29630
29875
|
const relSource = path59.relative(cwd, sourcePath);
|
|
29631
29876
|
const relDest = path59.relative(cwd, destPath);
|
|
29632
29877
|
if (!fs37.existsSync(sourcePath)) {
|
|
29633
|
-
console.log(
|
|
29878
|
+
console.log(chalk207.red(`File not found: ${source}`));
|
|
29634
29879
|
process.exit(1);
|
|
29635
29880
|
}
|
|
29636
29881
|
if (destPath !== sourcePath && fs37.existsSync(destPath)) {
|
|
29637
|
-
console.log(
|
|
29882
|
+
console.log(chalk207.red(`Destination already exists: ${destination}`));
|
|
29638
29883
|
process.exit(1);
|
|
29639
29884
|
}
|
|
29640
|
-
console.log(
|
|
29641
|
-
console.log(
|
|
29642
|
-
console.log(
|
|
29885
|
+
console.log(chalk207.bold(`Rename: ${relSource} \u2192 ${relDest}`));
|
|
29886
|
+
console.log(chalk207.dim("Loading project..."));
|
|
29887
|
+
console.log(chalk207.dim("Scanning imports across the project..."));
|
|
29643
29888
|
const rewrites = computeRenameRewrites(sourcePath, destPath);
|
|
29644
29889
|
const affectedFiles = new Set(rewrites.map((r) => r.file)).size;
|
|
29645
29890
|
console.log(
|
|
29646
|
-
|
|
29891
|
+
chalk207.dim(
|
|
29647
29892
|
`${rewrites.length} import path(s) to update across ${affectedFiles} file(s)`
|
|
29648
29893
|
)
|
|
29649
29894
|
);
|
|
@@ -29652,11 +29897,11 @@ async function rename(source, destination, options2 = {}) {
|
|
|
29652
29897
|
return;
|
|
29653
29898
|
}
|
|
29654
29899
|
applyRename(rewrites, sourcePath, destPath, cwd);
|
|
29655
|
-
console.log(
|
|
29900
|
+
console.log(chalk207.green("Done"));
|
|
29656
29901
|
}
|
|
29657
29902
|
|
|
29658
29903
|
// src/commands/refactor/renameSymbol/index.ts
|
|
29659
|
-
import
|
|
29904
|
+
import chalk208 from "chalk";
|
|
29660
29905
|
|
|
29661
29906
|
// src/commands/refactor/renameSymbol/findSymbol.ts
|
|
29662
29907
|
import { SyntaxKind as SyntaxKind15 } from "ts-morph";
|
|
@@ -29702,33 +29947,33 @@ async function renameSymbol(file, oldName, newName, options2 = {}) {
|
|
|
29702
29947
|
const { project, sourceFile } = loadProjectFile(file);
|
|
29703
29948
|
const symbol = findSymbol(sourceFile, oldName);
|
|
29704
29949
|
if (!symbol) {
|
|
29705
|
-
console.log(
|
|
29950
|
+
console.log(chalk208.red(`Symbol "${oldName}" not found in ${file}`));
|
|
29706
29951
|
process.exit(1);
|
|
29707
29952
|
}
|
|
29708
29953
|
const grouped = groupReferences(symbol, cwd);
|
|
29709
29954
|
const totalRefs = [...grouped.values()].reduce((s, l) => s + l.length, 0);
|
|
29710
29955
|
console.log(
|
|
29711
|
-
|
|
29956
|
+
chalk208.bold(`Rename: ${oldName} \u2192 ${newName} (${totalRefs} references)
|
|
29712
29957
|
`)
|
|
29713
29958
|
);
|
|
29714
29959
|
for (const [refFile, lines2] of grouped) {
|
|
29715
29960
|
console.log(
|
|
29716
|
-
` ${
|
|
29961
|
+
` ${chalk208.dim(refFile)}: lines ${chalk208.cyan(lines2.join(", "))}`
|
|
29717
29962
|
);
|
|
29718
29963
|
}
|
|
29719
29964
|
if (options2.apply) {
|
|
29720
29965
|
symbol.rename(newName);
|
|
29721
29966
|
await project.save();
|
|
29722
|
-
console.log(
|
|
29967
|
+
console.log(chalk208.green(`
|
|
29723
29968
|
Renamed ${oldName} \u2192 ${newName}`));
|
|
29724
29969
|
} else {
|
|
29725
|
-
console.log(
|
|
29970
|
+
console.log(chalk208.dim("\nDry run. Use --apply to execute."));
|
|
29726
29971
|
}
|
|
29727
29972
|
}
|
|
29728
29973
|
|
|
29729
29974
|
// src/commands/refactor/restructure/index.ts
|
|
29730
29975
|
import path67 from "path";
|
|
29731
|
-
import
|
|
29976
|
+
import chalk211 from "chalk";
|
|
29732
29977
|
|
|
29733
29978
|
// src/commands/refactor/restructure/clusterDirectories.ts
|
|
29734
29979
|
import path61 from "path";
|
|
@@ -29807,50 +30052,50 @@ function clusterFiles(graph) {
|
|
|
29807
30052
|
|
|
29808
30053
|
// src/commands/refactor/restructure/displayPlan.ts
|
|
29809
30054
|
import path63 from "path";
|
|
29810
|
-
import
|
|
30055
|
+
import chalk209 from "chalk";
|
|
29811
30056
|
function relPath(filePath) {
|
|
29812
30057
|
return path63.relative(process.cwd(), filePath);
|
|
29813
30058
|
}
|
|
29814
30059
|
function displayMoves(plan2) {
|
|
29815
30060
|
if (plan2.moves.length === 0) return;
|
|
29816
|
-
console.log(
|
|
30061
|
+
console.log(chalk209.bold("\nFile moves:"));
|
|
29817
30062
|
for (const move2 of plan2.moves) {
|
|
29818
30063
|
console.log(
|
|
29819
|
-
` ${
|
|
30064
|
+
` ${chalk209.red(relPath(move2.from))} \u2192 ${chalk209.green(relPath(move2.to))}`
|
|
29820
30065
|
);
|
|
29821
|
-
console.log(
|
|
30066
|
+
console.log(chalk209.dim(` ${move2.reason}`));
|
|
29822
30067
|
}
|
|
29823
30068
|
}
|
|
29824
30069
|
function displayRewrites(rewrites) {
|
|
29825
30070
|
if (rewrites.length === 0) return;
|
|
29826
30071
|
const affectedFiles = new Set(rewrites.map((r) => r.file));
|
|
29827
|
-
console.log(
|
|
30072
|
+
console.log(chalk209.bold(`
|
|
29828
30073
|
Import rewrites (${affectedFiles.size} files):`));
|
|
29829
30074
|
for (const file of affectedFiles) {
|
|
29830
|
-
console.log(` ${
|
|
30075
|
+
console.log(` ${chalk209.cyan(relPath(file))}:`);
|
|
29831
30076
|
for (const { oldSpecifier, newSpecifier } of rewrites.filter(
|
|
29832
30077
|
(r) => r.file === file
|
|
29833
30078
|
)) {
|
|
29834
30079
|
console.log(
|
|
29835
|
-
` ${
|
|
30080
|
+
` ${chalk209.red(`"${oldSpecifier}"`)} \u2192 ${chalk209.green(`"${newSpecifier}"`)}`
|
|
29836
30081
|
);
|
|
29837
30082
|
}
|
|
29838
30083
|
}
|
|
29839
30084
|
}
|
|
29840
30085
|
function displayPlan2(plan2) {
|
|
29841
30086
|
if (plan2.warnings.length > 0) {
|
|
29842
|
-
console.log(
|
|
29843
|
-
for (const w of plan2.warnings) console.log(
|
|
30087
|
+
console.log(chalk209.yellow("\nWarnings:"));
|
|
30088
|
+
for (const w of plan2.warnings) console.log(chalk209.yellow(` ${w}`));
|
|
29844
30089
|
}
|
|
29845
30090
|
if (plan2.newDirectories.length > 0) {
|
|
29846
|
-
console.log(
|
|
30091
|
+
console.log(chalk209.bold("\nNew directories:"));
|
|
29847
30092
|
for (const dir of plan2.newDirectories)
|
|
29848
|
-
console.log(
|
|
30093
|
+
console.log(chalk209.green(` ${dir}/`));
|
|
29849
30094
|
}
|
|
29850
30095
|
displayMoves(plan2);
|
|
29851
30096
|
displayRewrites(plan2.rewrites);
|
|
29852
30097
|
console.log(
|
|
29853
|
-
|
|
30098
|
+
chalk209.dim(
|
|
29854
30099
|
`
|
|
29855
30100
|
Summary: ${plan2.moves.length} file(s) moved, ${plan2.rewrites.length} imports rewritten`
|
|
29856
30101
|
)
|
|
@@ -29860,18 +30105,18 @@ Summary: ${plan2.moves.length} file(s) moved, ${plan2.rewrites.length} imports r
|
|
|
29860
30105
|
// src/commands/refactor/restructure/executePlan.ts
|
|
29861
30106
|
import fs38 from "fs";
|
|
29862
30107
|
import path64 from "path";
|
|
29863
|
-
import
|
|
30108
|
+
import chalk210 from "chalk";
|
|
29864
30109
|
function executePlan(plan2) {
|
|
29865
30110
|
const updatedContents = applyRewrites(plan2.rewrites);
|
|
29866
30111
|
for (const [file, content] of updatedContents) {
|
|
29867
30112
|
fs38.writeFileSync(file, content, "utf8");
|
|
29868
30113
|
console.log(
|
|
29869
|
-
|
|
30114
|
+
chalk210.cyan(` Rewrote imports in ${path64.relative(process.cwd(), file)}`)
|
|
29870
30115
|
);
|
|
29871
30116
|
}
|
|
29872
30117
|
for (const dir of plan2.newDirectories) {
|
|
29873
30118
|
fs38.mkdirSync(dir, { recursive: true });
|
|
29874
|
-
console.log(
|
|
30119
|
+
console.log(chalk210.green(` Created ${path64.relative(process.cwd(), dir)}/`));
|
|
29875
30120
|
}
|
|
29876
30121
|
for (const move2 of plan2.moves) {
|
|
29877
30122
|
const targetDir = path64.dirname(move2.to);
|
|
@@ -29880,7 +30125,7 @@ function executePlan(plan2) {
|
|
|
29880
30125
|
}
|
|
29881
30126
|
fs38.renameSync(move2.from, move2.to);
|
|
29882
30127
|
console.log(
|
|
29883
|
-
|
|
30128
|
+
chalk210.white(
|
|
29884
30129
|
` Moved ${path64.relative(process.cwd(), move2.from)} \u2192 ${path64.relative(process.cwd(), move2.to)}`
|
|
29885
30130
|
)
|
|
29886
30131
|
);
|
|
@@ -29895,7 +30140,7 @@ function removeEmptyDirectories(dirs) {
|
|
|
29895
30140
|
if (entries.length === 0) {
|
|
29896
30141
|
fs38.rmdirSync(dir);
|
|
29897
30142
|
console.log(
|
|
29898
|
-
|
|
30143
|
+
chalk210.dim(
|
|
29899
30144
|
` Removed empty directory ${path64.relative(process.cwd(), dir)}`
|
|
29900
30145
|
)
|
|
29901
30146
|
);
|
|
@@ -30028,22 +30273,22 @@ async function restructure(pattern2, options2 = {}) {
|
|
|
30028
30273
|
const targetPattern = pattern2 ?? "src";
|
|
30029
30274
|
const files = findSourceFiles2(targetPattern);
|
|
30030
30275
|
if (files.length === 0) {
|
|
30031
|
-
console.log(
|
|
30276
|
+
console.log(chalk211.yellow("No files found matching pattern"));
|
|
30032
30277
|
return;
|
|
30033
30278
|
}
|
|
30034
30279
|
const tsConfigPath = findTsConfig(path67.resolve(files[0]));
|
|
30035
30280
|
const plan2 = buildPlan3(files, tsConfigPath);
|
|
30036
30281
|
if (plan2.moves.length === 0) {
|
|
30037
|
-
console.log(
|
|
30282
|
+
console.log(chalk211.green("No restructuring needed"));
|
|
30038
30283
|
return;
|
|
30039
30284
|
}
|
|
30040
30285
|
displayPlan2(plan2);
|
|
30041
30286
|
if (options2.apply) {
|
|
30042
|
-
console.log(
|
|
30287
|
+
console.log(chalk211.bold("\nApplying changes..."));
|
|
30043
30288
|
executePlan(plan2);
|
|
30044
|
-
console.log(
|
|
30289
|
+
console.log(chalk211.green("\nRestructuring complete"));
|
|
30045
30290
|
} else {
|
|
30046
|
-
console.log(
|
|
30291
|
+
console.log(chalk211.dim("\nDry run. Use --apply to execute."));
|
|
30047
30292
|
}
|
|
30048
30293
|
}
|
|
30049
30294
|
|
|
@@ -30096,6 +30341,293 @@ async function checkoutOnlySession(number) {
|
|
|
30096
30341
|
await done2;
|
|
30097
30342
|
}
|
|
30098
30343
|
|
|
30344
|
+
// src/commands/review/highLevel/runHighLevelReview.ts
|
|
30345
|
+
import chalk213 from "chalk";
|
|
30346
|
+
|
|
30347
|
+
// src/commands/review/fetchPrDiffInfo.ts
|
|
30348
|
+
import { execSync as execSync57 } from "child_process";
|
|
30349
|
+
function getCurrentBranch3() {
|
|
30350
|
+
return execSync57("git rev-parse --abbrev-ref HEAD", {
|
|
30351
|
+
encoding: "utf8"
|
|
30352
|
+
}).trim();
|
|
30353
|
+
}
|
|
30354
|
+
function fetchPrDiffInfo() {
|
|
30355
|
+
const { org, repo } = getRepoInfo();
|
|
30356
|
+
const branch2 = getCurrentBranch3();
|
|
30357
|
+
const fields = "number,baseRefName,baseRefOid,headRefName,headRefOid";
|
|
30358
|
+
const raw = execSync57(
|
|
30359
|
+
`gh pr list --state open --head ${branch2} --json ${fields} -R ${org}/${repo}`,
|
|
30360
|
+
{
|
|
30361
|
+
encoding: "utf8",
|
|
30362
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
30363
|
+
}
|
|
30364
|
+
);
|
|
30365
|
+
const parsed = JSON.parse(raw);
|
|
30366
|
+
const pr = parsed[0];
|
|
30367
|
+
if (!pr) {
|
|
30368
|
+
console.error(
|
|
30369
|
+
`Error: No open pull request found for branch \`${branch2}\`. Open a PR for this branch before running \`assist review\`.`
|
|
30370
|
+
);
|
|
30371
|
+
process.exit(1);
|
|
30372
|
+
}
|
|
30373
|
+
return {
|
|
30374
|
+
prNumber: pr.number,
|
|
30375
|
+
baseRef: pr.baseRefName,
|
|
30376
|
+
baseSha: pr.baseRefOid,
|
|
30377
|
+
headRef: pr.headRefName,
|
|
30378
|
+
headSha: pr.headRefOid
|
|
30379
|
+
};
|
|
30380
|
+
}
|
|
30381
|
+
function fetchPrChangedFiles(prNumber) {
|
|
30382
|
+
const { org, repo } = getRepoInfo();
|
|
30383
|
+
const out = execSync57(
|
|
30384
|
+
`gh api repos/${org}/${repo}/pulls/${prNumber}/files --paginate --jq ".[].filename"`,
|
|
30385
|
+
{
|
|
30386
|
+
encoding: "utf8",
|
|
30387
|
+
maxBuffer: 64 * 1024 * 1024
|
|
30388
|
+
}
|
|
30389
|
+
);
|
|
30390
|
+
return out.trim().split("\n").filter(Boolean);
|
|
30391
|
+
}
|
|
30392
|
+
|
|
30393
|
+
// src/commands/review/highLevel/checkDescriptionLinksIssue.ts
|
|
30394
|
+
var ISSUE_URL_PATTERN = /https?:\/\/github\.com\/[\w.-]+\/[\w.-]+\/issues\/\d+/g;
|
|
30395
|
+
var CROSS_REPO_PATTERN = /\b[\w.-]+\/[\w.-]+#\d+/g;
|
|
30396
|
+
var SAME_REPO_PATTERN2 = /(?<![\w/#-])#\d+\b/g;
|
|
30397
|
+
function firstMatch(body, pattern2) {
|
|
30398
|
+
return body.match(pattern2)?.[0] ?? null;
|
|
30399
|
+
}
|
|
30400
|
+
function checkDescriptionLinksIssue(body) {
|
|
30401
|
+
const reference = firstMatch(body, ISSUE_URL_PATTERN) ?? firstMatch(body, CROSS_REPO_PATTERN) ?? firstMatch(body, SAME_REPO_PATTERN2);
|
|
30402
|
+
if (!reference)
|
|
30403
|
+
return {
|
|
30404
|
+
status: "fail",
|
|
30405
|
+
reason: "no GitHub issue reference (`#123`, `owner/repo#123`, or an issue URL) in the description"
|
|
30406
|
+
};
|
|
30407
|
+
return { status: "pass", reason: `links ${reference}` };
|
|
30408
|
+
}
|
|
30409
|
+
|
|
30410
|
+
// src/commands/review/highLevel/checkDescriptionSections.ts
|
|
30411
|
+
function sectionContent(body, heading2) {
|
|
30412
|
+
const section2 = parsePrBody(body).find(
|
|
30413
|
+
(candidate) => candidate.heading.trim().toLowerCase() === heading2
|
|
30414
|
+
);
|
|
30415
|
+
return section2 ? section2.content.trim() : null;
|
|
30416
|
+
}
|
|
30417
|
+
function describe5(heading2, content) {
|
|
30418
|
+
if (content === null) return `no \`## ${heading2}\` section`;
|
|
30419
|
+
if (content === "") return `\`## ${heading2}\` is empty`;
|
|
30420
|
+
return null;
|
|
30421
|
+
}
|
|
30422
|
+
function checkDescriptionSections(body) {
|
|
30423
|
+
const problems = [
|
|
30424
|
+
describe5("What", sectionContent(body, "what")),
|
|
30425
|
+
describe5("Why", sectionContent(body, "why"))
|
|
30426
|
+
].filter((problem) => problem !== null);
|
|
30427
|
+
if (problems.length > 0)
|
|
30428
|
+
return { status: "fail", reason: problems.join("; ") };
|
|
30429
|
+
const how = sectionContent(body, "how");
|
|
30430
|
+
const optional = how ? ", plus an optional `## How`" : "";
|
|
30431
|
+
return {
|
|
30432
|
+
status: "pass",
|
|
30433
|
+
reason: `\`## What\` and \`## Why\` are both present${optional}`
|
|
30434
|
+
};
|
|
30435
|
+
}
|
|
30436
|
+
|
|
30437
|
+
// src/commands/review/highLevel/checkDescriptionWordCap.ts
|
|
30438
|
+
function checkDescriptionWordCap(body, cap2) {
|
|
30439
|
+
const { prose, code } = countReadingWords(body);
|
|
30440
|
+
const words = prose + code;
|
|
30441
|
+
if (words > cap2)
|
|
30442
|
+
return {
|
|
30443
|
+
status: "fail",
|
|
30444
|
+
reason: `${words} words, ${words - cap2} over the ${cap2}-word cap`
|
|
30445
|
+
};
|
|
30446
|
+
return { status: "pass", reason: `${words} of ${cap2} words` };
|
|
30447
|
+
}
|
|
30448
|
+
|
|
30449
|
+
// src/commands/review/highLevel/checkUiEvidence.ts
|
|
30450
|
+
import { minimatch as minimatch7 } from "minimatch";
|
|
30451
|
+
var EVIDENCE_PATTERNS = [
|
|
30452
|
+
/!\[[^\]]*\]\([^)]+\)/g,
|
|
30453
|
+
/<(?:img|video)\b[^>]*>/gi,
|
|
30454
|
+
/https?:\/\/github\.com\/user-attachments\/assets\/\S+/g,
|
|
30455
|
+
/https?:\/\/\S+\.(?:png|jpe?g|gif|webp|avif|svg|mp4|mov|webm)\b/gi
|
|
30456
|
+
];
|
|
30457
|
+
function countEvidence(body) {
|
|
30458
|
+
return EVIDENCE_PATTERNS.reduce(
|
|
30459
|
+
(total, pattern2) => total + (body.match(pattern2)?.length ?? 0),
|
|
30460
|
+
0
|
|
30461
|
+
);
|
|
30462
|
+
}
|
|
30463
|
+
function plural(count8, noun) {
|
|
30464
|
+
return `${count8} ${noun}${count8 === 1 ? "" : "s"}`;
|
|
30465
|
+
}
|
|
30466
|
+
function checkUiEvidence(body, changedFiles2, uiPaths) {
|
|
30467
|
+
if (uiPaths.length === 0)
|
|
30468
|
+
return {
|
|
30469
|
+
status: "pass",
|
|
30470
|
+
reason: "review.highLevel.uiPaths is unset, so no UI evidence is required"
|
|
30471
|
+
};
|
|
30472
|
+
const touched = changedFiles2.filter(
|
|
30473
|
+
(file) => uiPaths.some((glob) => minimatch7(file, glob))
|
|
30474
|
+
);
|
|
30475
|
+
if (touched.length === 0)
|
|
30476
|
+
return {
|
|
30477
|
+
status: "pass",
|
|
30478
|
+
reason: "no changed file matches review.highLevel.uiPaths"
|
|
30479
|
+
};
|
|
30480
|
+
const evidence = countEvidence(body);
|
|
30481
|
+
if (evidence === 0)
|
|
30482
|
+
return {
|
|
30483
|
+
status: "fail",
|
|
30484
|
+
reason: `${plural(touched.length, "UI file")} changed (${touched[0]}) but no screenshot or video in the description`
|
|
30485
|
+
};
|
|
30486
|
+
const shown = evidence === 1 ? "1 screenshot or video" : `${evidence} screenshots or videos`;
|
|
30487
|
+
return {
|
|
30488
|
+
status: "pass",
|
|
30489
|
+
reason: `${shown} for ${plural(touched.length, "changed UI file")}`
|
|
30490
|
+
};
|
|
30491
|
+
}
|
|
30492
|
+
|
|
30493
|
+
// src/commands/review/highLevel/highLevelChecklist.ts
|
|
30494
|
+
var highLevelChecklist = [
|
|
30495
|
+
{
|
|
30496
|
+
id: "description-what-why",
|
|
30497
|
+
kind: "deterministic",
|
|
30498
|
+
title: "Description has a What and a Why",
|
|
30499
|
+
backing: "The PR body's `## What` and `## Why` sections"
|
|
30500
|
+
},
|
|
30501
|
+
{
|
|
30502
|
+
id: "description-word-cap",
|
|
30503
|
+
kind: "deterministic",
|
|
30504
|
+
title: "Description is under the word cap",
|
|
30505
|
+
backing: "The PR body's word count against review.highLevel.descriptionWordCap"
|
|
30506
|
+
},
|
|
30507
|
+
{
|
|
30508
|
+
id: "description-links-issue",
|
|
30509
|
+
kind: "deterministic",
|
|
30510
|
+
title: "Description links the GitHub issue the PR resolves",
|
|
30511
|
+
backing: "GitHub issue references in the PR body"
|
|
30512
|
+
},
|
|
30513
|
+
{
|
|
30514
|
+
id: "ui-evidence",
|
|
30515
|
+
kind: "deterministic",
|
|
30516
|
+
title: "UI changes are evidenced by a screenshot or video",
|
|
30517
|
+
backing: "Images and videos in the PR body, when a changed file matches review.highLevel.uiPaths"
|
|
30518
|
+
},
|
|
30519
|
+
{
|
|
30520
|
+
id: "structure-sensible",
|
|
30521
|
+
kind: "manual",
|
|
30522
|
+
title: "The structure of the change is sensible",
|
|
30523
|
+
backing: "The changed-file tree with add/delete/modify counts and per-file GitHub diff links"
|
|
30524
|
+
},
|
|
30525
|
+
{
|
|
30526
|
+
id: "critical-diffs-correct",
|
|
30527
|
+
kind: "manual",
|
|
30528
|
+
title: "The critical-file diffs are correct",
|
|
30529
|
+
backing: "Full diffs of files matching review.highLevel.criticalPaths"
|
|
30530
|
+
},
|
|
30531
|
+
{
|
|
30532
|
+
id: "backend-pr-linked",
|
|
30533
|
+
kind: "manual",
|
|
30534
|
+
title: "The backend PR is linked, if the change needs backend work",
|
|
30535
|
+
backing: "The PR body"
|
|
30536
|
+
}
|
|
30537
|
+
];
|
|
30538
|
+
|
|
30539
|
+
// src/commands/review/highLevel/evaluateHighLevelChecks.ts
|
|
30540
|
+
var evaluators = {
|
|
30541
|
+
"description-what-why": (subject) => checkDescriptionSections(subject.body),
|
|
30542
|
+
"description-word-cap": (subject) => checkDescriptionWordCap(subject.body, subject.config.descriptionWordCap),
|
|
30543
|
+
"description-links-issue": (subject) => checkDescriptionLinksIssue(subject.body),
|
|
30544
|
+
"ui-evidence": (subject) => checkUiEvidence(subject.body, subject.changedFiles, subject.config.uiPaths)
|
|
30545
|
+
};
|
|
30546
|
+
function evaluateHighLevelChecks(subject) {
|
|
30547
|
+
return highLevelChecklist.map((check2) => {
|
|
30548
|
+
if (check2.kind === "manual")
|
|
30549
|
+
return { ...check2, status: "manual", reason: check2.backing };
|
|
30550
|
+
return { ...check2, ...evaluators[check2.id](subject) };
|
|
30551
|
+
});
|
|
30552
|
+
}
|
|
30553
|
+
|
|
30554
|
+
// src/commands/review/highLevel/formatHighLevelChecklist.ts
|
|
30555
|
+
import chalk212 from "chalk";
|
|
30556
|
+
var MARKERS = {
|
|
30557
|
+
pass: () => chalk212.green("\u2714"),
|
|
30558
|
+
fail: () => chalk212.red("\u2718"),
|
|
30559
|
+
manual: () => chalk212.yellow("\u25A1")
|
|
30560
|
+
};
|
|
30561
|
+
function formatCheck(check2) {
|
|
30562
|
+
const title = check2.status === "fail" ? chalk212.red(check2.title) : chalk212.bold(check2.title);
|
|
30563
|
+
return ` ${MARKERS[check2.status]()} ${title}
|
|
30564
|
+
${chalk212.dim(check2.reason)}`;
|
|
30565
|
+
}
|
|
30566
|
+
function formatGroup(heading2, checks2) {
|
|
30567
|
+
if (checks2.length === 0) return [];
|
|
30568
|
+
return ["", chalk212.bold.underline(heading2), ...checks2.map(formatCheck)];
|
|
30569
|
+
}
|
|
30570
|
+
function formatHighLevelChecklist(checks2) {
|
|
30571
|
+
const deterministic = checks2.filter(
|
|
30572
|
+
(check2) => check2.kind === "deterministic"
|
|
30573
|
+
);
|
|
30574
|
+
const failed2 = deterministic.filter(
|
|
30575
|
+
(check2) => check2.status === "fail"
|
|
30576
|
+
).length;
|
|
30577
|
+
const summary = failed2 === 0 ? chalk212.green(`All ${deterministic.length} deterministic checks pass`) : chalk212.red(
|
|
30578
|
+
`${failed2} of ${deterministic.length} deterministic checks fail`
|
|
30579
|
+
);
|
|
30580
|
+
return [
|
|
30581
|
+
...formatGroup("Deterministic", deterministic),
|
|
30582
|
+
...formatGroup(
|
|
30583
|
+
"Manual",
|
|
30584
|
+
checks2.filter((check2) => check2.kind === "manual")
|
|
30585
|
+
),
|
|
30586
|
+
"",
|
|
30587
|
+
summary
|
|
30588
|
+
].join("\n");
|
|
30589
|
+
}
|
|
30590
|
+
|
|
30591
|
+
// src/commands/review/highLevel/resolveHighLevelConfig.ts
|
|
30592
|
+
var DEFAULT_DESCRIPTION_WORD_CAP = 300;
|
|
30593
|
+
function resolveHighLevelConfig(config) {
|
|
30594
|
+
const highLevel = config.review?.highLevel;
|
|
30595
|
+
return {
|
|
30596
|
+
criticalPaths: highLevel?.criticalPaths ?? [],
|
|
30597
|
+
uiPaths: highLevel?.uiPaths ?? [],
|
|
30598
|
+
descriptionWordCap: highLevel?.descriptionWordCap ?? DEFAULT_DESCRIPTION_WORD_CAP
|
|
30599
|
+
};
|
|
30600
|
+
}
|
|
30601
|
+
|
|
30602
|
+
// src/commands/review/highLevel/runHighLevelReview.ts
|
|
30603
|
+
function resolvePrNumber2(number) {
|
|
30604
|
+
if (number === void 0) return getCurrentPrNumber();
|
|
30605
|
+
const parsed = Number(number);
|
|
30606
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
30607
|
+
console.error(`Error: \`${number}\` is not a pull request number.`);
|
|
30608
|
+
process.exit(1);
|
|
30609
|
+
}
|
|
30610
|
+
return parsed;
|
|
30611
|
+
}
|
|
30612
|
+
function runHighLevelReview(number) {
|
|
30613
|
+
const prNumber = resolvePrNumber2(number);
|
|
30614
|
+
const { org, repo } = getRepoInfo();
|
|
30615
|
+
const config = resolveHighLevelConfig(loadConfig());
|
|
30616
|
+
const body = fetchPrBody(prNumber, { org, repo });
|
|
30617
|
+
const changedFiles2 = fetchPrChangedFiles(prNumber);
|
|
30618
|
+
const checks2 = evaluateHighLevelChecks({ body, changedFiles: changedFiles2, config });
|
|
30619
|
+
console.log(
|
|
30620
|
+
chalk213.bold(`High-level review of ${org}/${repo}#${prNumber}`),
|
|
30621
|
+
chalk213.dim(`\xB7 ${changedFiles2.length} changed files`)
|
|
30622
|
+
);
|
|
30623
|
+
console.log(formatHighLevelChecklist(checks2));
|
|
30624
|
+
console.log(
|
|
30625
|
+
chalk213.dim(
|
|
30626
|
+
"\nManual items are for the reviewer to judge; see docs/high-level-review.md."
|
|
30627
|
+
)
|
|
30628
|
+
);
|
|
30629
|
+
}
|
|
30630
|
+
|
|
30099
30631
|
// src/commands/review/annotateDiffWithLineNumbers.ts
|
|
30100
30632
|
var FILE_HEADER2 = /^\+\+\+ (?:b\/)?(.+)$/;
|
|
30101
30633
|
var HUNK_HEADER2 = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
|
|
@@ -30229,9 +30761,9 @@ function buildReviewPaths(repoRoot2, key) {
|
|
|
30229
30761
|
}
|
|
30230
30762
|
|
|
30231
30763
|
// src/commands/review/fetchExistingComments.ts
|
|
30232
|
-
import { execSync as
|
|
30764
|
+
import { execSync as execSync58 } from "child_process";
|
|
30233
30765
|
function fetchRawComments(org, repo, prNumber) {
|
|
30234
|
-
const out =
|
|
30766
|
+
const out = execSync58(
|
|
30235
30767
|
`gh api --paginate repos/${org}/${repo}/pulls/${prNumber}/comments`,
|
|
30236
30768
|
{ encoding: "utf8", maxBuffer: 64 * 1024 * 1024 }
|
|
30237
30769
|
);
|
|
@@ -30262,14 +30794,14 @@ function fetchExistingComments() {
|
|
|
30262
30794
|
}
|
|
30263
30795
|
|
|
30264
30796
|
// src/commands/review/gatherContext.ts
|
|
30265
|
-
import { execSync as
|
|
30797
|
+
import { execSync as execSync60 } from "child_process";
|
|
30266
30798
|
|
|
30267
30799
|
// src/commands/review/fetchPrDiff.ts
|
|
30268
|
-
import { execSync as
|
|
30800
|
+
import { execSync as execSync59 } from "child_process";
|
|
30269
30801
|
function fetchPrDiff(prNumber, baseSha, headSha) {
|
|
30270
30802
|
const { org, repo } = getRepoInfo();
|
|
30271
30803
|
try {
|
|
30272
|
-
return
|
|
30804
|
+
return execSync59(`gh pr diff ${prNumber} -R ${org}/${repo}`, {
|
|
30273
30805
|
encoding: "utf8",
|
|
30274
30806
|
maxBuffer: 256 * 1024 * 1024,
|
|
30275
30807
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -30284,68 +30816,22 @@ function isDiffTooLarge(error) {
|
|
|
30284
30816
|
}
|
|
30285
30817
|
function fetchDiffViaGit(baseSha, headSha) {
|
|
30286
30818
|
try {
|
|
30287
|
-
|
|
30819
|
+
execSync59(`git fetch origin ${baseSha} ${headSha}`, { stdio: "ignore" });
|
|
30288
30820
|
} catch {
|
|
30289
30821
|
}
|
|
30290
|
-
return
|
|
30822
|
+
return execSync59(`git diff ${baseSha}...${headSha}`, {
|
|
30291
30823
|
encoding: "utf8",
|
|
30292
30824
|
maxBuffer: 256 * 1024 * 1024
|
|
30293
30825
|
});
|
|
30294
30826
|
}
|
|
30295
30827
|
|
|
30296
|
-
// src/commands/review/fetchPrDiffInfo.ts
|
|
30297
|
-
import { execSync as execSync58 } from "child_process";
|
|
30298
|
-
function getCurrentBranch3() {
|
|
30299
|
-
return execSync58("git rev-parse --abbrev-ref HEAD", {
|
|
30300
|
-
encoding: "utf8"
|
|
30301
|
-
}).trim();
|
|
30302
|
-
}
|
|
30303
|
-
function fetchPrDiffInfo() {
|
|
30304
|
-
const { org, repo } = getRepoInfo();
|
|
30305
|
-
const branch2 = getCurrentBranch3();
|
|
30306
|
-
const fields = "number,baseRefName,baseRefOid,headRefName,headRefOid";
|
|
30307
|
-
const raw = execSync58(
|
|
30308
|
-
`gh pr list --state open --head ${branch2} --json ${fields} -R ${org}/${repo}`,
|
|
30309
|
-
{
|
|
30310
|
-
encoding: "utf8",
|
|
30311
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
30312
|
-
}
|
|
30313
|
-
);
|
|
30314
|
-
const parsed = JSON.parse(raw);
|
|
30315
|
-
const pr = parsed[0];
|
|
30316
|
-
if (!pr) {
|
|
30317
|
-
console.error(
|
|
30318
|
-
`Error: No open pull request found for branch \`${branch2}\`. Open a PR for this branch before running \`assist review\`.`
|
|
30319
|
-
);
|
|
30320
|
-
process.exit(1);
|
|
30321
|
-
}
|
|
30322
|
-
return {
|
|
30323
|
-
prNumber: pr.number,
|
|
30324
|
-
baseRef: pr.baseRefName,
|
|
30325
|
-
baseSha: pr.baseRefOid,
|
|
30326
|
-
headRef: pr.headRefName,
|
|
30327
|
-
headSha: pr.headRefOid
|
|
30328
|
-
};
|
|
30329
|
-
}
|
|
30330
|
-
function fetchPrChangedFiles(prNumber) {
|
|
30331
|
-
const { org, repo } = getRepoInfo();
|
|
30332
|
-
const out = execSync58(
|
|
30333
|
-
`gh api repos/${org}/${repo}/pulls/${prNumber}/files --paginate --jq ".[].filename"`,
|
|
30334
|
-
{
|
|
30335
|
-
encoding: "utf8",
|
|
30336
|
-
maxBuffer: 64 * 1024 * 1024
|
|
30337
|
-
}
|
|
30338
|
-
);
|
|
30339
|
-
return out.trim().split("\n").filter(Boolean);
|
|
30340
|
-
}
|
|
30341
|
-
|
|
30342
30828
|
// src/commands/review/gatherContext.ts
|
|
30343
30829
|
function gatherContext() {
|
|
30344
|
-
const branch2 =
|
|
30830
|
+
const branch2 = execSync60("git rev-parse --abbrev-ref HEAD", {
|
|
30345
30831
|
encoding: "utf8"
|
|
30346
30832
|
}).trim();
|
|
30347
|
-
const sha =
|
|
30348
|
-
const shortSha =
|
|
30833
|
+
const sha = execSync60("git rev-parse HEAD", { encoding: "utf8" }).trim();
|
|
30834
|
+
const shortSha = execSync60("git rev-parse --short=7 HEAD", {
|
|
30349
30835
|
encoding: "utf8"
|
|
30350
30836
|
}).trim();
|
|
30351
30837
|
const prInfo = fetchPrDiffInfo();
|
|
@@ -30704,18 +31190,18 @@ function partitionFindingsByDiff(findings, index3) {
|
|
|
30704
31190
|
}
|
|
30705
31191
|
|
|
30706
31192
|
// src/commands/review/warnOutOfDiff.ts
|
|
30707
|
-
import
|
|
31193
|
+
import chalk214 from "chalk";
|
|
30708
31194
|
function warnOutOfDiff(outOfDiff) {
|
|
30709
31195
|
if (outOfDiff.length === 0) return;
|
|
30710
31196
|
console.warn(
|
|
30711
|
-
|
|
31197
|
+
chalk214.yellow(
|
|
30712
31198
|
`Moved ${outOfDiff.length} finding(s) whose lines fall outside the PR diff into the review body (GitHub cannot anchor a comment on these):`
|
|
30713
31199
|
)
|
|
30714
31200
|
);
|
|
30715
31201
|
for (const finding of outOfDiff) {
|
|
30716
31202
|
const range = finding.startLine !== void 0 ? `${finding.startLine}-${finding.line}` : `${finding.line}`;
|
|
30717
31203
|
console.warn(
|
|
30718
|
-
` ${
|
|
31204
|
+
` ${chalk214.yellow("\xB7")} ${finding.title} ${chalk214.dim(
|
|
30719
31205
|
`(${finding.file}:${range})`
|
|
30720
31206
|
)}`
|
|
30721
31207
|
);
|
|
@@ -30739,18 +31225,18 @@ function selectInDiffFindings(lineBound, prDiff) {
|
|
|
30739
31225
|
}
|
|
30740
31226
|
|
|
30741
31227
|
// src/commands/review/warnUnlocated.ts
|
|
30742
|
-
import
|
|
31228
|
+
import chalk215 from "chalk";
|
|
30743
31229
|
function warnUnlocated(unlocated) {
|
|
30744
31230
|
if (unlocated.length === 0) return;
|
|
30745
31231
|
console.warn(
|
|
30746
|
-
|
|
31232
|
+
chalk215.yellow(
|
|
30747
31233
|
`Moved ${unlocated.length} finding(s) without a parseable file:line into the review body:`
|
|
30748
31234
|
)
|
|
30749
31235
|
);
|
|
30750
31236
|
for (const finding of unlocated) {
|
|
30751
|
-
const where = finding.location ||
|
|
31237
|
+
const where = finding.location || chalk215.dim("missing");
|
|
30752
31238
|
console.warn(
|
|
30753
|
-
` ${
|
|
31239
|
+
` ${chalk215.yellow("\xB7")} ${finding.title} ${chalk215.dim(`(${where})`)}`
|
|
30754
31240
|
);
|
|
30755
31241
|
}
|
|
30756
31242
|
}
|
|
@@ -31340,9 +31826,9 @@ import { writeFileSync as writeFileSync39 } from "fs";
|
|
|
31340
31826
|
// src/commands/review/finaliseReviewerSpinner.ts
|
|
31341
31827
|
var SUMMARY_MAX_LEN = 80;
|
|
31342
31828
|
function summariseStderr(stderr) {
|
|
31343
|
-
const
|
|
31344
|
-
if (!
|
|
31345
|
-
const trimmed =
|
|
31829
|
+
const firstLine3 = stderr.split(/\r?\n/).find((l) => l.trim().length > 0);
|
|
31830
|
+
if (!firstLine3) return "";
|
|
31831
|
+
const trimmed = firstLine3.trim();
|
|
31346
31832
|
return trimmed.length > SUMMARY_MAX_LEN ? `${trimmed.slice(0, SUMMARY_MAX_LEN - 1)}\u2026` : trimmed;
|
|
31347
31833
|
}
|
|
31348
31834
|
function finaliseReviewerSpinner(spinner, outcome) {
|
|
@@ -32050,14 +32536,8 @@ async function reviewPr(repoRoot2, options2) {
|
|
|
32050
32536
|
console.log(`Done. Review folder: ${paths.reviewDir}`);
|
|
32051
32537
|
}
|
|
32052
32538
|
|
|
32053
|
-
// src/commands/review/
|
|
32054
|
-
function
|
|
32055
|
-
const repoRoot2 = findRepoRoot(process.cwd());
|
|
32056
|
-
if (repoRoot2) return repoRoot2;
|
|
32057
|
-
console.error("Error: not inside a git repository.");
|
|
32058
|
-
process.exit(1);
|
|
32059
|
-
}
|
|
32060
|
-
function validateOptions(options2) {
|
|
32539
|
+
// src/commands/review/validateReviewOptions.ts
|
|
32540
|
+
function validateReviewOptions(options2) {
|
|
32061
32541
|
if (options2.apply && options2.refine) {
|
|
32062
32542
|
console.error("Error: --apply cannot be combined with --refine.");
|
|
32063
32543
|
process.exit(1);
|
|
@@ -32069,6 +32549,7 @@ function validateOptions(options2) {
|
|
|
32069
32549
|
process.exit(1);
|
|
32070
32550
|
}
|
|
32071
32551
|
validateCheckoutOnly(options2);
|
|
32552
|
+
validateHighLevel(options2);
|
|
32072
32553
|
}
|
|
32073
32554
|
function validateCheckoutOnly(options2) {
|
|
32074
32555
|
if (!options2.checkoutOnly) return;
|
|
@@ -32083,13 +32564,31 @@ function validateCheckoutOnly(options2) {
|
|
|
32083
32564
|
process.exit(1);
|
|
32084
32565
|
}
|
|
32085
32566
|
}
|
|
32567
|
+
function validateHighLevel(options2) {
|
|
32568
|
+
if (!options2.highLevel) return;
|
|
32569
|
+
if (options2.refine || options2.apply || options2.backlog || options2.submit || options2.checkoutOnly) {
|
|
32570
|
+
console.error(
|
|
32571
|
+
"Error: --high-level cannot be combined with --refine, --apply, --backlog, --submit or --checkout-only."
|
|
32572
|
+
);
|
|
32573
|
+
process.exit(1);
|
|
32574
|
+
}
|
|
32575
|
+
}
|
|
32576
|
+
|
|
32577
|
+
// src/commands/review/review.ts
|
|
32578
|
+
function resolveRepoRoot() {
|
|
32579
|
+
const repoRoot2 = findRepoRoot(process.cwd());
|
|
32580
|
+
if (repoRoot2) return repoRoot2;
|
|
32581
|
+
console.error("Error: not inside a git repository.");
|
|
32582
|
+
process.exit(1);
|
|
32583
|
+
}
|
|
32086
32584
|
async function review(options2 = {}) {
|
|
32087
|
-
|
|
32585
|
+
validateReviewOptions(options2);
|
|
32088
32586
|
startReviewLog();
|
|
32089
32587
|
const invokedIn = resolveRepoRoot();
|
|
32090
32588
|
if (options2.checkoutOnly && options2.number)
|
|
32091
32589
|
return checkoutOnlySession(options2.number);
|
|
32092
32590
|
emitActivity({ kind: "command", name: "review" });
|
|
32591
|
+
if (options2.highLevel) return runHighLevelReview(options2.number);
|
|
32093
32592
|
if (!options2.number) return reviewPr(invokedIn, options2);
|
|
32094
32593
|
await checkoutPr(options2.number);
|
|
32095
32594
|
return reviewPr(resolveRepoRoot(), options2);
|
|
@@ -32123,6 +32622,9 @@ function registerReview(program2) {
|
|
|
32123
32622
|
).option(
|
|
32124
32623
|
"--checkout-only",
|
|
32125
32624
|
"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"
|
|
32625
|
+
).option(
|
|
32626
|
+
"--high-level",
|
|
32627
|
+
"Skip the LLM review; evaluate the high-level review checklist against the PR description and changed files and print each item as pass, fail or manual. Nothing is posted to GitHub; cannot be combined with --refine, --apply, --backlog, --submit or --checkout-only"
|
|
32126
32628
|
).option(
|
|
32127
32629
|
"--address-comments",
|
|
32128
32630
|
"After posting and submitting the review, start an Address Comments session (assist review-pr-comments <n>) for the PR; no-op when nothing was posted or the review was not submitted, and only inside an assist session"
|
|
@@ -32145,7 +32647,7 @@ function registerReview(program2) {
|
|
|
32145
32647
|
// src/commands/rules/addRule.ts
|
|
32146
32648
|
import { existsSync as existsSync68, readFileSync as readFileSync52, writeFileSync as writeFileSync41 } from "fs";
|
|
32147
32649
|
import path72 from "path";
|
|
32148
|
-
import
|
|
32650
|
+
import chalk216 from "chalk";
|
|
32149
32651
|
|
|
32150
32652
|
// src/commands/rules/insertRuleBullet.ts
|
|
32151
32653
|
function ruleBullet({ code, title, text: text18 }) {
|
|
@@ -32281,7 +32783,7 @@ function read2(file) {
|
|
|
32281
32783
|
function addRule(text18, options2) {
|
|
32282
32784
|
const rule = text18.trim();
|
|
32283
32785
|
if (rule === "") {
|
|
32284
|
-
console.error(
|
|
32786
|
+
console.error(chalk216.red("Rule text is required"));
|
|
32285
32787
|
process.exitCode = 1;
|
|
32286
32788
|
return;
|
|
32287
32789
|
}
|
|
@@ -32299,13 +32801,13 @@ function addRule(text18, options2) {
|
|
|
32299
32801
|
);
|
|
32300
32802
|
updateScopedRulesIndex(root);
|
|
32301
32803
|
console.log(
|
|
32302
|
-
`Added ${
|
|
32804
|
+
`Added ${chalk216.cyan(code)} to ${path72.relative(process.cwd(), target) || target}`
|
|
32303
32805
|
);
|
|
32304
32806
|
}
|
|
32305
32807
|
|
|
32306
32808
|
// src/commands/rules/indexRules.ts
|
|
32307
32809
|
import path73 from "path";
|
|
32308
|
-
import
|
|
32810
|
+
import chalk217 from "chalk";
|
|
32309
32811
|
function indexRules() {
|
|
32310
32812
|
const startDir = scopeDirectory(process.cwd());
|
|
32311
32813
|
const root = findRepoRoot(startDir) ?? startDir;
|
|
@@ -32313,24 +32815,24 @@ function indexRules() {
|
|
|
32313
32815
|
const rootFile = path73.relative(process.cwd(), path73.join(root, "CLAUDE.md"));
|
|
32314
32816
|
if (directories.length === 0) {
|
|
32315
32817
|
console.log(
|
|
32316
|
-
|
|
32818
|
+
chalk217.gray(`No directories carry their own \`## Rules\` under ${root}`)
|
|
32317
32819
|
);
|
|
32318
32820
|
return;
|
|
32319
32821
|
}
|
|
32320
32822
|
console.log(`Recorded in ${rootFile || "CLAUDE.md"}`);
|
|
32321
32823
|
for (const directory of directories)
|
|
32322
|
-
console.log(` ${
|
|
32824
|
+
console.log(` ${chalk217.cyan(directory)}`);
|
|
32323
32825
|
}
|
|
32324
32826
|
|
|
32325
32827
|
// src/commands/rules/listRules.ts
|
|
32326
32828
|
import path74 from "path";
|
|
32327
|
-
import
|
|
32829
|
+
import chalk218 from "chalk";
|
|
32328
32830
|
function listRules(target, options2 = {}) {
|
|
32329
32831
|
const resolved = path74.resolve(target ?? process.cwd());
|
|
32330
32832
|
const rules3 = readScopedRules(resolved);
|
|
32331
32833
|
if (rules3.length === 0) {
|
|
32332
32834
|
const label2 = path74.relative(process.cwd(), resolved) || ".";
|
|
32333
|
-
console.log(
|
|
32835
|
+
console.log(chalk218.gray(`No rules in scope for ${label2}`));
|
|
32334
32836
|
return;
|
|
32335
32837
|
}
|
|
32336
32838
|
const base = findRepoRoot(scopeDirectory(resolved));
|
|
@@ -32340,14 +32842,14 @@ function listRules(target, options2 = {}) {
|
|
|
32340
32842
|
if (rule.source !== shown) {
|
|
32341
32843
|
shown = rule.source;
|
|
32342
32844
|
console.log(
|
|
32343
|
-
|
|
32845
|
+
chalk218.dim(base ? path74.relative(base, rule.source) : rule.source)
|
|
32344
32846
|
);
|
|
32345
32847
|
}
|
|
32346
32848
|
console.log(
|
|
32347
|
-
` ${
|
|
32849
|
+
` ${chalk218.cyan(rule.code.padEnd(width))} ${rule.title ?? rule.text}`
|
|
32348
32850
|
);
|
|
32349
32851
|
if (options2.full && rule.title)
|
|
32350
|
-
console.log(` ${" ".repeat(width)} ${
|
|
32852
|
+
console.log(` ${" ".repeat(width)} ${chalk218.dim(rule.text)}`);
|
|
32351
32853
|
}
|
|
32352
32854
|
}
|
|
32353
32855
|
|
|
@@ -32376,7 +32878,7 @@ function registerRules(program2) {
|
|
|
32376
32878
|
}
|
|
32377
32879
|
|
|
32378
32880
|
// src/commands/seq/seqAuth.ts
|
|
32379
|
-
import
|
|
32881
|
+
import chalk220 from "chalk";
|
|
32380
32882
|
|
|
32381
32883
|
// src/commands/seq/loadConnections.ts
|
|
32382
32884
|
function loadConnections2() {
|
|
@@ -32405,10 +32907,10 @@ function setDefaultConnection(name) {
|
|
|
32405
32907
|
}
|
|
32406
32908
|
|
|
32407
32909
|
// src/shared/assertUniqueName.ts
|
|
32408
|
-
import
|
|
32910
|
+
import chalk219 from "chalk";
|
|
32409
32911
|
function assertUniqueName(existingNames, name) {
|
|
32410
32912
|
if (existingNames.includes(name)) {
|
|
32411
|
-
console.error(
|
|
32913
|
+
console.error(chalk219.red(`Connection "${name}" already exists.`));
|
|
32412
32914
|
process.exit(1);
|
|
32413
32915
|
}
|
|
32414
32916
|
}
|
|
@@ -32426,16 +32928,16 @@ async function promptConnection2(existingNames) {
|
|
|
32426
32928
|
var seqAuth = createConnectionAuth({
|
|
32427
32929
|
load: loadConnections2,
|
|
32428
32930
|
save: saveConnections2,
|
|
32429
|
-
format: (c) => `${
|
|
32931
|
+
format: (c) => `${chalk220.bold(c.name)} ${c.url}`,
|
|
32430
32932
|
promptNew: promptConnection2,
|
|
32431
32933
|
onFirst: (c) => setDefaultConnection(c.name)
|
|
32432
32934
|
});
|
|
32433
32935
|
|
|
32434
32936
|
// src/commands/seq/seqQuery.ts
|
|
32435
|
-
import
|
|
32937
|
+
import chalk224 from "chalk";
|
|
32436
32938
|
|
|
32437
32939
|
// src/commands/seq/fetchSeq.ts
|
|
32438
|
-
import
|
|
32940
|
+
import chalk221 from "chalk";
|
|
32439
32941
|
async function fetchSeq(conn, path91, params) {
|
|
32440
32942
|
const url = `${conn.url}${path91}?${params}`;
|
|
32441
32943
|
const response = await fetch(url, {
|
|
@@ -32446,7 +32948,7 @@ async function fetchSeq(conn, path91, params) {
|
|
|
32446
32948
|
});
|
|
32447
32949
|
if (!response.ok) {
|
|
32448
32950
|
const body = await response.text();
|
|
32449
|
-
console.error(
|
|
32951
|
+
console.error(chalk221.red(`Seq returned ${response.status}: ${body}`));
|
|
32450
32952
|
process.exit(1);
|
|
32451
32953
|
}
|
|
32452
32954
|
return response;
|
|
@@ -32505,23 +33007,23 @@ async function fetchSeqEvents(conn, params) {
|
|
|
32505
33007
|
}
|
|
32506
33008
|
|
|
32507
33009
|
// src/commands/seq/formatEvent.ts
|
|
32508
|
-
import
|
|
33010
|
+
import chalk222 from "chalk";
|
|
32509
33011
|
function levelColor(level) {
|
|
32510
33012
|
switch (level) {
|
|
32511
33013
|
case "Fatal":
|
|
32512
|
-
return
|
|
33014
|
+
return chalk222.bgRed.white;
|
|
32513
33015
|
case "Error":
|
|
32514
|
-
return
|
|
33016
|
+
return chalk222.red;
|
|
32515
33017
|
case "Warning":
|
|
32516
|
-
return
|
|
33018
|
+
return chalk222.yellow;
|
|
32517
33019
|
case "Information":
|
|
32518
|
-
return
|
|
33020
|
+
return chalk222.cyan;
|
|
32519
33021
|
case "Debug":
|
|
32520
|
-
return
|
|
33022
|
+
return chalk222.gray;
|
|
32521
33023
|
case "Verbose":
|
|
32522
|
-
return
|
|
33024
|
+
return chalk222.dim;
|
|
32523
33025
|
default:
|
|
32524
|
-
return
|
|
33026
|
+
return chalk222.white;
|
|
32525
33027
|
}
|
|
32526
33028
|
}
|
|
32527
33029
|
function levelAbbrev(level) {
|
|
@@ -32562,12 +33064,12 @@ function formatTimestamp(iso) {
|
|
|
32562
33064
|
function formatEvent(event) {
|
|
32563
33065
|
const color = levelColor(event.Level);
|
|
32564
33066
|
const abbrev = levelAbbrev(event.Level);
|
|
32565
|
-
const ts8 =
|
|
33067
|
+
const ts8 = chalk222.dim(formatTimestamp(event.Timestamp));
|
|
32566
33068
|
const msg = renderMessage(event);
|
|
32567
33069
|
const lines2 = [`${ts8} ${color(`[${abbrev}]`)} ${msg}`];
|
|
32568
33070
|
if (event.Exception) {
|
|
32569
33071
|
for (const line of event.Exception.split("\n")) {
|
|
32570
|
-
lines2.push(
|
|
33072
|
+
lines2.push(chalk222.red(` ${line}`));
|
|
32571
33073
|
}
|
|
32572
33074
|
}
|
|
32573
33075
|
return lines2.join("\n");
|
|
@@ -32600,11 +33102,11 @@ function rejectTimestampFilter(filter) {
|
|
|
32600
33102
|
}
|
|
32601
33103
|
|
|
32602
33104
|
// src/shared/resolveNamedConnection.ts
|
|
32603
|
-
import
|
|
33105
|
+
import chalk223 from "chalk";
|
|
32604
33106
|
function resolveNamedConnection(connections, requested, defaultName, kind, authCommand) {
|
|
32605
33107
|
if (connections.length === 0) {
|
|
32606
33108
|
console.error(
|
|
32607
|
-
|
|
33109
|
+
chalk223.red(
|
|
32608
33110
|
`No ${kind} connections configured. Run '${authCommand}' first.`
|
|
32609
33111
|
)
|
|
32610
33112
|
);
|
|
@@ -32613,7 +33115,7 @@ function resolveNamedConnection(connections, requested, defaultName, kind, authC
|
|
|
32613
33115
|
const target = requested ?? defaultName ?? connections[0].name;
|
|
32614
33116
|
const connection = connections.find((c) => c.name === target);
|
|
32615
33117
|
if (!connection) {
|
|
32616
|
-
console.error(
|
|
33118
|
+
console.error(chalk223.red(`${kind} connection "${target}" not found.`));
|
|
32617
33119
|
process.exit(1);
|
|
32618
33120
|
}
|
|
32619
33121
|
return connection;
|
|
@@ -32642,7 +33144,7 @@ async function seqQuery(filter, options2) {
|
|
|
32642
33144
|
new URLSearchParams({ filter, count: String(count8) })
|
|
32643
33145
|
);
|
|
32644
33146
|
if (events.length === 0) {
|
|
32645
|
-
console.log(
|
|
33147
|
+
console.log(chalk224.yellow("No events found."));
|
|
32646
33148
|
return;
|
|
32647
33149
|
}
|
|
32648
33150
|
if (options2.json) {
|
|
@@ -32653,11 +33155,11 @@ async function seqQuery(filter, options2) {
|
|
|
32653
33155
|
for (const event of chronological) {
|
|
32654
33156
|
console.log(formatEvent(event));
|
|
32655
33157
|
}
|
|
32656
|
-
console.log(
|
|
33158
|
+
console.log(chalk224.dim(`
|
|
32657
33159
|
${events.length} events`));
|
|
32658
33160
|
if (events.length >= count8) {
|
|
32659
33161
|
console.log(
|
|
32660
|
-
|
|
33162
|
+
chalk224.yellow(
|
|
32661
33163
|
`Results limited to ${count8}. Use --count to retrieve more.`
|
|
32662
33164
|
)
|
|
32663
33165
|
);
|
|
@@ -32665,10 +33167,10 @@ ${events.length} events`));
|
|
|
32665
33167
|
}
|
|
32666
33168
|
|
|
32667
33169
|
// src/shared/setNamedDefaultConnection.ts
|
|
32668
|
-
import
|
|
33170
|
+
import chalk225 from "chalk";
|
|
32669
33171
|
function setNamedDefaultConnection(connections, name, setDefault, kind) {
|
|
32670
33172
|
if (!connections.find((c) => c.name === name)) {
|
|
32671
|
-
console.error(
|
|
33173
|
+
console.error(chalk225.red(`Connection "${name}" not found.`));
|
|
32672
33174
|
process.exit(1);
|
|
32673
33175
|
}
|
|
32674
33176
|
setDefault(name);
|
|
@@ -32817,7 +33319,7 @@ function registerSlack(program2) {
|
|
|
32817
33319
|
}
|
|
32818
33320
|
|
|
32819
33321
|
// src/commands/sql/sqlAuth.ts
|
|
32820
|
-
import
|
|
33322
|
+
import chalk227 from "chalk";
|
|
32821
33323
|
|
|
32822
33324
|
// src/commands/sql/loadConnections.ts
|
|
32823
33325
|
function loadConnections3() {
|
|
@@ -32846,7 +33348,7 @@ function setDefaultConnection2(name) {
|
|
|
32846
33348
|
}
|
|
32847
33349
|
|
|
32848
33350
|
// src/commands/sql/promptConnection.ts
|
|
32849
|
-
import
|
|
33351
|
+
import chalk226 from "chalk";
|
|
32850
33352
|
async function promptConnection3(existingNames) {
|
|
32851
33353
|
const name = await promptInput("name", "Connection name:", "default");
|
|
32852
33354
|
assertUniqueName(existingNames, name);
|
|
@@ -32854,7 +33356,7 @@ async function promptConnection3(existingNames) {
|
|
|
32854
33356
|
const portStr = await promptInput("port", "Port:", "1433");
|
|
32855
33357
|
const port = Number.parseInt(portStr, 10);
|
|
32856
33358
|
if (!Number.isFinite(port)) {
|
|
32857
|
-
console.error(
|
|
33359
|
+
console.error(chalk226.red(`Invalid port "${portStr}".`));
|
|
32858
33360
|
process.exit(1);
|
|
32859
33361
|
}
|
|
32860
33362
|
const user = await promptInput("user", "User:");
|
|
@@ -32867,13 +33369,13 @@ async function promptConnection3(existingNames) {
|
|
|
32867
33369
|
var sqlAuth = createConnectionAuth({
|
|
32868
33370
|
load: loadConnections3,
|
|
32869
33371
|
save: saveConnections3,
|
|
32870
|
-
format: (c) => `${
|
|
33372
|
+
format: (c) => `${chalk227.bold(c.name)} ${c.server}:${c.port}/${c.database} (${c.user})`,
|
|
32871
33373
|
promptNew: promptConnection3,
|
|
32872
33374
|
onFirst: (c) => setDefaultConnection2(c.name)
|
|
32873
33375
|
});
|
|
32874
33376
|
|
|
32875
33377
|
// src/commands/sql/printTable.ts
|
|
32876
|
-
import
|
|
33378
|
+
import chalk228 from "chalk";
|
|
32877
33379
|
function formatCell(value) {
|
|
32878
33380
|
if (value === null || value === void 0) return "";
|
|
32879
33381
|
if (value instanceof Date) return value.toISOString();
|
|
@@ -32882,7 +33384,7 @@ function formatCell(value) {
|
|
|
32882
33384
|
}
|
|
32883
33385
|
function printTable(rows) {
|
|
32884
33386
|
if (rows.length === 0) {
|
|
32885
|
-
console.log(
|
|
33387
|
+
console.log(chalk228.yellow("(no rows)"));
|
|
32886
33388
|
return;
|
|
32887
33389
|
}
|
|
32888
33390
|
const columns = Object.keys(rows[0]);
|
|
@@ -32890,13 +33392,13 @@ function printTable(rows) {
|
|
|
32890
33392
|
(col) => Math.max(col.length, ...rows.map((r) => formatCell(r[col]).length))
|
|
32891
33393
|
);
|
|
32892
33394
|
const header = columns.map((c, i) => c.padEnd(widths[i])).join(" ");
|
|
32893
|
-
console.log(
|
|
32894
|
-
console.log(
|
|
33395
|
+
console.log(chalk228.dim(header));
|
|
33396
|
+
console.log(chalk228.dim("-".repeat(header.length)));
|
|
32895
33397
|
for (const row of rows) {
|
|
32896
33398
|
const line = columns.map((c, i) => formatCell(row[c]).padEnd(widths[i])).join(" ");
|
|
32897
33399
|
console.log(line);
|
|
32898
33400
|
}
|
|
32899
|
-
console.log(
|
|
33401
|
+
console.log(chalk228.dim(`
|
|
32900
33402
|
${rows.length} row${rows.length === 1 ? "" : "s"}`));
|
|
32901
33403
|
}
|
|
32902
33404
|
|
|
@@ -32956,7 +33458,7 @@ async function sqlColumns(table, connectionName) {
|
|
|
32956
33458
|
}
|
|
32957
33459
|
|
|
32958
33460
|
// src/commands/sql/sqlMutate.ts
|
|
32959
|
-
import
|
|
33461
|
+
import chalk229 from "chalk";
|
|
32960
33462
|
|
|
32961
33463
|
// src/commands/sql/isMutation.ts
|
|
32962
33464
|
var MUTATION_KEYWORDS = [
|
|
@@ -32990,7 +33492,7 @@ function isMutation(sql29) {
|
|
|
32990
33492
|
async function sqlMutate(query, connectionName) {
|
|
32991
33493
|
if (!isMutation(query)) {
|
|
32992
33494
|
console.error(
|
|
32993
|
-
|
|
33495
|
+
chalk229.red(
|
|
32994
33496
|
"assist sql mutate refuses non-mutating statements. Use `assist sql query` instead."
|
|
32995
33497
|
)
|
|
32996
33498
|
);
|
|
@@ -33000,18 +33502,18 @@ async function sqlMutate(query, connectionName) {
|
|
|
33000
33502
|
const pool = await sqlConnect(conn);
|
|
33001
33503
|
try {
|
|
33002
33504
|
const result = await pool.request().query(query);
|
|
33003
|
-
console.log(
|
|
33505
|
+
console.log(chalk229.dim(`${result.rowsAffected.join(", ")} row(s) affected`));
|
|
33004
33506
|
} finally {
|
|
33005
33507
|
await pool.close();
|
|
33006
33508
|
}
|
|
33007
33509
|
}
|
|
33008
33510
|
|
|
33009
33511
|
// src/commands/sql/sqlQuery.ts
|
|
33010
|
-
import
|
|
33512
|
+
import chalk230 from "chalk";
|
|
33011
33513
|
async function sqlQuery(query, connectionName) {
|
|
33012
33514
|
if (isMutation(query)) {
|
|
33013
33515
|
console.error(
|
|
33014
|
-
|
|
33516
|
+
chalk230.red(
|
|
33015
33517
|
"assist sql query refuses mutating statements. Use `assist sql mutate` instead."
|
|
33016
33518
|
)
|
|
33017
33519
|
);
|
|
@@ -33026,7 +33528,7 @@ async function sqlQuery(query, connectionName) {
|
|
|
33026
33528
|
printTable(rows);
|
|
33027
33529
|
} else {
|
|
33028
33530
|
console.log(
|
|
33029
|
-
|
|
33531
|
+
chalk230.dim(`${result.rowsAffected.join(", ")} row(s) affected`)
|
|
33030
33532
|
);
|
|
33031
33533
|
}
|
|
33032
33534
|
} finally {
|
|
@@ -33400,7 +33902,7 @@ function syncPi(claudeDir, options2) {
|
|
|
33400
33902
|
// src/commands/sync/syncSettings.ts
|
|
33401
33903
|
import * as fs48 from "fs";
|
|
33402
33904
|
import * as path84 from "path";
|
|
33403
|
-
import
|
|
33905
|
+
import chalk231 from "chalk";
|
|
33404
33906
|
async function syncSettings(claudeDir, targetBase, options2) {
|
|
33405
33907
|
const source = path84.join(claudeDir, "settings.json");
|
|
33406
33908
|
const target = path84.join(targetBase, "settings.json");
|
|
@@ -33419,7 +33921,7 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
33419
33921
|
if (mergedContent !== normalizedTarget) {
|
|
33420
33922
|
if (!options2?.yes) {
|
|
33421
33923
|
console.log(
|
|
33422
|
-
|
|
33924
|
+
chalk231.yellow(
|
|
33423
33925
|
"\n\u26A0\uFE0F Warning: settings.json differs from existing file"
|
|
33424
33926
|
)
|
|
33425
33927
|
);
|
|
@@ -33427,7 +33929,7 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
33427
33929
|
printDiff(targetContent, mergedContent);
|
|
33428
33930
|
printAutoConfirmHint();
|
|
33429
33931
|
const confirm = await promptConfirm(
|
|
33430
|
-
|
|
33932
|
+
chalk231.red("Overwrite existing settings.json?"),
|
|
33431
33933
|
false
|
|
33432
33934
|
);
|
|
33433
33935
|
if (!confirm) {
|
|
@@ -34296,7 +34798,7 @@ import { mkdirSync as mkdirSync31 } from "fs";
|
|
|
34296
34798
|
import { join as join93 } from "path";
|
|
34297
34799
|
|
|
34298
34800
|
// src/commands/voice/checkLockFile.ts
|
|
34299
|
-
import { execSync as
|
|
34801
|
+
import { execSync as execSync61 } from "child_process";
|
|
34300
34802
|
import { existsSync as existsSync78, mkdirSync as mkdirSync30, readFileSync as readFileSync58, writeFileSync as writeFileSync48 } from "fs";
|
|
34301
34803
|
import { join as join92 } from "path";
|
|
34302
34804
|
function isProcessAlive2(pid) {
|
|
@@ -34325,7 +34827,7 @@ function bootstrapVenv() {
|
|
|
34325
34827
|
if (existsSync78(getVenvPython())) return;
|
|
34326
34828
|
console.log("Setting up Python environment...");
|
|
34327
34829
|
const pythonDir = getPythonDir();
|
|
34328
|
-
|
|
34830
|
+
execSync61(
|
|
34329
34831
|
`uv sync --project "${pythonDir}" --extra runtime --no-install-project`,
|
|
34330
34832
|
{
|
|
34331
34833
|
stdio: "inherit",
|
|
@@ -34946,11 +35448,11 @@ function runCommandToCompletion(command, args, env, cwd, quiet) {
|
|
|
34946
35448
|
}
|
|
34947
35449
|
|
|
34948
35450
|
// src/commands/run/runPreCommands.ts
|
|
34949
|
-
import { execSync as
|
|
35451
|
+
import { execSync as execSync62 } from "child_process";
|
|
34950
35452
|
function runPreCommands(pre, cwd) {
|
|
34951
35453
|
for (const cmd of pre) {
|
|
34952
35454
|
try {
|
|
34953
|
-
|
|
35455
|
+
execSync62(cmd, { stdio: "inherit", cwd });
|
|
34954
35456
|
} catch (error) {
|
|
34955
35457
|
const code = error && typeof error === "object" && "status" in error ? error.status : 1;
|
|
34956
35458
|
process.exit(code);
|
|
@@ -35169,7 +35671,7 @@ function registerWatch(program2) {
|
|
|
35169
35671
|
|
|
35170
35672
|
// src/commands/roam/auth.ts
|
|
35171
35673
|
import { randomBytes } from "crypto";
|
|
35172
|
-
import
|
|
35674
|
+
import chalk232 from "chalk";
|
|
35173
35675
|
|
|
35174
35676
|
// src/commands/roam/waitForCallback.ts
|
|
35175
35677
|
import { createServer as createServer3 } from "http";
|
|
@@ -35300,13 +35802,13 @@ async function auth() {
|
|
|
35300
35802
|
saveGlobalConfig(config);
|
|
35301
35803
|
const state = randomBytes(16).toString("hex");
|
|
35302
35804
|
console.log(
|
|
35303
|
-
|
|
35805
|
+
chalk232.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
|
|
35304
35806
|
);
|
|
35305
|
-
console.log(
|
|
35306
|
-
console.log(
|
|
35307
|
-
console.log(
|
|
35807
|
+
console.log(chalk232.white("http://localhost:14523/callback\n"));
|
|
35808
|
+
console.log(chalk232.blue("Opening browser for authorization..."));
|
|
35809
|
+
console.log(chalk232.dim("Waiting for authorization callback..."));
|
|
35308
35810
|
const { code, redirectUri } = await authorizeInBrowser(clientId, state);
|
|
35309
|
-
console.log(
|
|
35811
|
+
console.log(chalk232.dim("Exchanging code for tokens..."));
|
|
35310
35812
|
const tokens = await exchangeToken({
|
|
35311
35813
|
code,
|
|
35312
35814
|
clientId,
|
|
@@ -35322,7 +35824,7 @@ async function auth() {
|
|
|
35322
35824
|
};
|
|
35323
35825
|
saveGlobalConfig(config);
|
|
35324
35826
|
console.log(
|
|
35325
|
-
|
|
35827
|
+
chalk232.green("Roam credentials and tokens saved to ~/.assist.yml")
|
|
35326
35828
|
);
|
|
35327
35829
|
}
|
|
35328
35830
|
|
|
@@ -35677,11 +36179,11 @@ function registerRun(program2) {
|
|
|
35677
36179
|
}
|
|
35678
36180
|
|
|
35679
36181
|
// src/commands/screenshot/index.ts
|
|
35680
|
-
import { execSync as
|
|
36182
|
+
import { execSync as execSync63 } from "child_process";
|
|
35681
36183
|
import { existsSync as existsSync85, mkdirSync as mkdirSync34, unlinkSync as unlinkSync22, writeFileSync as writeFileSync51 } from "fs";
|
|
35682
36184
|
import { tmpdir as tmpdir9 } from "os";
|
|
35683
36185
|
import { join as join100, resolve as resolve21 } from "path";
|
|
35684
|
-
import
|
|
36186
|
+
import chalk233 from "chalk";
|
|
35685
36187
|
|
|
35686
36188
|
// src/commands/screenshot/captureWindowPs1.ts
|
|
35687
36189
|
var captureWindowPs1 = `
|
|
@@ -35820,7 +36322,7 @@ function runPowerShellScript(processName, outputPath) {
|
|
|
35820
36322
|
const scriptPath = join100(tmpdir9(), `assist-screenshot-${Date.now()}.ps1`);
|
|
35821
36323
|
writeFileSync51(scriptPath, captureWindowPs1, "utf8");
|
|
35822
36324
|
try {
|
|
35823
|
-
|
|
36325
|
+
execSync63(
|
|
35824
36326
|
`powershell -NoProfile -ExecutionPolicy Bypass -File "${scriptPath}" -ProcessName "${processName}" -OutputPath "${outputPath}"`,
|
|
35825
36327
|
{ stdio: ["ignore", "pipe", "pipe"], encoding: "utf8" }
|
|
35826
36328
|
);
|
|
@@ -35832,13 +36334,13 @@ function screenshot(processName) {
|
|
|
35832
36334
|
const config = loadConfig();
|
|
35833
36335
|
const outputDir = resolve21(config.screenshot.outputDir);
|
|
35834
36336
|
const outputPath = buildOutputPath(outputDir, processName);
|
|
35835
|
-
console.log(
|
|
36337
|
+
console.log(chalk233.gray(`Capturing window for process "${processName}" ...`));
|
|
35836
36338
|
try {
|
|
35837
36339
|
runPowerShellScript(processName, outputPath);
|
|
35838
|
-
console.log(
|
|
36340
|
+
console.log(chalk233.green(`Screenshot saved: ${outputPath}`));
|
|
35839
36341
|
} catch (error) {
|
|
35840
36342
|
const msg = error instanceof Error ? error.message : String(error);
|
|
35841
|
-
console.error(
|
|
36343
|
+
console.error(chalk233.red(`Failed to capture screenshot: ${msg}`));
|
|
35842
36344
|
process.exit(1);
|
|
35843
36345
|
}
|
|
35844
36346
|
}
|
|
@@ -41800,7 +42302,7 @@ async function renameSession(title) {
|
|
|
41800
42302
|
|
|
41801
42303
|
// src/commands/sessions/summarise/index.ts
|
|
41802
42304
|
import * as fs57 from "fs";
|
|
41803
|
-
import
|
|
42305
|
+
import chalk234 from "chalk";
|
|
41804
42306
|
|
|
41805
42307
|
// src/commands/sessions/summarise/shared.ts
|
|
41806
42308
|
import * as fs56 from "fs";
|
|
@@ -41859,22 +42361,22 @@ ${firstMessage}`);
|
|
|
41859
42361
|
async function summarise2(options2) {
|
|
41860
42362
|
const files = await discoverSessionFiles();
|
|
41861
42363
|
if (files.length === 0) {
|
|
41862
|
-
console.log(
|
|
42364
|
+
console.log(chalk234.yellow("No sessions found."));
|
|
41863
42365
|
return;
|
|
41864
42366
|
}
|
|
41865
42367
|
const toProcess = selectCandidates(files, options2);
|
|
41866
42368
|
if (toProcess.length === 0) {
|
|
41867
|
-
console.log(
|
|
42369
|
+
console.log(chalk234.green("All sessions already summarised."));
|
|
41868
42370
|
return;
|
|
41869
42371
|
}
|
|
41870
42372
|
console.log(
|
|
41871
|
-
|
|
42373
|
+
chalk234.cyan(
|
|
41872
42374
|
`Summarising ${toProcess.length} session(s) (${files.length} total)\u2026`
|
|
41873
42375
|
)
|
|
41874
42376
|
);
|
|
41875
42377
|
const { succeeded, failed: failed2 } = processSessions(toProcess);
|
|
41876
42378
|
console.log(
|
|
41877
|
-
|
|
42379
|
+
chalk234.green(`Done: ${succeeded} summarised`) + (failed2 > 0 ? chalk234.yellow(`, ${failed2} skipped`) : "")
|
|
41878
42380
|
);
|
|
41879
42381
|
}
|
|
41880
42382
|
function selectCandidates(files, options2) {
|
|
@@ -41894,16 +42396,16 @@ function processSessions(files) {
|
|
|
41894
42396
|
let failed2 = 0;
|
|
41895
42397
|
for (let i = 0; i < files.length; i++) {
|
|
41896
42398
|
const file = files[i];
|
|
41897
|
-
process.stdout.write(
|
|
42399
|
+
process.stdout.write(chalk234.dim(` [${i + 1}/${files.length}] `));
|
|
41898
42400
|
const summary = summariseSession(file);
|
|
41899
42401
|
if (summary) {
|
|
41900
42402
|
writeSummary(file, summary);
|
|
41901
42403
|
succeeded++;
|
|
41902
|
-
process.stdout.write(`${
|
|
42404
|
+
process.stdout.write(`${chalk234.green("\u2713")} ${summary}
|
|
41903
42405
|
`);
|
|
41904
42406
|
} else {
|
|
41905
42407
|
failed2++;
|
|
41906
|
-
process.stdout.write(` ${
|
|
42408
|
+
process.stdout.write(` ${chalk234.yellow("skip")}
|
|
41907
42409
|
`);
|
|
41908
42410
|
}
|
|
41909
42411
|
}
|
|
@@ -41930,7 +42432,7 @@ function registerSessions(program2) {
|
|
|
41930
42432
|
}
|
|
41931
42433
|
|
|
41932
42434
|
// src/commands/statusLine.ts
|
|
41933
|
-
import
|
|
42435
|
+
import chalk236 from "chalk";
|
|
41934
42436
|
|
|
41935
42437
|
// src/shared/contextLevel.ts
|
|
41936
42438
|
function contextLevel(pct) {
|
|
@@ -41940,7 +42442,7 @@ function contextLevel(pct) {
|
|
|
41940
42442
|
}
|
|
41941
42443
|
|
|
41942
42444
|
// src/commands/buildLimitsSegment.ts
|
|
41943
|
-
import
|
|
42445
|
+
import chalk235 from "chalk";
|
|
41944
42446
|
|
|
41945
42447
|
// src/shared/rateLimitLevel.ts
|
|
41946
42448
|
var FIVE_HOUR_SECONDS = 5 * 3600;
|
|
@@ -41978,9 +42480,9 @@ function rateLimitLevel(pct, resetsAt, windowSeconds, now) {
|
|
|
41978
42480
|
|
|
41979
42481
|
// src/commands/buildLimitsSegment.ts
|
|
41980
42482
|
var LEVEL_COLOR = {
|
|
41981
|
-
ok:
|
|
41982
|
-
warn:
|
|
41983
|
-
over:
|
|
42483
|
+
ok: chalk235.green,
|
|
42484
|
+
warn: chalk235.yellow,
|
|
42485
|
+
over: chalk235.red
|
|
41984
42486
|
};
|
|
41985
42487
|
function formatLimit(pct, resetsAt, windowSeconds, fallbackLabel, now) {
|
|
41986
42488
|
const level = rateLimitLevel(pct, resetsAt, windowSeconds, now);
|
|
@@ -42076,7 +42578,7 @@ async function relayUsage(claudeSessionId, transcriptPath2, usedPct) {
|
|
|
42076
42578
|
}
|
|
42077
42579
|
|
|
42078
42580
|
// src/commands/statusLine.ts
|
|
42079
|
-
|
|
42581
|
+
chalk236.level = 3;
|
|
42080
42582
|
function formatNumber(num) {
|
|
42081
42583
|
return num.toLocaleString("en-US");
|
|
42082
42584
|
}
|
|
@@ -42084,9 +42586,9 @@ function colorizePercent(pct) {
|
|
|
42084
42586
|
const label2 = `${Math.round(pct)}%`;
|
|
42085
42587
|
switch (contextLevel(pct)) {
|
|
42086
42588
|
case "red":
|
|
42087
|
-
return
|
|
42589
|
+
return chalk236.red(label2);
|
|
42088
42590
|
case "yellow":
|
|
42089
|
-
return
|
|
42591
|
+
return chalk236.yellow(label2);
|
|
42090
42592
|
default:
|
|
42091
42593
|
return label2;
|
|
42092
42594
|
}
|
|
@@ -42099,7 +42601,7 @@ async function statusLine() {
|
|
|
42099
42601
|
const usedPct = data.context_window.used_percentage ?? 0;
|
|
42100
42602
|
const dir = data.workspace?.current_dir ?? data.cwd;
|
|
42101
42603
|
const branch2 = dir ? readGitBranch(toGitCwd(dir)) : null;
|
|
42102
|
-
const branchSegment = branch2 ? `\u{1F33F}\uFE0F ${
|
|
42604
|
+
const branchSegment = branch2 ? `\u{1F33F}\uFE0F ${chalk236.cyan(branch2)} | ` : "";
|
|
42103
42605
|
console.log(
|
|
42104
42606
|
`${branchSegment}${model} | Tokens - ${formatNumber(totalIn)} \u2191 : ${formatNumber(totalOut)} \u2193 | Context - ${colorizePercent(usedPct)}${buildLimitsSegment(data.rate_limits)}`
|
|
42105
42607
|
);
|
|
@@ -42112,7 +42614,7 @@ async function statusLine() {
|
|
|
42112
42614
|
}
|
|
42113
42615
|
|
|
42114
42616
|
// src/commands/update.ts
|
|
42115
|
-
import { execSync as
|
|
42617
|
+
import { execSync as execSync64 } from "child_process";
|
|
42116
42618
|
import * as path90 from "path";
|
|
42117
42619
|
|
|
42118
42620
|
// src/commands/restartDaemonAfterUpdate.ts
|
|
@@ -42136,7 +42638,7 @@ function isGlobalNpmInstall(dir) {
|
|
|
42136
42638
|
if (resolved.split(path90.sep).includes("node_modules")) {
|
|
42137
42639
|
return true;
|
|
42138
42640
|
}
|
|
42139
|
-
const globalPrefix =
|
|
42641
|
+
const globalPrefix = execSync64("npm prefix -g", { stdio: "pipe" }).toString().trim();
|
|
42140
42642
|
return resolved.toLowerCase().startsWith(path90.resolve(globalPrefix).toLowerCase());
|
|
42141
42643
|
} catch {
|
|
42142
42644
|
return false;
|
|
@@ -42147,18 +42649,18 @@ async function update2() {
|
|
|
42147
42649
|
console.log(`Assist is installed at: ${installDir}`);
|
|
42148
42650
|
if (isGitRepo(installDir)) {
|
|
42149
42651
|
console.log("Detected git repo installation, pulling latest...");
|
|
42150
|
-
|
|
42652
|
+
execSync64("git pull", { cwd: installDir, stdio: "inherit" });
|
|
42151
42653
|
console.log("Installing dependencies...");
|
|
42152
|
-
|
|
42654
|
+
execSync64("npm i", { cwd: installDir, stdio: "inherit" });
|
|
42153
42655
|
console.log("Building...");
|
|
42154
|
-
|
|
42656
|
+
execSync64("npm run build", { cwd: installDir, stdio: "inherit" });
|
|
42155
42657
|
console.log("Syncing commands...");
|
|
42156
|
-
|
|
42658
|
+
execSync64("assist sync", { stdio: "inherit" });
|
|
42157
42659
|
} else if (isGlobalNpmInstall(installDir)) {
|
|
42158
42660
|
console.log("Detected global npm installation, updating...");
|
|
42159
|
-
|
|
42661
|
+
execSync64("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
|
|
42160
42662
|
console.log("Syncing commands...");
|
|
42161
|
-
|
|
42663
|
+
execSync64("assist sync", { stdio: "inherit" });
|
|
42162
42664
|
} else {
|
|
42163
42665
|
console.error(
|
|
42164
42666
|
"Could not determine installation method. Expected a git repo or global npm install."
|
|
@@ -42169,10 +42671,10 @@ async function update2() {
|
|
|
42169
42671
|
}
|
|
42170
42672
|
|
|
42171
42673
|
// src/reportCliError.ts
|
|
42172
|
-
import
|
|
42674
|
+
import chalk237 from "chalk";
|
|
42173
42675
|
function reportCliError(error) {
|
|
42174
42676
|
if (error instanceof InvalidItemIdError || error instanceof AmbiguousRepoConfigError || error instanceof UnknownRepoConfigError || error instanceof MissingRunCwdError || error instanceof MiroExtractError) {
|
|
42175
|
-
console.error(
|
|
42677
|
+
console.error(chalk237.red(error.message));
|
|
42176
42678
|
} else {
|
|
42177
42679
|
console.error(error);
|
|
42178
42680
|
}
|