@staff0rd/assist 0.120.0 → 0.121.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 +7 -3
- package/claude/settings.json +2 -2
- package/dist/index.js +970 -674
- 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.121.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -83,17 +83,17 @@ import { execSync } from "child_process";
|
|
|
83
83
|
// src/shared/loadConfig.ts
|
|
84
84
|
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
85
85
|
import { homedir } from "os";
|
|
86
|
-
import { basename, join } from "path";
|
|
86
|
+
import { basename, dirname, join } from "path";
|
|
87
87
|
import chalk from "chalk";
|
|
88
88
|
import { stringify as stringifyYaml } from "yaml";
|
|
89
89
|
|
|
90
90
|
// src/shared/loadRawYaml.ts
|
|
91
91
|
import { existsSync, readFileSync } from "fs";
|
|
92
92
|
import { parse as parseYaml } from "yaml";
|
|
93
|
-
function loadRawYaml(
|
|
94
|
-
if (!existsSync(
|
|
93
|
+
function loadRawYaml(path44) {
|
|
94
|
+
if (!existsSync(path44)) return {};
|
|
95
95
|
try {
|
|
96
|
-
const content = readFileSync(
|
|
96
|
+
const content = readFileSync(path44, "utf-8");
|
|
97
97
|
return parseYaml(content) || {};
|
|
98
98
|
} catch {
|
|
99
99
|
return {};
|
|
@@ -114,7 +114,8 @@ var runConfigSchema = z.strictObject({
|
|
|
114
114
|
args: z.array(z.string()).optional(),
|
|
115
115
|
params: z.array(runParamSchema).optional(),
|
|
116
116
|
env: z.record(z.string(), z.string()).optional(),
|
|
117
|
-
filter: z.string().optional()
|
|
117
|
+
filter: z.string().optional(),
|
|
118
|
+
pre: z.array(z.string()).optional()
|
|
118
119
|
});
|
|
119
120
|
var transcriptConfigSchema = z.strictObject({
|
|
120
121
|
vttDir: z.string(),
|
|
@@ -192,11 +193,20 @@ var assistConfigSchema = z.strictObject({
|
|
|
192
193
|
});
|
|
193
194
|
|
|
194
195
|
// src/shared/loadConfig.ts
|
|
195
|
-
function
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
196
|
+
function findConfigUp(startDir) {
|
|
197
|
+
let current = startDir;
|
|
198
|
+
while (current !== dirname(current)) {
|
|
199
|
+
const claudePath = join(current, ".claude", "assist.yml");
|
|
200
|
+
if (existsSync2(claudePath)) return claudePath;
|
|
201
|
+
const rootPath = join(current, "assist.yml");
|
|
202
|
+
if (existsSync2(rootPath)) return rootPath;
|
|
203
|
+
current = dirname(current);
|
|
199
204
|
}
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
function getConfigPath() {
|
|
208
|
+
const found = findConfigUp(process.cwd());
|
|
209
|
+
if (found) return found;
|
|
200
210
|
return join(process.cwd(), "assist.yml");
|
|
201
211
|
}
|
|
202
212
|
function getGlobalConfigPath() {
|
|
@@ -342,9 +352,9 @@ function isTraversable(value) {
|
|
|
342
352
|
function stepInto(current, key) {
|
|
343
353
|
return isTraversable(current) ? current[key] : void 0;
|
|
344
354
|
}
|
|
345
|
-
function getNestedValue(obj,
|
|
355
|
+
function getNestedValue(obj, path44) {
|
|
346
356
|
let current = obj;
|
|
347
|
-
for (const key of
|
|
357
|
+
for (const key of path44.split(".")) current = stepInto(current, key);
|
|
348
358
|
return current;
|
|
349
359
|
}
|
|
350
360
|
|
|
@@ -385,8 +395,8 @@ function stepIntoNested(container, key, nextKey) {
|
|
|
385
395
|
}
|
|
386
396
|
return ensureObject(container, resolved);
|
|
387
397
|
}
|
|
388
|
-
function setNestedValue(obj,
|
|
389
|
-
const keys =
|
|
398
|
+
function setNestedValue(obj, path44, value) {
|
|
399
|
+
const keys = path44.split(".");
|
|
390
400
|
const result = { ...obj };
|
|
391
401
|
let current = result;
|
|
392
402
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
@@ -693,7 +703,7 @@ import chalk13 from "chalk";
|
|
|
693
703
|
// src/commands/lint/init.ts
|
|
694
704
|
import { execSync as execSync4 } from "child_process";
|
|
695
705
|
import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
696
|
-
import { dirname as
|
|
706
|
+
import { dirname as dirname6, join as join4 } from "path";
|
|
697
707
|
import { fileURLToPath } from "url";
|
|
698
708
|
import chalk12 from "chalk";
|
|
699
709
|
|
|
@@ -826,7 +836,7 @@ function printDiff(oldContent, newContent) {
|
|
|
826
836
|
}
|
|
827
837
|
|
|
828
838
|
// src/commands/lint/init.ts
|
|
829
|
-
var __dirname2 =
|
|
839
|
+
var __dirname2 = dirname6(fileURLToPath(import.meta.url));
|
|
830
840
|
async function init() {
|
|
831
841
|
removeEslint();
|
|
832
842
|
const biomeConfigPath = "biome.json";
|
|
@@ -1977,11 +1987,11 @@ import enquirer3 from "enquirer";
|
|
|
1977
1987
|
|
|
1978
1988
|
// src/commands/deploy/init/updateWorkflow.ts
|
|
1979
1989
|
import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync9 } from "fs";
|
|
1980
|
-
import { dirname as
|
|
1990
|
+
import { dirname as dirname12, join as join7 } from "path";
|
|
1981
1991
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1982
1992
|
import chalk23 from "chalk";
|
|
1983
1993
|
var WORKFLOW_PATH = ".github/workflows/build.yml";
|
|
1984
|
-
var __dirname3 =
|
|
1994
|
+
var __dirname3 = dirname12(fileURLToPath2(import.meta.url));
|
|
1985
1995
|
function getExistingSiteId() {
|
|
1986
1996
|
if (!existsSync10(WORKFLOW_PATH)) {
|
|
1987
1997
|
return null;
|
|
@@ -2533,7 +2543,7 @@ import { readFileSync as readFileSync12 } from "fs";
|
|
|
2533
2543
|
import {
|
|
2534
2544
|
createServer
|
|
2535
2545
|
} from "http";
|
|
2536
|
-
import { dirname as
|
|
2546
|
+
import { dirname as dirname13, join as join10 } from "path";
|
|
2537
2547
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2538
2548
|
import chalk32 from "chalk";
|
|
2539
2549
|
function respondJson(res, status2, data) {
|
|
@@ -2541,7 +2551,7 @@ function respondJson(res, status2, data) {
|
|
|
2541
2551
|
res.end(JSON.stringify(data));
|
|
2542
2552
|
}
|
|
2543
2553
|
function createBundleHandler(importMetaUrl, bundlePath) {
|
|
2544
|
-
const dir =
|
|
2554
|
+
const dir = dirname13(fileURLToPath3(importMetaUrl));
|
|
2545
2555
|
let cache;
|
|
2546
2556
|
return (_req, res) => {
|
|
2547
2557
|
if (!cache) {
|
|
@@ -2822,22 +2832,22 @@ function extractGraphqlQuery(args) {
|
|
|
2822
2832
|
|
|
2823
2833
|
// src/shared/loadCliReads.ts
|
|
2824
2834
|
import { existsSync as existsSync16, readFileSync as readFileSync13, writeFileSync as writeFileSync13 } from "fs";
|
|
2825
|
-
import { dirname as
|
|
2835
|
+
import { dirname as dirname14, resolve as resolve2 } from "path";
|
|
2826
2836
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
2827
2837
|
var __filename2 = fileURLToPath4(import.meta.url);
|
|
2828
|
-
var __dirname4 =
|
|
2838
|
+
var __dirname4 = dirname14(__filename2);
|
|
2829
2839
|
function getCliReadsPath() {
|
|
2830
2840
|
return resolve2(__dirname4, "..", "assist.cli-reads");
|
|
2831
2841
|
}
|
|
2832
2842
|
var cachedLines;
|
|
2833
2843
|
function getCliReadsLines() {
|
|
2834
2844
|
if (cachedLines) return cachedLines;
|
|
2835
|
-
const
|
|
2836
|
-
if (!existsSync16(
|
|
2845
|
+
const path44 = getCliReadsPath();
|
|
2846
|
+
if (!existsSync16(path44)) {
|
|
2837
2847
|
cachedLines = [];
|
|
2838
2848
|
return cachedLines;
|
|
2839
2849
|
}
|
|
2840
|
-
cachedLines = readFileSync13(
|
|
2850
|
+
cachedLines = readFileSync13(path44, "utf-8").split("\n").filter((line) => line.trim() !== "");
|
|
2841
2851
|
return cachedLines;
|
|
2842
2852
|
}
|
|
2843
2853
|
function loadCliReads() {
|
|
@@ -3043,10 +3053,10 @@ import { join as join12 } from "path";
|
|
|
3043
3053
|
|
|
3044
3054
|
// src/shared/getInstallDir.ts
|
|
3045
3055
|
import { execSync as execSync13 } from "child_process";
|
|
3046
|
-
import { dirname as
|
|
3056
|
+
import { dirname as dirname15, resolve as resolve4 } from "path";
|
|
3047
3057
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
3048
3058
|
var __filename3 = fileURLToPath5(import.meta.url);
|
|
3049
|
-
var __dirname5 =
|
|
3059
|
+
var __dirname5 = dirname15(__filename3);
|
|
3050
3060
|
function getInstallDir() {
|
|
3051
3061
|
return resolve4(__dirname5, "..");
|
|
3052
3062
|
}
|
|
@@ -3189,14 +3199,14 @@ function showProgress(p, label2) {
|
|
|
3189
3199
|
const pct = Math.round(p.done / p.total * 100);
|
|
3190
3200
|
process.stderr.write(`\r\x1B[K[${pct}%] Scanning ${label2}...`);
|
|
3191
3201
|
}
|
|
3192
|
-
async function resolveCommand(cli,
|
|
3193
|
-
showProgress(p,
|
|
3194
|
-
const subHelp = await runHelp([cli, ...
|
|
3202
|
+
async function resolveCommand(cli, path44, description, depth, p) {
|
|
3203
|
+
showProgress(p, path44.join(" "));
|
|
3204
|
+
const subHelp = await runHelp([cli, ...path44]);
|
|
3195
3205
|
if (!subHelp || !hasSubcommands(subHelp)) {
|
|
3196
|
-
return [{ path:
|
|
3206
|
+
return [{ path: path44, description }];
|
|
3197
3207
|
}
|
|
3198
|
-
const children = await discoverAt(cli,
|
|
3199
|
-
return children.length > 0 ? children : [{ path:
|
|
3208
|
+
const children = await discoverAt(cli, path44, depth + 1, p);
|
|
3209
|
+
return children.length > 0 ? children : [{ path: path44, description }];
|
|
3200
3210
|
}
|
|
3201
3211
|
async function discoverAt(cli, parentPath, depth, p) {
|
|
3202
3212
|
if (depth > SAFETY_DEPTH) return [];
|
|
@@ -3344,9 +3354,9 @@ function logPath(cli) {
|
|
|
3344
3354
|
return join12(homedir4(), ".assist", `cli-discover-${safeName}.log`);
|
|
3345
3355
|
}
|
|
3346
3356
|
function readCache(cli) {
|
|
3347
|
-
const
|
|
3348
|
-
if (!existsSync18(
|
|
3349
|
-
return readFileSync15(
|
|
3357
|
+
const path44 = logPath(cli);
|
|
3358
|
+
if (!existsSync18(path44)) return void 0;
|
|
3359
|
+
return readFileSync15(path44, "utf-8");
|
|
3350
3360
|
}
|
|
3351
3361
|
function writeCache(cli, output) {
|
|
3352
3362
|
const dir = join12(homedir4(), ".assist");
|
|
@@ -4428,234 +4438,88 @@ function registerDevlog(program2) {
|
|
|
4428
4438
|
).option("--all", "Show all non-archived repos regardless of push date").action(repos);
|
|
4429
4439
|
}
|
|
4430
4440
|
|
|
4431
|
-
// src/commands/
|
|
4432
|
-
import
|
|
4433
|
-
|
|
4434
|
-
// src/commands/jira/adfToText.ts
|
|
4435
|
-
function renderInline(node) {
|
|
4436
|
-
const text = node.text ?? "";
|
|
4437
|
-
if (node.marks?.some((m) => m.type === "code")) return `\`${text}\``;
|
|
4438
|
-
return text;
|
|
4439
|
-
}
|
|
4440
|
-
function renderChildren(node, indent) {
|
|
4441
|
-
return renderNodes(node.content ?? [], indent);
|
|
4442
|
-
}
|
|
4443
|
-
function renderOrderedList(node, indent) {
|
|
4444
|
-
let counter = 0;
|
|
4445
|
-
return (node.content ?? []).map((item) => {
|
|
4446
|
-
counter++;
|
|
4447
|
-
return renderListItem(item, indent, `${counter}.`);
|
|
4448
|
-
}).join("\n");
|
|
4449
|
-
}
|
|
4450
|
-
function renderBulletList(node, indent) {
|
|
4451
|
-
return (node.content ?? []).map((item) => renderListItem(item, indent, "-")).join("\n");
|
|
4452
|
-
}
|
|
4453
|
-
function renderHeading(node, indent) {
|
|
4454
|
-
const level = node.attrs?.level ?? 1;
|
|
4455
|
-
return `${"#".repeat(level)} ${renderChildren(node, indent)}`;
|
|
4456
|
-
}
|
|
4457
|
-
var renderers = {
|
|
4458
|
-
text: (node) => renderInline(node),
|
|
4459
|
-
paragraph: renderChildren,
|
|
4460
|
-
orderedList: renderOrderedList,
|
|
4461
|
-
bulletList: renderBulletList,
|
|
4462
|
-
listItem: (node, indent) => renderListItem(node, indent, "-"),
|
|
4463
|
-
heading: renderHeading,
|
|
4464
|
-
doc: renderChildren
|
|
4465
|
-
};
|
|
4466
|
-
function renderNode(node, indent) {
|
|
4467
|
-
const renderer = renderers[node.type];
|
|
4468
|
-
if (renderer) return renderer(node, indent);
|
|
4469
|
-
return node.content ? renderChildren(node, indent) : "";
|
|
4470
|
-
}
|
|
4471
|
-
function renderNodes(nodes, indent) {
|
|
4472
|
-
return nodes.map((node) => renderNode(node, indent)).join("");
|
|
4473
|
-
}
|
|
4474
|
-
function isListNode(node) {
|
|
4475
|
-
return node.type === "orderedList" || node.type === "bulletList";
|
|
4476
|
-
}
|
|
4477
|
-
function renderListChild(child, indent, pad, marker, isFirst) {
|
|
4478
|
-
if (isListNode(child)) return renderNodes([child], indent + 1);
|
|
4479
|
-
if (child.type !== "paragraph") return renderNode(child, indent);
|
|
4480
|
-
const text = renderChildren(child, indent);
|
|
4481
|
-
return isFirst ? `${pad}${marker} ${text}` : `${pad} ${text}`;
|
|
4482
|
-
}
|
|
4483
|
-
function renderListItem(node, indent, marker) {
|
|
4484
|
-
const pad = " ".repeat(indent);
|
|
4485
|
-
return (node.content ?? []).map((child, i) => renderListChild(child, indent, pad, marker, i === 0)).join("\n");
|
|
4486
|
-
}
|
|
4487
|
-
function adfToText(doc) {
|
|
4488
|
-
return renderNodes([doc], 0);
|
|
4489
|
-
}
|
|
4490
|
-
|
|
4491
|
-
// src/commands/jira/fetchIssue.ts
|
|
4492
|
-
import { execSync as execSync20 } from "child_process";
|
|
4441
|
+
// src/commands/dotnet/checkBuildLocks.ts
|
|
4442
|
+
import { closeSync, openSync, readdirSync as readdirSync2 } from "fs";
|
|
4443
|
+
import { join as join16 } from "path";
|
|
4493
4444
|
import chalk48 from "chalk";
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
const stderr = error.stderr;
|
|
4504
|
-
if (stderr.includes("unauthorized")) {
|
|
4505
|
-
console.error(
|
|
4506
|
-
chalk48.red("Jira authentication expired."),
|
|
4507
|
-
"Run",
|
|
4508
|
-
chalk48.cyan("assist jira auth"),
|
|
4509
|
-
"to re-authenticate."
|
|
4510
|
-
);
|
|
4511
|
-
process.exit(1);
|
|
4512
|
-
}
|
|
4445
|
+
|
|
4446
|
+
// src/shared/findRepoRoot.ts
|
|
4447
|
+
import { existsSync as existsSync20 } from "fs";
|
|
4448
|
+
import path21 from "path";
|
|
4449
|
+
function findRepoRoot(dir) {
|
|
4450
|
+
let current = dir;
|
|
4451
|
+
while (current !== path21.dirname(current)) {
|
|
4452
|
+
if (existsSync20(path21.join(current, ".git"))) {
|
|
4453
|
+
return current;
|
|
4513
4454
|
}
|
|
4514
|
-
|
|
4515
|
-
process.exit(1);
|
|
4455
|
+
current = path21.dirname(current);
|
|
4516
4456
|
}
|
|
4517
|
-
return
|
|
4457
|
+
return null;
|
|
4518
4458
|
}
|
|
4519
4459
|
|
|
4520
|
-
// src/commands/
|
|
4521
|
-
var
|
|
4522
|
-
function
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
console.log(chalk49.yellow(`No acceptance criteria found on ${issueKey}.`));
|
|
4529
|
-
return;
|
|
4530
|
-
}
|
|
4531
|
-
if (typeof acValue === "string") {
|
|
4532
|
-
console.log(acValue);
|
|
4533
|
-
return;
|
|
4534
|
-
}
|
|
4535
|
-
if (acValue.type === "doc") {
|
|
4536
|
-
console.log(adfToText(acValue));
|
|
4537
|
-
return;
|
|
4460
|
+
// src/commands/dotnet/checkBuildLocks.ts
|
|
4461
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "packages"]);
|
|
4462
|
+
function isLockedDll(debugDir) {
|
|
4463
|
+
let files;
|
|
4464
|
+
try {
|
|
4465
|
+
files = readdirSync2(debugDir, { recursive: true });
|
|
4466
|
+
} catch {
|
|
4467
|
+
return null;
|
|
4538
4468
|
}
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
// src/commands/jira/jiraAuth.ts
|
|
4543
|
-
import { execSync as execSync21 } from "child_process";
|
|
4544
|
-
import Enquirer from "enquirer";
|
|
4545
|
-
|
|
4546
|
-
// src/shared/loadJson.ts
|
|
4547
|
-
import { existsSync as existsSync20, mkdirSync as mkdirSync5, readFileSync as readFileSync18, writeFileSync as writeFileSync17 } from "fs";
|
|
4548
|
-
import { homedir as homedir6 } from "os";
|
|
4549
|
-
import { join as join16 } from "path";
|
|
4550
|
-
function getStoreDir() {
|
|
4551
|
-
return join16(homedir6(), ".assist");
|
|
4552
|
-
}
|
|
4553
|
-
function getStorePath(filename) {
|
|
4554
|
-
return join16(getStoreDir(), filename);
|
|
4555
|
-
}
|
|
4556
|
-
function loadJson(filename) {
|
|
4557
|
-
const path42 = getStorePath(filename);
|
|
4558
|
-
if (existsSync20(path42)) {
|
|
4469
|
+
for (const file of files) {
|
|
4470
|
+
if (!file.toLowerCase().endsWith(".dll")) continue;
|
|
4471
|
+
const dllPath = join16(debugDir, file);
|
|
4559
4472
|
try {
|
|
4560
|
-
|
|
4473
|
+
const fd = openSync(dllPath, "r+");
|
|
4474
|
+
closeSync(fd);
|
|
4561
4475
|
} catch {
|
|
4562
|
-
return
|
|
4476
|
+
return dllPath;
|
|
4563
4477
|
}
|
|
4564
4478
|
}
|
|
4565
|
-
return
|
|
4566
|
-
}
|
|
4567
|
-
function saveJson(filename, data) {
|
|
4568
|
-
const dir = getStoreDir();
|
|
4569
|
-
if (!existsSync20(dir)) {
|
|
4570
|
-
mkdirSync5(dir, { recursive: true });
|
|
4571
|
-
}
|
|
4572
|
-
writeFileSync17(getStorePath(filename), JSON.stringify(data, null, 2));
|
|
4573
|
-
}
|
|
4574
|
-
|
|
4575
|
-
// src/commands/jira/jiraAuth.ts
|
|
4576
|
-
var CONFIG_FILE = "jira.json";
|
|
4577
|
-
async function promptCredentials(config) {
|
|
4578
|
-
const { Input: Input2, Password } = Enquirer;
|
|
4579
|
-
const site = await new Input2({
|
|
4580
|
-
name: "site",
|
|
4581
|
-
message: "Jira site (e.g., mycompany.atlassian.net):",
|
|
4582
|
-
initial: config.site
|
|
4583
|
-
}).run();
|
|
4584
|
-
const email = await new Input2({
|
|
4585
|
-
name: "email",
|
|
4586
|
-
message: "Email:",
|
|
4587
|
-
initial: config.email
|
|
4588
|
-
}).run();
|
|
4589
|
-
const token = await new Password({
|
|
4590
|
-
name: "token",
|
|
4591
|
-
message: "API token (https://id.atlassian.com/manage-profile/security/api-tokens):"
|
|
4592
|
-
}).run();
|
|
4593
|
-
return { site, email, token };
|
|
4479
|
+
return null;
|
|
4594
4480
|
}
|
|
4595
|
-
|
|
4596
|
-
|
|
4481
|
+
function findFirstLockedDll(dir) {
|
|
4482
|
+
let entries;
|
|
4597
4483
|
try {
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
process.exit(1);
|
|
4602
|
-
}
|
|
4603
|
-
execSync21(`acli jira auth login --site ${site} --email "${email}" --token`, {
|
|
4604
|
-
encoding: "utf-8",
|
|
4605
|
-
input: token,
|
|
4606
|
-
stdio: ["pipe", "inherit", "inherit"]
|
|
4607
|
-
});
|
|
4608
|
-
saveJson(CONFIG_FILE, { site, email });
|
|
4609
|
-
console.log("Successfully authenticated with Jira.");
|
|
4610
|
-
} catch (error) {
|
|
4611
|
-
if (error instanceof Error) {
|
|
4612
|
-
console.error("Error authenticating with Jira:", error.message);
|
|
4613
|
-
}
|
|
4614
|
-
process.exit(1);
|
|
4484
|
+
entries = readdirSync2(dir);
|
|
4485
|
+
} catch {
|
|
4486
|
+
return null;
|
|
4615
4487
|
}
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
import chalk50 from "chalk";
|
|
4620
|
-
function viewIssue(issueKey) {
|
|
4621
|
-
const parsed = fetchIssue(issueKey, "summary,description");
|
|
4622
|
-
const fields = parsed?.fields;
|
|
4623
|
-
const summary = fields?.summary;
|
|
4624
|
-
const description = fields?.description;
|
|
4625
|
-
if (summary) {
|
|
4626
|
-
console.log(chalk50.bold(summary));
|
|
4488
|
+
if (entries.includes("bin")) {
|
|
4489
|
+
const locked = isLockedDll(join16(dir, "bin", "Debug"));
|
|
4490
|
+
if (locked) return locked;
|
|
4627
4491
|
}
|
|
4628
|
-
|
|
4629
|
-
if (
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
console.log(adfToText(description));
|
|
4634
|
-
} else {
|
|
4635
|
-
console.log(JSON.stringify(description, null, 2));
|
|
4636
|
-
}
|
|
4492
|
+
for (const entry of entries) {
|
|
4493
|
+
if (SKIP_DIRS.has(entry) || entry === "bin" || entry.startsWith("."))
|
|
4494
|
+
continue;
|
|
4495
|
+
const found = findFirstLockedDll(join16(dir, entry));
|
|
4496
|
+
if (found) return found;
|
|
4637
4497
|
}
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4498
|
+
return null;
|
|
4499
|
+
}
|
|
4500
|
+
function getSearchRoot() {
|
|
4501
|
+
return findRepoRoot(process.cwd()) ?? process.cwd();
|
|
4502
|
+
}
|
|
4503
|
+
function checkBuildLocks(startDir) {
|
|
4504
|
+
const locked = findFirstLockedDll(startDir ?? getSearchRoot());
|
|
4505
|
+
if (locked) {
|
|
4506
|
+
console.error(
|
|
4507
|
+
chalk48.red("Build output locked (is VS debugging?): ") + locked
|
|
4641
4508
|
);
|
|
4509
|
+
process.exit(1);
|
|
4642
4510
|
}
|
|
4643
4511
|
}
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
const jiraCommand = program2.command("jira").description("Jira utilities");
|
|
4648
|
-
jiraCommand.command("auth").description("Authenticate with Jira via API token").action(() => jiraAuth());
|
|
4649
|
-
jiraCommand.command("ac <issue-key>").description("Print acceptance criteria for a Jira issue").action((issueKey) => acceptanceCriteria(issueKey));
|
|
4650
|
-
jiraCommand.command("view <issue-key>").description("Print the title and description of a Jira issue").action((issueKey) => viewIssue(issueKey));
|
|
4512
|
+
async function checkBuildLocksCommand() {
|
|
4513
|
+
checkBuildLocks();
|
|
4514
|
+
console.log(chalk48.green("No build locks detected"));
|
|
4651
4515
|
}
|
|
4652
4516
|
|
|
4653
|
-
// src/commands/
|
|
4654
|
-
import { readFileSync as
|
|
4655
|
-
import
|
|
4517
|
+
// src/commands/dotnet/buildTree.ts
|
|
4518
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
4519
|
+
import path22 from "path";
|
|
4656
4520
|
var PROJECT_REF_RE = /<ProjectReference\s+Include="([^"]+)"/g;
|
|
4657
4521
|
function getProjectRefs(csprojPath) {
|
|
4658
|
-
const content =
|
|
4522
|
+
const content = readFileSync18(csprojPath, "utf-8");
|
|
4659
4523
|
const refs = [];
|
|
4660
4524
|
for (const match of content.matchAll(PROJECT_REF_RE)) {
|
|
4661
4525
|
refs.push(match[1].replace(/\\/g, "/"));
|
|
@@ -4663,16 +4527,16 @@ function getProjectRefs(csprojPath) {
|
|
|
4663
4527
|
return refs;
|
|
4664
4528
|
}
|
|
4665
4529
|
function buildTree(csprojPath, repoRoot, visited = /* @__PURE__ */ new Set()) {
|
|
4666
|
-
const abs =
|
|
4667
|
-
const rel =
|
|
4530
|
+
const abs = path22.resolve(csprojPath);
|
|
4531
|
+
const rel = path22.relative(repoRoot, abs);
|
|
4668
4532
|
const node = { path: abs, relativePath: rel, children: [] };
|
|
4669
4533
|
if (visited.has(abs)) return node;
|
|
4670
4534
|
visited.add(abs);
|
|
4671
|
-
const dir =
|
|
4535
|
+
const dir = path22.dirname(abs);
|
|
4672
4536
|
for (const ref of getProjectRefs(abs)) {
|
|
4673
|
-
const childAbs =
|
|
4537
|
+
const childAbs = path22.resolve(dir, ref);
|
|
4674
4538
|
try {
|
|
4675
|
-
|
|
4539
|
+
readFileSync18(childAbs);
|
|
4676
4540
|
node.children.push(buildTree(childAbs, repoRoot, visited));
|
|
4677
4541
|
} catch {
|
|
4678
4542
|
node.children.push({
|
|
@@ -4696,22 +4560,22 @@ function collectAllDeps(node) {
|
|
|
4696
4560
|
return result;
|
|
4697
4561
|
}
|
|
4698
4562
|
|
|
4699
|
-
// src/commands/
|
|
4700
|
-
import { readdirSync as
|
|
4701
|
-
import
|
|
4563
|
+
// src/commands/dotnet/findContainingSolutions.ts
|
|
4564
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync19, statSync } from "fs";
|
|
4565
|
+
import path23 from "path";
|
|
4702
4566
|
function findSlnFiles(dir, maxDepth, depth = 0) {
|
|
4703
4567
|
if (depth > maxDepth) return [];
|
|
4704
4568
|
const results = [];
|
|
4705
4569
|
let entries;
|
|
4706
4570
|
try {
|
|
4707
|
-
entries =
|
|
4571
|
+
entries = readdirSync3(dir);
|
|
4708
4572
|
} catch {
|
|
4709
4573
|
return results;
|
|
4710
4574
|
}
|
|
4711
4575
|
for (const entry of entries) {
|
|
4712
4576
|
if (entry.startsWith(".") || entry === "node_modules" || entry === "packages")
|
|
4713
4577
|
continue;
|
|
4714
|
-
const full =
|
|
4578
|
+
const full = path23.join(dir, entry);
|
|
4715
4579
|
try {
|
|
4716
4580
|
const stat = statSync(full);
|
|
4717
4581
|
if (stat.isFile() && entry.endsWith(".sln")) {
|
|
@@ -4725,16 +4589,16 @@ function findSlnFiles(dir, maxDepth, depth = 0) {
|
|
|
4725
4589
|
return results;
|
|
4726
4590
|
}
|
|
4727
4591
|
function findContainingSolutions(csprojPath, repoRoot) {
|
|
4728
|
-
const csprojAbs =
|
|
4729
|
-
const csprojBasename =
|
|
4592
|
+
const csprojAbs = path23.resolve(csprojPath);
|
|
4593
|
+
const csprojBasename = path23.basename(csprojAbs);
|
|
4730
4594
|
const slnFiles = findSlnFiles(repoRoot, 3);
|
|
4731
4595
|
const matches = [];
|
|
4732
4596
|
const pattern2 = new RegExp(`[\\\\"/]${escapeRegex(csprojBasename)}"`);
|
|
4733
4597
|
for (const sln of slnFiles) {
|
|
4734
4598
|
try {
|
|
4735
|
-
const content =
|
|
4599
|
+
const content = readFileSync19(sln, "utf-8");
|
|
4736
4600
|
if (pattern2.test(content)) {
|
|
4737
|
-
matches.push(
|
|
4601
|
+
matches.push(path23.relative(repoRoot, sln));
|
|
4738
4602
|
}
|
|
4739
4603
|
} catch {
|
|
4740
4604
|
}
|
|
@@ -4745,31 +4609,31 @@ function escapeRegex(s) {
|
|
|
4745
4609
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4746
4610
|
}
|
|
4747
4611
|
|
|
4748
|
-
// src/commands/
|
|
4749
|
-
import
|
|
4612
|
+
// src/commands/dotnet/printTree.ts
|
|
4613
|
+
import chalk49 from "chalk";
|
|
4750
4614
|
function printNodes(nodes, prefix2) {
|
|
4751
4615
|
for (let i = 0; i < nodes.length; i++) {
|
|
4752
4616
|
const isLast = i === nodes.length - 1;
|
|
4753
4617
|
const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
4754
4618
|
const childPrefix = isLast ? " " : "\u2502 ";
|
|
4755
4619
|
const isMissing = nodes[i].relativePath.startsWith("[MISSING]");
|
|
4756
|
-
const label2 = isMissing ?
|
|
4620
|
+
const label2 = isMissing ? chalk49.red(nodes[i].relativePath) : nodes[i].relativePath;
|
|
4757
4621
|
console.log(`${prefix2}${connector}${label2}`);
|
|
4758
4622
|
printNodes(nodes[i].children, prefix2 + childPrefix);
|
|
4759
4623
|
}
|
|
4760
4624
|
}
|
|
4761
4625
|
function printTree(tree, totalCount, solutions) {
|
|
4762
|
-
console.log(
|
|
4763
|
-
console.log(
|
|
4626
|
+
console.log(chalk49.bold("\nProject Dependency Tree"));
|
|
4627
|
+
console.log(chalk49.cyan(tree.relativePath));
|
|
4764
4628
|
printNodes(tree.children, "");
|
|
4765
|
-
console.log(
|
|
4629
|
+
console.log(chalk49.dim(`
|
|
4766
4630
|
${totalCount} projects total (including root)`));
|
|
4767
|
-
console.log(
|
|
4631
|
+
console.log(chalk49.bold("\nSolution Membership"));
|
|
4768
4632
|
if (solutions.length === 0) {
|
|
4769
|
-
console.log(
|
|
4633
|
+
console.log(chalk49.yellow(" Not found in any .sln"));
|
|
4770
4634
|
} else {
|
|
4771
4635
|
for (const sln of solutions) {
|
|
4772
|
-
console.log(` ${
|
|
4636
|
+
console.log(` ${chalk49.green(sln)}`);
|
|
4773
4637
|
}
|
|
4774
4638
|
}
|
|
4775
4639
|
console.log();
|
|
@@ -4795,41 +4659,25 @@ function printJson(tree, totalCount, solutions) {
|
|
|
4795
4659
|
);
|
|
4796
4660
|
}
|
|
4797
4661
|
|
|
4798
|
-
// src/commands/
|
|
4799
|
-
import { existsSync as existsSync22 } from "fs";
|
|
4800
|
-
import path24 from "path";
|
|
4801
|
-
import chalk52 from "chalk";
|
|
4802
|
-
|
|
4803
|
-
// src/commands/netframework/findRepoRoot.ts
|
|
4662
|
+
// src/commands/dotnet/resolveCsproj.ts
|
|
4804
4663
|
import { existsSync as existsSync21 } from "fs";
|
|
4805
|
-
import
|
|
4806
|
-
|
|
4807
|
-
let current = dir;
|
|
4808
|
-
while (current !== path23.dirname(current)) {
|
|
4809
|
-
if (existsSync21(path23.join(current, ".git"))) {
|
|
4810
|
-
return current;
|
|
4811
|
-
}
|
|
4812
|
-
current = path23.dirname(current);
|
|
4813
|
-
}
|
|
4814
|
-
return null;
|
|
4815
|
-
}
|
|
4816
|
-
|
|
4817
|
-
// src/commands/netframework/resolveCsproj.ts
|
|
4664
|
+
import path24 from "path";
|
|
4665
|
+
import chalk50 from "chalk";
|
|
4818
4666
|
function resolveCsproj(csprojPath) {
|
|
4819
4667
|
const resolved = path24.resolve(csprojPath);
|
|
4820
|
-
if (!
|
|
4821
|
-
console.error(
|
|
4668
|
+
if (!existsSync21(resolved)) {
|
|
4669
|
+
console.error(chalk50.red(`File not found: ${resolved}`));
|
|
4822
4670
|
process.exit(1);
|
|
4823
4671
|
}
|
|
4824
4672
|
const repoRoot = findRepoRoot(path24.dirname(resolved));
|
|
4825
4673
|
if (!repoRoot) {
|
|
4826
|
-
console.error(
|
|
4674
|
+
console.error(chalk50.red("Could not find git repository root"));
|
|
4827
4675
|
process.exit(1);
|
|
4828
4676
|
}
|
|
4829
4677
|
return { resolved, repoRoot };
|
|
4830
4678
|
}
|
|
4831
4679
|
|
|
4832
|
-
// src/commands/
|
|
4680
|
+
// src/commands/dotnet/deps.ts
|
|
4833
4681
|
async function deps(csprojPath, options2) {
|
|
4834
4682
|
const { resolved, repoRoot } = resolveCsproj(csprojPath);
|
|
4835
4683
|
const tree = buildTree(resolved, repoRoot);
|
|
@@ -4842,13 +4690,13 @@ async function deps(csprojPath, options2) {
|
|
|
4842
4690
|
}
|
|
4843
4691
|
}
|
|
4844
4692
|
|
|
4845
|
-
// src/commands/
|
|
4846
|
-
import
|
|
4693
|
+
// src/commands/dotnet/inSln.ts
|
|
4694
|
+
import chalk51 from "chalk";
|
|
4847
4695
|
async function inSln(csprojPath) {
|
|
4848
4696
|
const { resolved, repoRoot } = resolveCsproj(csprojPath);
|
|
4849
4697
|
const solutions = findContainingSolutions(resolved, repoRoot);
|
|
4850
4698
|
if (solutions.length === 0) {
|
|
4851
|
-
console.log(
|
|
4699
|
+
console.log(chalk51.yellow("Not found in any .sln file"));
|
|
4852
4700
|
process.exit(1);
|
|
4853
4701
|
}
|
|
4854
4702
|
for (const sln of solutions) {
|
|
@@ -4856,20 +4704,464 @@ async function inSln(csprojPath) {
|
|
|
4856
4704
|
}
|
|
4857
4705
|
}
|
|
4858
4706
|
|
|
4859
|
-
// src/commands/
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4707
|
+
// src/commands/dotnet/inspect.ts
|
|
4708
|
+
import { existsSync as existsSync23 } from "fs";
|
|
4709
|
+
import path26 from "path";
|
|
4710
|
+
import chalk55 from "chalk";
|
|
4711
|
+
|
|
4712
|
+
// src/shared/formatElapsed.ts
|
|
4713
|
+
function formatElapsed(ms) {
|
|
4714
|
+
const secs = ms / 1e3;
|
|
4715
|
+
if (secs < 60) return `${secs.toFixed(1)}s`;
|
|
4716
|
+
const mins = Math.floor(secs / 60);
|
|
4717
|
+
const remainSecs = secs - mins * 60;
|
|
4718
|
+
return `${mins}m ${remainSecs.toFixed(1)}s`;
|
|
4864
4719
|
}
|
|
4865
4720
|
|
|
4866
|
-
// src/commands/
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4721
|
+
// src/commands/dotnet/deadCodeRules.ts
|
|
4722
|
+
var deadCodeRules = /* @__PURE__ */ new Set([
|
|
4723
|
+
"UnusedMember.Global",
|
|
4724
|
+
"UnusedMember.Local",
|
|
4725
|
+
"UnusedType.Global",
|
|
4726
|
+
"UnusedType.Local",
|
|
4727
|
+
"UnusedParameter.Global",
|
|
4728
|
+
"UnusedParameter.Local",
|
|
4729
|
+
"NotAccessedField.Global",
|
|
4730
|
+
"NotAccessedField.Local",
|
|
4731
|
+
"NotAccessedVariable.Local",
|
|
4732
|
+
"UnusedAutoPropertyAccessor.Global",
|
|
4733
|
+
"UnusedAutoPropertyAccessor.Local",
|
|
4734
|
+
"ClassNeverInstantiated.Global",
|
|
4735
|
+
"ClassNeverInstantiated.Local",
|
|
4736
|
+
"UnusedMethodReturnValue.Global",
|
|
4737
|
+
"UnusedMethodReturnValue.Local",
|
|
4738
|
+
"UnusedVariable.Compiler",
|
|
4739
|
+
"RedundantUsingDirective"
|
|
4740
|
+
]);
|
|
4741
|
+
|
|
4742
|
+
// src/commands/dotnet/displayIssues.ts
|
|
4743
|
+
import chalk52 from "chalk";
|
|
4744
|
+
var SEVERITY_COLOR = {
|
|
4745
|
+
ERROR: chalk52.red,
|
|
4746
|
+
WARNING: chalk52.yellow,
|
|
4747
|
+
SUGGESTION: chalk52.cyan,
|
|
4748
|
+
HINT: chalk52.dim
|
|
4749
|
+
};
|
|
4750
|
+
function groupByFile(issues) {
|
|
4751
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
4752
|
+
for (const issue of issues) {
|
|
4753
|
+
const existing = byFile.get(issue.file);
|
|
4754
|
+
if (existing) {
|
|
4755
|
+
existing.push(issue);
|
|
4756
|
+
} else {
|
|
4757
|
+
byFile.set(issue.file, [issue]);
|
|
4758
|
+
}
|
|
4759
|
+
}
|
|
4760
|
+
return byFile;
|
|
4761
|
+
}
|
|
4762
|
+
function displayIssues(issues) {
|
|
4763
|
+
for (const [file, fileIssues] of groupByFile(issues)) {
|
|
4764
|
+
console.log(chalk52.bold(file));
|
|
4765
|
+
for (const issue of fileIssues.sort((a, b) => a.line - b.line)) {
|
|
4766
|
+
const color = SEVERITY_COLOR[issue.severity] ?? chalk52.white;
|
|
4767
|
+
console.log(
|
|
4768
|
+
` ${chalk52.dim(`${issue.line}:`)} ${color(issue.severity)} [${issue.typeId}] ${issue.message}`
|
|
4769
|
+
);
|
|
4770
|
+
}
|
|
4771
|
+
}
|
|
4772
|
+
console.log(chalk52.dim(`
|
|
4773
|
+
${issues.length} issue(s) found`));
|
|
4774
|
+
}
|
|
4775
|
+
|
|
4776
|
+
// src/commands/dotnet/findSolution.ts
|
|
4777
|
+
import { readdirSync as readdirSync4 } from "fs";
|
|
4778
|
+
import { dirname as dirname16, join as join17 } from "path";
|
|
4779
|
+
import chalk53 from "chalk";
|
|
4780
|
+
function findSlnInDir(dir) {
|
|
4781
|
+
try {
|
|
4782
|
+
return readdirSync4(dir).filter((f) => f.endsWith(".sln")).map((f) => join17(dir, f));
|
|
4783
|
+
} catch {
|
|
4784
|
+
return [];
|
|
4785
|
+
}
|
|
4786
|
+
}
|
|
4787
|
+
function findSolution() {
|
|
4788
|
+
const repoRoot = findRepoRoot(process.cwd());
|
|
4789
|
+
const ceiling = repoRoot ?? process.cwd();
|
|
4790
|
+
let current = process.cwd();
|
|
4791
|
+
while (true) {
|
|
4792
|
+
const slnFiles = findSlnInDir(current);
|
|
4793
|
+
if (slnFiles.length === 1) return slnFiles[0];
|
|
4794
|
+
if (slnFiles.length > 1) {
|
|
4795
|
+
console.error(chalk53.red(`Multiple .sln files found in ${current}:`));
|
|
4796
|
+
for (const f of slnFiles) console.error(` ${f}`);
|
|
4797
|
+
console.error(
|
|
4798
|
+
chalk53.yellow("Specify which one: assist dotnet inspect <sln>")
|
|
4799
|
+
);
|
|
4800
|
+
process.exit(1);
|
|
4801
|
+
}
|
|
4802
|
+
if (current === ceiling) break;
|
|
4803
|
+
current = dirname16(current);
|
|
4804
|
+
}
|
|
4805
|
+
console.error(chalk53.red("No .sln file found between cwd and repo root"));
|
|
4806
|
+
process.exit(1);
|
|
4807
|
+
}
|
|
4808
|
+
|
|
4809
|
+
// src/commands/dotnet/getChangedCsFiles.ts
|
|
4810
|
+
import { execSync as execSync20 } from "child_process";
|
|
4811
|
+
function getChangedCsFiles(ref) {
|
|
4812
|
+
const cmd = ref ? `git diff --name-only ${ref}~1 ${ref}` : "git diff --name-only HEAD";
|
|
4813
|
+
const output = execSync20(cmd, { encoding: "utf-8" }).trim();
|
|
4814
|
+
if (output === "") return [];
|
|
4815
|
+
return output.split("\n").filter((f) => f.toLowerCase().endsWith(".cs"));
|
|
4816
|
+
}
|
|
4817
|
+
|
|
4818
|
+
// src/commands/dotnet/parseInspectReport.ts
|
|
4819
|
+
var LEVEL_TO_SEVERITY = {
|
|
4820
|
+
error: "ERROR",
|
|
4821
|
+
warning: "WARNING",
|
|
4822
|
+
note: "SUGGESTION"
|
|
4823
|
+
};
|
|
4824
|
+
function parseInspectReport(json) {
|
|
4825
|
+
const sarif = JSON.parse(json);
|
|
4826
|
+
const results = sarif.runs?.[0]?.results;
|
|
4827
|
+
if (!Array.isArray(results)) return [];
|
|
4828
|
+
return results.map((r) => ({
|
|
4829
|
+
typeId: r.ruleId,
|
|
4830
|
+
file: r.locations?.[0]?.physicalLocation?.artifactLocation?.uri ?? "",
|
|
4831
|
+
line: r.locations?.[0]?.physicalLocation?.region?.startLine ?? 0,
|
|
4832
|
+
message: r.message?.text ?? "",
|
|
4833
|
+
severity: LEVEL_TO_SEVERITY[r.level] ?? "WARNING"
|
|
4834
|
+
}));
|
|
4835
|
+
}
|
|
4836
|
+
|
|
4837
|
+
// src/commands/dotnet/runInspectCode.ts
|
|
4838
|
+
import { execSync as execSync21 } from "child_process";
|
|
4839
|
+
import { existsSync as existsSync22, readFileSync as readFileSync20, unlinkSync as unlinkSync3 } from "fs";
|
|
4840
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
4841
|
+
import path25 from "path";
|
|
4842
|
+
import chalk54 from "chalk";
|
|
4843
|
+
function assertJbInstalled() {
|
|
4844
|
+
try {
|
|
4845
|
+
execSync21("jb inspectcode --version", { stdio: "pipe" });
|
|
4846
|
+
} catch {
|
|
4847
|
+
console.error(chalk54.red("jb is not installed. Install with:"));
|
|
4848
|
+
console.error(
|
|
4849
|
+
chalk54.yellow(" dotnet tool install -g JetBrains.ReSharper.GlobalTools")
|
|
4850
|
+
);
|
|
4851
|
+
process.exit(1);
|
|
4852
|
+
}
|
|
4853
|
+
}
|
|
4854
|
+
function runInspectCode(slnPath, include, swea) {
|
|
4855
|
+
const reportPath = path25.join(tmpdir2(), `inspect-${Date.now()}.xml`);
|
|
4856
|
+
const sweaFlag = swea ? " --swea" : "";
|
|
4857
|
+
try {
|
|
4858
|
+
execSync21(
|
|
4859
|
+
`jb inspectcode "${slnPath}" -o="${reportPath}" --include="${include}"${sweaFlag} --verbosity=OFF`,
|
|
4860
|
+
{ stdio: "pipe" }
|
|
4861
|
+
);
|
|
4862
|
+
} catch (err) {
|
|
4863
|
+
if (err && typeof err === "object" && "stderr" in err) {
|
|
4864
|
+
process.stderr.write(err.stderr);
|
|
4865
|
+
}
|
|
4866
|
+
console.error(chalk54.red("jb inspectcode failed"));
|
|
4867
|
+
process.exit(1);
|
|
4868
|
+
}
|
|
4869
|
+
if (!existsSync22(reportPath)) {
|
|
4870
|
+
console.error(chalk54.red("Report file not generated"));
|
|
4871
|
+
process.exit(1);
|
|
4872
|
+
}
|
|
4873
|
+
const xml = readFileSync20(reportPath, "utf-8");
|
|
4874
|
+
unlinkSync3(reportPath);
|
|
4875
|
+
return xml;
|
|
4876
|
+
}
|
|
4877
|
+
|
|
4878
|
+
// src/commands/dotnet/inspect.ts
|
|
4879
|
+
function resolveSolution(sln) {
|
|
4880
|
+
if (sln) {
|
|
4881
|
+
const resolved = path26.resolve(sln);
|
|
4882
|
+
if (!existsSync23(resolved)) {
|
|
4883
|
+
console.error(chalk55.red(`Solution file not found: ${resolved}`));
|
|
4884
|
+
process.exit(1);
|
|
4885
|
+
}
|
|
4886
|
+
return resolved;
|
|
4887
|
+
}
|
|
4888
|
+
return findSolution();
|
|
4889
|
+
}
|
|
4890
|
+
function runAndParse(resolved, changedFiles, all, swea) {
|
|
4891
|
+
const start3 = Date.now();
|
|
4892
|
+
const report = runInspectCode(resolved, changedFiles.join(";"), swea);
|
|
4893
|
+
const elapsed = Date.now() - start3;
|
|
4894
|
+
const allIssues = parseInspectReport(report);
|
|
4895
|
+
const issues = all ? allIssues : allIssues.filter((i) => deadCodeRules.has(i.typeId));
|
|
4896
|
+
return { issues, elapsed };
|
|
4897
|
+
}
|
|
4898
|
+
function reportResults(issues, elapsed) {
|
|
4899
|
+
if (issues.length > 0) displayIssues(issues);
|
|
4900
|
+
else console.log(chalk55.green("No issues found"));
|
|
4901
|
+
console.log(chalk55.dim(`Completed in ${formatElapsed(elapsed)}`));
|
|
4902
|
+
if (issues.length > 0) process.exit(1);
|
|
4903
|
+
}
|
|
4904
|
+
async function inspect(sln, options2) {
|
|
4905
|
+
const resolved = resolveSolution(sln);
|
|
4906
|
+
checkBuildLocks();
|
|
4907
|
+
assertJbInstalled();
|
|
4908
|
+
const changedFiles = getChangedCsFiles(options2.ref);
|
|
4909
|
+
if (changedFiles.length === 0) {
|
|
4910
|
+
console.log(chalk55.green("No changed .cs files found"));
|
|
4911
|
+
return;
|
|
4912
|
+
}
|
|
4913
|
+
console.log(
|
|
4914
|
+
chalk55.dim(`Inspecting ${changedFiles.length} changed file(s)...`)
|
|
4915
|
+
);
|
|
4916
|
+
const result = runAndParse(
|
|
4917
|
+
resolved,
|
|
4918
|
+
changedFiles,
|
|
4919
|
+
!!options2.all,
|
|
4920
|
+
!!options2.swea
|
|
4921
|
+
);
|
|
4922
|
+
reportResults(result.issues, result.elapsed);
|
|
4923
|
+
}
|
|
4924
|
+
|
|
4925
|
+
// src/commands/registerDotnet.ts
|
|
4926
|
+
function registerDotnet(program2) {
|
|
4927
|
+
const cmd = program2.command("dotnet").description(".NET project utilities");
|
|
4928
|
+
cmd.command("inspect").description(
|
|
4929
|
+
"Run JetBrains inspections on changed .cs files to find dead code"
|
|
4930
|
+
).argument("[sln]", "Path to a .sln file (auto-detected if omitted)").option("--ref <ref>", "Git commit to inspect (default: working copy)").option("--all", "Show all issues, not just dead code").option("--swea", "Enable solution-wide error analysis").action(inspect);
|
|
4931
|
+
cmd.command("check-locks").description("Check if build output files are locked by a debugger").action(checkBuildLocksCommand);
|
|
4932
|
+
cmd.command("deps").description("Show .csproj project dependency tree and solution membership").argument("<csproj>", "Path to a .csproj file").option("--json", "Output as JSON").action(deps);
|
|
4933
|
+
cmd.command("in-sln").description("Check whether a .csproj is referenced by any .sln file").argument("<csproj>", "Path to a .csproj file").action(inSln);
|
|
4934
|
+
}
|
|
4935
|
+
|
|
4936
|
+
// src/commands/jira/acceptanceCriteria.ts
|
|
4937
|
+
import chalk57 from "chalk";
|
|
4938
|
+
|
|
4939
|
+
// src/commands/jira/adfToText.ts
|
|
4940
|
+
function renderInline(node) {
|
|
4941
|
+
const text = node.text ?? "";
|
|
4942
|
+
if (node.marks?.some((m) => m.type === "code")) return `\`${text}\``;
|
|
4943
|
+
return text;
|
|
4944
|
+
}
|
|
4945
|
+
function renderChildren(node, indent) {
|
|
4946
|
+
return renderNodes(node.content ?? [], indent);
|
|
4947
|
+
}
|
|
4948
|
+
function renderOrderedList(node, indent) {
|
|
4949
|
+
let counter = 0;
|
|
4950
|
+
return (node.content ?? []).map((item) => {
|
|
4951
|
+
counter++;
|
|
4952
|
+
return renderListItem(item, indent, `${counter}.`);
|
|
4953
|
+
}).join("\n");
|
|
4954
|
+
}
|
|
4955
|
+
function renderBulletList(node, indent) {
|
|
4956
|
+
return (node.content ?? []).map((item) => renderListItem(item, indent, "-")).join("\n");
|
|
4957
|
+
}
|
|
4958
|
+
function renderHeading(node, indent) {
|
|
4959
|
+
const level = node.attrs?.level ?? 1;
|
|
4960
|
+
return `${"#".repeat(level)} ${renderChildren(node, indent)}`;
|
|
4961
|
+
}
|
|
4962
|
+
var renderers = {
|
|
4963
|
+
text: (node) => renderInline(node),
|
|
4964
|
+
paragraph: renderChildren,
|
|
4965
|
+
orderedList: renderOrderedList,
|
|
4966
|
+
bulletList: renderBulletList,
|
|
4967
|
+
listItem: (node, indent) => renderListItem(node, indent, "-"),
|
|
4968
|
+
heading: renderHeading,
|
|
4969
|
+
doc: renderChildren
|
|
4970
|
+
};
|
|
4971
|
+
function renderNode(node, indent) {
|
|
4972
|
+
const renderer = renderers[node.type];
|
|
4973
|
+
if (renderer) return renderer(node, indent);
|
|
4974
|
+
return node.content ? renderChildren(node, indent) : "";
|
|
4975
|
+
}
|
|
4976
|
+
function renderNodes(nodes, indent) {
|
|
4977
|
+
return nodes.map((node) => renderNode(node, indent)).join("");
|
|
4978
|
+
}
|
|
4979
|
+
function isListNode(node) {
|
|
4980
|
+
return node.type === "orderedList" || node.type === "bulletList";
|
|
4981
|
+
}
|
|
4982
|
+
function renderListChild(child, indent, pad, marker, isFirst) {
|
|
4983
|
+
if (isListNode(child)) return renderNodes([child], indent + 1);
|
|
4984
|
+
if (child.type !== "paragraph") return renderNode(child, indent);
|
|
4985
|
+
const text = renderChildren(child, indent);
|
|
4986
|
+
return isFirst ? `${pad}${marker} ${text}` : `${pad} ${text}`;
|
|
4987
|
+
}
|
|
4988
|
+
function renderListItem(node, indent, marker) {
|
|
4989
|
+
const pad = " ".repeat(indent);
|
|
4990
|
+
return (node.content ?? []).map((child, i) => renderListChild(child, indent, pad, marker, i === 0)).join("\n");
|
|
4991
|
+
}
|
|
4992
|
+
function adfToText(doc) {
|
|
4993
|
+
return renderNodes([doc], 0);
|
|
4994
|
+
}
|
|
4995
|
+
|
|
4996
|
+
// src/commands/jira/fetchIssue.ts
|
|
4997
|
+
import { execSync as execSync22 } from "child_process";
|
|
4998
|
+
import chalk56 from "chalk";
|
|
4999
|
+
function fetchIssue(issueKey, fields) {
|
|
5000
|
+
let result;
|
|
5001
|
+
try {
|
|
5002
|
+
result = execSync22(
|
|
5003
|
+
`acli jira workitem view ${issueKey} -f ${fields} --json`,
|
|
5004
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
5005
|
+
);
|
|
5006
|
+
} catch (error) {
|
|
5007
|
+
if (error instanceof Error && "stderr" in error) {
|
|
5008
|
+
const stderr = error.stderr;
|
|
5009
|
+
if (stderr.includes("unauthorized")) {
|
|
5010
|
+
console.error(
|
|
5011
|
+
chalk56.red("Jira authentication expired."),
|
|
5012
|
+
"Run",
|
|
5013
|
+
chalk56.cyan("assist jira auth"),
|
|
5014
|
+
"to re-authenticate."
|
|
5015
|
+
);
|
|
5016
|
+
process.exit(1);
|
|
5017
|
+
}
|
|
5018
|
+
}
|
|
5019
|
+
console.error(chalk56.red(`Failed to fetch ${issueKey}.`));
|
|
5020
|
+
process.exit(1);
|
|
5021
|
+
}
|
|
5022
|
+
return JSON.parse(result);
|
|
5023
|
+
}
|
|
5024
|
+
|
|
5025
|
+
// src/commands/jira/acceptanceCriteria.ts
|
|
5026
|
+
var DEFAULT_AC_FIELD = "customfield_11937";
|
|
5027
|
+
function acceptanceCriteria(issueKey) {
|
|
5028
|
+
const config = loadConfig();
|
|
5029
|
+
const field = config.jira?.acField ?? DEFAULT_AC_FIELD;
|
|
5030
|
+
const parsed = fetchIssue(issueKey, field);
|
|
5031
|
+
const acValue = parsed?.fields?.[field];
|
|
5032
|
+
if (!acValue) {
|
|
5033
|
+
console.log(chalk57.yellow(`No acceptance criteria found on ${issueKey}.`));
|
|
5034
|
+
return;
|
|
5035
|
+
}
|
|
5036
|
+
if (typeof acValue === "string") {
|
|
5037
|
+
console.log(acValue);
|
|
5038
|
+
return;
|
|
5039
|
+
}
|
|
5040
|
+
if (acValue.type === "doc") {
|
|
5041
|
+
console.log(adfToText(acValue));
|
|
5042
|
+
return;
|
|
5043
|
+
}
|
|
5044
|
+
console.log(JSON.stringify(acValue, null, 2));
|
|
5045
|
+
}
|
|
5046
|
+
|
|
5047
|
+
// src/commands/jira/jiraAuth.ts
|
|
5048
|
+
import { execSync as execSync23 } from "child_process";
|
|
5049
|
+
import Enquirer from "enquirer";
|
|
5050
|
+
|
|
5051
|
+
// src/shared/loadJson.ts
|
|
5052
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync5, readFileSync as readFileSync21, writeFileSync as writeFileSync17 } from "fs";
|
|
5053
|
+
import { homedir as homedir6 } from "os";
|
|
5054
|
+
import { join as join18 } from "path";
|
|
5055
|
+
function getStoreDir() {
|
|
5056
|
+
return join18(homedir6(), ".assist");
|
|
5057
|
+
}
|
|
5058
|
+
function getStorePath(filename) {
|
|
5059
|
+
return join18(getStoreDir(), filename);
|
|
5060
|
+
}
|
|
5061
|
+
function loadJson(filename) {
|
|
5062
|
+
const path44 = getStorePath(filename);
|
|
5063
|
+
if (existsSync24(path44)) {
|
|
5064
|
+
try {
|
|
5065
|
+
return JSON.parse(readFileSync21(path44, "utf-8"));
|
|
5066
|
+
} catch {
|
|
5067
|
+
return {};
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
5070
|
+
return {};
|
|
5071
|
+
}
|
|
5072
|
+
function saveJson(filename, data) {
|
|
5073
|
+
const dir = getStoreDir();
|
|
5074
|
+
if (!existsSync24(dir)) {
|
|
5075
|
+
mkdirSync5(dir, { recursive: true });
|
|
5076
|
+
}
|
|
5077
|
+
writeFileSync17(getStorePath(filename), JSON.stringify(data, null, 2));
|
|
5078
|
+
}
|
|
5079
|
+
|
|
5080
|
+
// src/commands/jira/jiraAuth.ts
|
|
5081
|
+
var CONFIG_FILE = "jira.json";
|
|
5082
|
+
async function promptCredentials(config) {
|
|
5083
|
+
const { Input: Input2, Password } = Enquirer;
|
|
5084
|
+
const site = await new Input2({
|
|
5085
|
+
name: "site",
|
|
5086
|
+
message: "Jira site (e.g., mycompany.atlassian.net):",
|
|
5087
|
+
initial: config.site
|
|
5088
|
+
}).run();
|
|
5089
|
+
const email = await new Input2({
|
|
5090
|
+
name: "email",
|
|
5091
|
+
message: "Email:",
|
|
5092
|
+
initial: config.email
|
|
5093
|
+
}).run();
|
|
5094
|
+
const token = await new Password({
|
|
5095
|
+
name: "token",
|
|
5096
|
+
message: "API token (https://id.atlassian.com/manage-profile/security/api-tokens):"
|
|
5097
|
+
}).run();
|
|
5098
|
+
return { site, email, token };
|
|
5099
|
+
}
|
|
5100
|
+
async function jiraAuth() {
|
|
5101
|
+
const config = loadJson(CONFIG_FILE);
|
|
5102
|
+
try {
|
|
5103
|
+
const { site, email, token } = await promptCredentials(config);
|
|
5104
|
+
if (!site || !email || !token) {
|
|
5105
|
+
console.error("All fields are required.");
|
|
5106
|
+
process.exit(1);
|
|
5107
|
+
}
|
|
5108
|
+
execSync23(`acli jira auth login --site ${site} --email "${email}" --token`, {
|
|
5109
|
+
encoding: "utf-8",
|
|
5110
|
+
input: token,
|
|
5111
|
+
stdio: ["pipe", "inherit", "inherit"]
|
|
5112
|
+
});
|
|
5113
|
+
saveJson(CONFIG_FILE, { site, email });
|
|
5114
|
+
console.log("Successfully authenticated with Jira.");
|
|
5115
|
+
} catch (error) {
|
|
5116
|
+
if (error instanceof Error) {
|
|
5117
|
+
console.error("Error authenticating with Jira:", error.message);
|
|
5118
|
+
}
|
|
5119
|
+
process.exit(1);
|
|
5120
|
+
}
|
|
5121
|
+
}
|
|
5122
|
+
|
|
5123
|
+
// src/commands/jira/viewIssue.ts
|
|
5124
|
+
import chalk58 from "chalk";
|
|
5125
|
+
function viewIssue(issueKey) {
|
|
5126
|
+
const parsed = fetchIssue(issueKey, "summary,description");
|
|
5127
|
+
const fields = parsed?.fields;
|
|
5128
|
+
const summary = fields?.summary;
|
|
5129
|
+
const description = fields?.description;
|
|
5130
|
+
if (summary) {
|
|
5131
|
+
console.log(chalk58.bold(summary));
|
|
5132
|
+
}
|
|
5133
|
+
if (description) {
|
|
5134
|
+
if (summary) console.log();
|
|
5135
|
+
if (typeof description === "string") {
|
|
5136
|
+
console.log(description);
|
|
5137
|
+
} else if (description.type === "doc") {
|
|
5138
|
+
console.log(adfToText(description));
|
|
5139
|
+
} else {
|
|
5140
|
+
console.log(JSON.stringify(description, null, 2));
|
|
5141
|
+
}
|
|
5142
|
+
}
|
|
5143
|
+
if (!summary && !description) {
|
|
5144
|
+
console.log(
|
|
5145
|
+
chalk58.yellow(`No summary or description found on ${issueKey}.`)
|
|
5146
|
+
);
|
|
5147
|
+
}
|
|
5148
|
+
}
|
|
5149
|
+
|
|
5150
|
+
// src/commands/registerJira.ts
|
|
5151
|
+
function registerJira(program2) {
|
|
5152
|
+
const jiraCommand = program2.command("jira").description("Jira utilities");
|
|
5153
|
+
jiraCommand.command("auth").description("Authenticate with Jira via API token").action(() => jiraAuth());
|
|
5154
|
+
jiraCommand.command("ac <issue-key>").description("Print acceptance criteria for a Jira issue").action((issueKey) => acceptanceCriteria(issueKey));
|
|
5155
|
+
jiraCommand.command("view <issue-key>").description("Print the title and description of a Jira issue").action((issueKey) => viewIssue(issueKey));
|
|
5156
|
+
}
|
|
5157
|
+
|
|
5158
|
+
// src/commands/news/add/index.ts
|
|
5159
|
+
import chalk59 from "chalk";
|
|
5160
|
+
import enquirer5 from "enquirer";
|
|
5161
|
+
async function add2(url) {
|
|
5162
|
+
if (!url) {
|
|
5163
|
+
const response = await enquirer5.prompt({
|
|
5164
|
+
type: "input",
|
|
4873
5165
|
name: "url",
|
|
4874
5166
|
message: "RSS feed URL:",
|
|
4875
5167
|
validate: (value) => {
|
|
@@ -4887,17 +5179,17 @@ async function add2(url) {
|
|
|
4887
5179
|
const news = config.news ?? {};
|
|
4888
5180
|
const feeds = news.feeds ?? [];
|
|
4889
5181
|
if (feeds.includes(url)) {
|
|
4890
|
-
console.log(
|
|
5182
|
+
console.log(chalk59.yellow("Feed already exists in config"));
|
|
4891
5183
|
return;
|
|
4892
5184
|
}
|
|
4893
5185
|
feeds.push(url);
|
|
4894
5186
|
config.news = { ...news, feeds };
|
|
4895
5187
|
saveGlobalConfig(config);
|
|
4896
|
-
console.log(
|
|
5188
|
+
console.log(chalk59.green(`Added feed: ${url}`));
|
|
4897
5189
|
}
|
|
4898
5190
|
|
|
4899
5191
|
// src/commands/news/web/handleRequest.ts
|
|
4900
|
-
import
|
|
5192
|
+
import chalk60 from "chalk";
|
|
4901
5193
|
|
|
4902
5194
|
// src/commands/news/web/shared.ts
|
|
4903
5195
|
import { decodeHTML } from "entities";
|
|
@@ -5033,17 +5325,17 @@ function prefetch() {
|
|
|
5033
5325
|
const config = loadConfig();
|
|
5034
5326
|
const total = config.news.feeds.length;
|
|
5035
5327
|
if (total === 0) return;
|
|
5036
|
-
process.stdout.write(
|
|
5328
|
+
process.stdout.write(chalk60.dim(`Fetching ${total} feed(s)\u2026 `));
|
|
5037
5329
|
prefetchPromise = fetchFeeds(config.news.feeds, (done2, t) => {
|
|
5038
5330
|
const width = 20;
|
|
5039
5331
|
const filled = Math.round(done2 / t * width);
|
|
5040
5332
|
const bar = `${"\u2588".repeat(filled)}${"\u2591".repeat(width - filled)}`;
|
|
5041
5333
|
process.stdout.write(
|
|
5042
|
-
`\r${
|
|
5334
|
+
`\r${chalk60.dim(`Fetching feeds ${bar} ${done2}/${t}`)}`
|
|
5043
5335
|
);
|
|
5044
5336
|
}).then((items) => {
|
|
5045
5337
|
process.stdout.write(
|
|
5046
|
-
`\r${
|
|
5338
|
+
`\r${chalk60.green(`Fetched ${items.length} items from ${total} feed(s)`)}
|
|
5047
5339
|
`
|
|
5048
5340
|
);
|
|
5049
5341
|
cachedItems = items;
|
|
@@ -5089,12 +5381,12 @@ function registerNews(program2) {
|
|
|
5089
5381
|
|
|
5090
5382
|
// src/commands/prs/comment.ts
|
|
5091
5383
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
5092
|
-
import { unlinkSync as
|
|
5093
|
-
import { tmpdir as
|
|
5094
|
-
import { join as
|
|
5384
|
+
import { unlinkSync as unlinkSync4, writeFileSync as writeFileSync18 } from "fs";
|
|
5385
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
5386
|
+
import { join as join19 } from "path";
|
|
5095
5387
|
|
|
5096
5388
|
// src/commands/prs/shared.ts
|
|
5097
|
-
import { execSync as
|
|
5389
|
+
import { execSync as execSync24 } from "child_process";
|
|
5098
5390
|
function isGhNotInstalled(error) {
|
|
5099
5391
|
if (error instanceof Error) {
|
|
5100
5392
|
const msg = error.message.toLowerCase();
|
|
@@ -5110,14 +5402,14 @@ function isNotFound(error) {
|
|
|
5110
5402
|
}
|
|
5111
5403
|
function getRepoInfo() {
|
|
5112
5404
|
const repoInfo = JSON.parse(
|
|
5113
|
-
|
|
5405
|
+
execSync24("gh repo view --json owner,name", { encoding: "utf-8" })
|
|
5114
5406
|
);
|
|
5115
5407
|
return { org: repoInfo.owner.login, repo: repoInfo.name };
|
|
5116
5408
|
}
|
|
5117
5409
|
function getCurrentPrNumber() {
|
|
5118
5410
|
try {
|
|
5119
5411
|
const prInfo = JSON.parse(
|
|
5120
|
-
|
|
5412
|
+
execSync24("gh pr view --json number", { encoding: "utf-8" })
|
|
5121
5413
|
);
|
|
5122
5414
|
return prInfo.number;
|
|
5123
5415
|
} catch (error) {
|
|
@@ -5131,7 +5423,7 @@ function getCurrentPrNumber() {
|
|
|
5131
5423
|
function getCurrentPrNodeId() {
|
|
5132
5424
|
try {
|
|
5133
5425
|
const prInfo = JSON.parse(
|
|
5134
|
-
|
|
5426
|
+
execSync24("gh pr view --json id", { encoding: "utf-8" })
|
|
5135
5427
|
);
|
|
5136
5428
|
return prInfo.id;
|
|
5137
5429
|
} catch (error) {
|
|
@@ -5158,12 +5450,12 @@ function validateLine(line) {
|
|
|
5158
5450
|
process.exit(1);
|
|
5159
5451
|
}
|
|
5160
5452
|
}
|
|
5161
|
-
function comment(
|
|
5453
|
+
function comment(path44, line, body) {
|
|
5162
5454
|
validateBody(body);
|
|
5163
5455
|
validateLine(line);
|
|
5164
5456
|
try {
|
|
5165
5457
|
const prId = getCurrentPrNodeId();
|
|
5166
|
-
const queryFile =
|
|
5458
|
+
const queryFile = join19(tmpdir3(), `gh-query-${Date.now()}.graphql`);
|
|
5167
5459
|
writeFileSync18(queryFile, MUTATION);
|
|
5168
5460
|
try {
|
|
5169
5461
|
const result = spawnSync2(
|
|
@@ -5178,7 +5470,7 @@ function comment(path42, line, body) {
|
|
|
5178
5470
|
"-f",
|
|
5179
5471
|
`body=${body}`,
|
|
5180
5472
|
"-f",
|
|
5181
|
-
`path=${
|
|
5473
|
+
`path=${path44}`,
|
|
5182
5474
|
"-F",
|
|
5183
5475
|
`line=${line}`
|
|
5184
5476
|
],
|
|
@@ -5187,9 +5479,9 @@ function comment(path42, line, body) {
|
|
|
5187
5479
|
if (result.status !== 0) {
|
|
5188
5480
|
throw new Error(result.stderr || result.stdout);
|
|
5189
5481
|
}
|
|
5190
|
-
console.log(`Added review comment on ${
|
|
5482
|
+
console.log(`Added review comment on ${path44}:${line}`);
|
|
5191
5483
|
} finally {
|
|
5192
|
-
|
|
5484
|
+
unlinkSync4(queryFile);
|
|
5193
5485
|
}
|
|
5194
5486
|
} catch (error) {
|
|
5195
5487
|
if (isGhNotInstalled(error)) {
|
|
@@ -5202,55 +5494,55 @@ function comment(path42, line, body) {
|
|
|
5202
5494
|
}
|
|
5203
5495
|
|
|
5204
5496
|
// src/commands/prs/fixed.ts
|
|
5205
|
-
import { execSync as
|
|
5497
|
+
import { execSync as execSync26 } from "child_process";
|
|
5206
5498
|
|
|
5207
5499
|
// src/commands/prs/resolveCommentWithReply.ts
|
|
5208
|
-
import { execSync as
|
|
5209
|
-
import { unlinkSync as
|
|
5210
|
-
import { tmpdir as
|
|
5211
|
-
import { join as
|
|
5500
|
+
import { execSync as execSync25 } from "child_process";
|
|
5501
|
+
import { unlinkSync as unlinkSync6, writeFileSync as writeFileSync19 } from "fs";
|
|
5502
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
5503
|
+
import { join as join21 } from "path";
|
|
5212
5504
|
|
|
5213
5505
|
// src/commands/prs/loadCommentsCache.ts
|
|
5214
|
-
import { existsSync as
|
|
5215
|
-
import { join as
|
|
5506
|
+
import { existsSync as existsSync25, readFileSync as readFileSync22, unlinkSync as unlinkSync5 } from "fs";
|
|
5507
|
+
import { join as join20 } from "path";
|
|
5216
5508
|
import { parse as parse2 } from "yaml";
|
|
5217
5509
|
function getCachePath(prNumber) {
|
|
5218
|
-
return
|
|
5510
|
+
return join20(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`);
|
|
5219
5511
|
}
|
|
5220
5512
|
function loadCommentsCache(prNumber) {
|
|
5221
5513
|
const cachePath = getCachePath(prNumber);
|
|
5222
|
-
if (!
|
|
5514
|
+
if (!existsSync25(cachePath)) {
|
|
5223
5515
|
return null;
|
|
5224
5516
|
}
|
|
5225
|
-
const content =
|
|
5517
|
+
const content = readFileSync22(cachePath, "utf-8");
|
|
5226
5518
|
return parse2(content);
|
|
5227
5519
|
}
|
|
5228
5520
|
function deleteCommentsCache(prNumber) {
|
|
5229
5521
|
const cachePath = getCachePath(prNumber);
|
|
5230
|
-
if (
|
|
5231
|
-
|
|
5522
|
+
if (existsSync25(cachePath)) {
|
|
5523
|
+
unlinkSync5(cachePath);
|
|
5232
5524
|
console.log("No more unresolved line comments. Cache dropped.");
|
|
5233
5525
|
}
|
|
5234
5526
|
}
|
|
5235
5527
|
|
|
5236
5528
|
// src/commands/prs/resolveCommentWithReply.ts
|
|
5237
5529
|
function replyToComment(org, repo, prNumber, commentId, message) {
|
|
5238
|
-
|
|
5530
|
+
execSync25(
|
|
5239
5531
|
`gh api repos/${org}/${repo}/pulls/${prNumber}/comments -f body="${message.replace(/"/g, '\\"')}" -F in_reply_to=${commentId}`,
|
|
5240
5532
|
{ stdio: ["inherit", "pipe", "inherit"] }
|
|
5241
5533
|
);
|
|
5242
5534
|
}
|
|
5243
5535
|
function resolveThread(threadId) {
|
|
5244
5536
|
const mutation = `mutation($threadId: ID!) { resolveReviewThread(input: {threadId: $threadId}) { thread { isResolved } } }`;
|
|
5245
|
-
const queryFile =
|
|
5537
|
+
const queryFile = join21(tmpdir4(), `gh-mutation-${Date.now()}.graphql`);
|
|
5246
5538
|
writeFileSync19(queryFile, mutation);
|
|
5247
5539
|
try {
|
|
5248
|
-
|
|
5540
|
+
execSync25(
|
|
5249
5541
|
`gh api graphql -F query=@${queryFile} -f threadId="${threadId}"`,
|
|
5250
5542
|
{ stdio: ["inherit", "pipe", "inherit"] }
|
|
5251
5543
|
);
|
|
5252
5544
|
} finally {
|
|
5253
|
-
|
|
5545
|
+
unlinkSync6(queryFile);
|
|
5254
5546
|
}
|
|
5255
5547
|
}
|
|
5256
5548
|
function requireCache(prNumber) {
|
|
@@ -5297,7 +5589,7 @@ function resolveCommentWithReply(commentId, message) {
|
|
|
5297
5589
|
// src/commands/prs/fixed.ts
|
|
5298
5590
|
function verifySha(sha) {
|
|
5299
5591
|
try {
|
|
5300
|
-
return
|
|
5592
|
+
return execSync26(`git rev-parse --verify ${sha}`, {
|
|
5301
5593
|
encoding: "utf-8"
|
|
5302
5594
|
}).trim();
|
|
5303
5595
|
} catch {
|
|
@@ -5311,7 +5603,7 @@ function fixed(commentId, sha) {
|
|
|
5311
5603
|
const { org, repo } = getRepoInfo();
|
|
5312
5604
|
const repoUrl = `https://github.com/${org}/${repo}`;
|
|
5313
5605
|
const message = `Fixed in [${fullSha}](${repoUrl}/commit/${fullSha})`;
|
|
5314
|
-
|
|
5606
|
+
execSync26("git push", { stdio: "inherit" });
|
|
5315
5607
|
resolveCommentWithReply(commentId, message);
|
|
5316
5608
|
} catch (error) {
|
|
5317
5609
|
if (isGhNotInstalled(error)) {
|
|
@@ -5324,21 +5616,21 @@ function fixed(commentId, sha) {
|
|
|
5324
5616
|
}
|
|
5325
5617
|
|
|
5326
5618
|
// src/commands/prs/listComments/index.ts
|
|
5327
|
-
import { existsSync as
|
|
5328
|
-
import { join as
|
|
5619
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync6, writeFileSync as writeFileSync21 } from "fs";
|
|
5620
|
+
import { join as join23 } from "path";
|
|
5329
5621
|
import { stringify } from "yaml";
|
|
5330
5622
|
|
|
5331
5623
|
// src/commands/prs/fetchThreadIds.ts
|
|
5332
|
-
import { execSync as
|
|
5333
|
-
import { unlinkSync as
|
|
5334
|
-
import { tmpdir as
|
|
5335
|
-
import { join as
|
|
5624
|
+
import { execSync as execSync27 } from "child_process";
|
|
5625
|
+
import { unlinkSync as unlinkSync7, writeFileSync as writeFileSync20 } from "fs";
|
|
5626
|
+
import { tmpdir as tmpdir5 } from "os";
|
|
5627
|
+
import { join as join22 } from "path";
|
|
5336
5628
|
var THREAD_QUERY = `query($owner: String!, $repo: String!, $prNumber: Int!) { repository(owner: $owner, name: $repo) { pullRequest(number: $prNumber) { reviewThreads(first: 100) { nodes { id isResolved comments(first: 100) { nodes { databaseId } } } } } } }`;
|
|
5337
5629
|
function fetchThreadIds(org, repo, prNumber) {
|
|
5338
|
-
const queryFile =
|
|
5630
|
+
const queryFile = join22(tmpdir5(), `gh-query-${Date.now()}.graphql`);
|
|
5339
5631
|
writeFileSync20(queryFile, THREAD_QUERY);
|
|
5340
5632
|
try {
|
|
5341
|
-
const result =
|
|
5633
|
+
const result = execSync27(
|
|
5342
5634
|
`gh api graphql -F query=@${queryFile} -F owner="${org}" -F repo="${repo}" -F prNumber=${prNumber}`,
|
|
5343
5635
|
{ encoding: "utf-8" }
|
|
5344
5636
|
);
|
|
@@ -5355,14 +5647,14 @@ function fetchThreadIds(org, repo, prNumber) {
|
|
|
5355
5647
|
}
|
|
5356
5648
|
return { threadMap, resolvedThreadIds };
|
|
5357
5649
|
} finally {
|
|
5358
|
-
|
|
5650
|
+
unlinkSync7(queryFile);
|
|
5359
5651
|
}
|
|
5360
5652
|
}
|
|
5361
5653
|
|
|
5362
5654
|
// src/commands/prs/listComments/fetchReviewComments.ts
|
|
5363
|
-
import { execSync as
|
|
5655
|
+
import { execSync as execSync28 } from "child_process";
|
|
5364
5656
|
function fetchJson(endpoint) {
|
|
5365
|
-
const result =
|
|
5657
|
+
const result = execSync28(`gh api --paginate ${endpoint}`, {
|
|
5366
5658
|
encoding: "utf-8"
|
|
5367
5659
|
});
|
|
5368
5660
|
if (!result.trim()) return [];
|
|
@@ -5404,20 +5696,20 @@ function fetchLineComments(org, repo, prNumber, threadInfo) {
|
|
|
5404
5696
|
}
|
|
5405
5697
|
|
|
5406
5698
|
// src/commands/prs/listComments/printComments.ts
|
|
5407
|
-
import
|
|
5699
|
+
import chalk61 from "chalk";
|
|
5408
5700
|
function formatForHuman(comment2) {
|
|
5409
5701
|
if (comment2.type === "review") {
|
|
5410
|
-
const stateColor = comment2.state === "APPROVED" ?
|
|
5702
|
+
const stateColor = comment2.state === "APPROVED" ? chalk61.green : comment2.state === "CHANGES_REQUESTED" ? chalk61.red : chalk61.yellow;
|
|
5411
5703
|
return [
|
|
5412
|
-
`${
|
|
5704
|
+
`${chalk61.cyan("Review")} by ${chalk61.bold(comment2.user)} ${stateColor(`[${comment2.state}]`)}`,
|
|
5413
5705
|
comment2.body,
|
|
5414
5706
|
""
|
|
5415
5707
|
].join("\n");
|
|
5416
5708
|
}
|
|
5417
5709
|
const location = comment2.line ? `:${comment2.line}` : "";
|
|
5418
5710
|
return [
|
|
5419
|
-
`${
|
|
5420
|
-
|
|
5711
|
+
`${chalk61.cyan("Line comment")} by ${chalk61.bold(comment2.user)} on ${chalk61.dim(`${comment2.path}${location}`)}`,
|
|
5712
|
+
chalk61.dim(comment2.diff_hunk.split("\n").slice(-3).join("\n")),
|
|
5421
5713
|
comment2.body,
|
|
5422
5714
|
""
|
|
5423
5715
|
].join("\n");
|
|
@@ -5449,8 +5741,8 @@ function printComments(result) {
|
|
|
5449
5741
|
|
|
5450
5742
|
// src/commands/prs/listComments/index.ts
|
|
5451
5743
|
function writeCommentsCache(prNumber, comments) {
|
|
5452
|
-
const assistDir =
|
|
5453
|
-
if (!
|
|
5744
|
+
const assistDir = join23(process.cwd(), ".assist");
|
|
5745
|
+
if (!existsSync26(assistDir)) {
|
|
5454
5746
|
mkdirSync6(assistDir, { recursive: true });
|
|
5455
5747
|
}
|
|
5456
5748
|
const cacheData = {
|
|
@@ -5458,7 +5750,7 @@ function writeCommentsCache(prNumber, comments) {
|
|
|
5458
5750
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5459
5751
|
comments
|
|
5460
5752
|
};
|
|
5461
|
-
const cachePath =
|
|
5753
|
+
const cachePath = join23(assistDir, `pr-${prNumber}-comments.yaml`);
|
|
5462
5754
|
writeFileSync21(cachePath, stringify(cacheData));
|
|
5463
5755
|
}
|
|
5464
5756
|
function handleKnownErrors(error) {
|
|
@@ -5491,7 +5783,7 @@ async function listComments() {
|
|
|
5491
5783
|
];
|
|
5492
5784
|
updateCache(prNumber, allComments);
|
|
5493
5785
|
const hasLineComments = allComments.some((c) => c.type === "line");
|
|
5494
|
-
const cachePath = hasLineComments ?
|
|
5786
|
+
const cachePath = hasLineComments ? join23(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`) : null;
|
|
5495
5787
|
return { comments: allComments, cachePath };
|
|
5496
5788
|
} catch (error) {
|
|
5497
5789
|
const handled = handleKnownErrors(error);
|
|
@@ -5501,19 +5793,19 @@ async function listComments() {
|
|
|
5501
5793
|
}
|
|
5502
5794
|
|
|
5503
5795
|
// src/commands/prs/prs/index.ts
|
|
5504
|
-
import { execSync as
|
|
5796
|
+
import { execSync as execSync29 } from "child_process";
|
|
5505
5797
|
|
|
5506
5798
|
// src/commands/prs/prs/displayPaginated/index.ts
|
|
5507
5799
|
import enquirer6 from "enquirer";
|
|
5508
5800
|
|
|
5509
5801
|
// src/commands/prs/prs/displayPaginated/printPr.ts
|
|
5510
|
-
import
|
|
5802
|
+
import chalk62 from "chalk";
|
|
5511
5803
|
var STATUS_MAP = {
|
|
5512
|
-
MERGED: (pr) => pr.mergedAt ? { label:
|
|
5513
|
-
CLOSED: (pr) => pr.closedAt ? { label:
|
|
5804
|
+
MERGED: (pr) => pr.mergedAt ? { label: chalk62.magenta("merged"), date: pr.mergedAt } : null,
|
|
5805
|
+
CLOSED: (pr) => pr.closedAt ? { label: chalk62.red("closed"), date: pr.closedAt } : null
|
|
5514
5806
|
};
|
|
5515
5807
|
function defaultStatus(pr) {
|
|
5516
|
-
return { label:
|
|
5808
|
+
return { label: chalk62.green("opened"), date: pr.createdAt };
|
|
5517
5809
|
}
|
|
5518
5810
|
function getStatus2(pr) {
|
|
5519
5811
|
return STATUS_MAP[pr.state]?.(pr) ?? defaultStatus(pr);
|
|
@@ -5522,11 +5814,11 @@ function formatDate(dateStr) {
|
|
|
5522
5814
|
return new Date(dateStr).toISOString().split("T")[0];
|
|
5523
5815
|
}
|
|
5524
5816
|
function formatPrHeader(pr, status2) {
|
|
5525
|
-
return `${
|
|
5817
|
+
return `${chalk62.cyan(`#${pr.number}`)} ${pr.title} ${chalk62.dim(`(${pr.author.login},`)} ${status2.label} ${chalk62.dim(`${formatDate(status2.date)})`)}`;
|
|
5526
5818
|
}
|
|
5527
5819
|
function logPrDetails(pr) {
|
|
5528
5820
|
console.log(
|
|
5529
|
-
|
|
5821
|
+
chalk62.dim(` ${pr.changedFiles.toLocaleString()} files | ${pr.url}`)
|
|
5530
5822
|
);
|
|
5531
5823
|
console.log();
|
|
5532
5824
|
}
|
|
@@ -5607,7 +5899,7 @@ async function displayPaginated(pullRequests) {
|
|
|
5607
5899
|
async function prs(options2) {
|
|
5608
5900
|
const state = options2.open ? "open" : options2.closed ? "closed" : "all";
|
|
5609
5901
|
try {
|
|
5610
|
-
const result =
|
|
5902
|
+
const result = execSync29(
|
|
5611
5903
|
`gh pr list --state ${state} --json number,title,url,author,createdAt,mergedAt,closedAt,state,changedFiles --limit 100`,
|
|
5612
5904
|
{ encoding: "utf-8" }
|
|
5613
5905
|
);
|
|
@@ -5630,7 +5922,7 @@ async function prs(options2) {
|
|
|
5630
5922
|
}
|
|
5631
5923
|
|
|
5632
5924
|
// src/commands/prs/wontfix.ts
|
|
5633
|
-
import { execSync as
|
|
5925
|
+
import { execSync as execSync30 } from "child_process";
|
|
5634
5926
|
function validateReason(reason) {
|
|
5635
5927
|
const lowerReason = reason.toLowerCase();
|
|
5636
5928
|
if (lowerReason.includes("claude") || lowerReason.includes("opus")) {
|
|
@@ -5647,7 +5939,7 @@ function validateShaReferences(reason) {
|
|
|
5647
5939
|
const invalidShas = [];
|
|
5648
5940
|
for (const sha of shas) {
|
|
5649
5941
|
try {
|
|
5650
|
-
|
|
5942
|
+
execSync30(`git cat-file -t ${sha}`, { stdio: "pipe" });
|
|
5651
5943
|
} catch {
|
|
5652
5944
|
invalidShas.push(sha);
|
|
5653
5945
|
}
|
|
@@ -5686,13 +5978,13 @@ function registerPrs(program2) {
|
|
|
5686
5978
|
prsCommand.command("wontfix <comment-id> <reason>").description("Reply with reason and resolve thread").action((commentId, reason) => {
|
|
5687
5979
|
wontfix(Number.parseInt(commentId, 10), reason);
|
|
5688
5980
|
});
|
|
5689
|
-
prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((
|
|
5690
|
-
comment(
|
|
5981
|
+
prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((path44, line, body) => {
|
|
5982
|
+
comment(path44, Number.parseInt(line, 10), body);
|
|
5691
5983
|
});
|
|
5692
5984
|
}
|
|
5693
5985
|
|
|
5694
5986
|
// src/commands/ravendb/ravendbAuth.ts
|
|
5695
|
-
import
|
|
5987
|
+
import chalk67 from "chalk";
|
|
5696
5988
|
|
|
5697
5989
|
// src/commands/ravendb/loadConnections.ts
|
|
5698
5990
|
function loadConnections() {
|
|
@@ -5709,18 +6001,18 @@ function saveConnections(connections) {
|
|
|
5709
6001
|
}
|
|
5710
6002
|
|
|
5711
6003
|
// src/commands/ravendb/promptConnection.ts
|
|
5712
|
-
import
|
|
6004
|
+
import chalk65 from "chalk";
|
|
5713
6005
|
import Enquirer3 from "enquirer";
|
|
5714
6006
|
|
|
5715
6007
|
// src/commands/ravendb/selectOpSecret.ts
|
|
5716
|
-
import
|
|
6008
|
+
import chalk64 from "chalk";
|
|
5717
6009
|
import Enquirer2 from "enquirer";
|
|
5718
6010
|
|
|
5719
6011
|
// src/commands/ravendb/searchItems.ts
|
|
5720
|
-
import { execSync as
|
|
5721
|
-
import
|
|
6012
|
+
import { execSync as execSync31 } from "child_process";
|
|
6013
|
+
import chalk63 from "chalk";
|
|
5722
6014
|
function opExec(args) {
|
|
5723
|
-
return
|
|
6015
|
+
return execSync31(`op ${args}`, {
|
|
5724
6016
|
encoding: "utf-8",
|
|
5725
6017
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5726
6018
|
}).trim();
|
|
@@ -5731,7 +6023,7 @@ function searchItems(search) {
|
|
|
5731
6023
|
items = JSON.parse(opExec("item list --format=json"));
|
|
5732
6024
|
} catch {
|
|
5733
6025
|
console.error(
|
|
5734
|
-
|
|
6026
|
+
chalk63.red(
|
|
5735
6027
|
"Failed to search 1Password. Ensure the CLI is installed and you are signed in."
|
|
5736
6028
|
)
|
|
5737
6029
|
);
|
|
@@ -5745,7 +6037,7 @@ function getItemFields(itemId) {
|
|
|
5745
6037
|
const item = JSON.parse(opExec(`item get "${itemId}" --format=json`));
|
|
5746
6038
|
return item.fields.filter((f) => f.reference && f.label);
|
|
5747
6039
|
} catch {
|
|
5748
|
-
console.error(
|
|
6040
|
+
console.error(chalk63.red("Failed to get item details from 1Password."));
|
|
5749
6041
|
process.exit(1);
|
|
5750
6042
|
}
|
|
5751
6043
|
}
|
|
@@ -5764,7 +6056,7 @@ async function selectOpSecret(searchTerm) {
|
|
|
5764
6056
|
}).run();
|
|
5765
6057
|
const items = searchItems(search);
|
|
5766
6058
|
if (items.length === 0) {
|
|
5767
|
-
console.error(
|
|
6059
|
+
console.error(chalk64.red(`No items found matching "${search}".`));
|
|
5768
6060
|
process.exit(1);
|
|
5769
6061
|
}
|
|
5770
6062
|
const itemId = await selectOne(
|
|
@@ -5773,7 +6065,7 @@ async function selectOpSecret(searchTerm) {
|
|
|
5773
6065
|
);
|
|
5774
6066
|
const fields = getItemFields(itemId);
|
|
5775
6067
|
if (fields.length === 0) {
|
|
5776
|
-
console.error(
|
|
6068
|
+
console.error(chalk64.red("No fields with references found on this item."));
|
|
5777
6069
|
process.exit(1);
|
|
5778
6070
|
}
|
|
5779
6071
|
const ref = await selectOne(
|
|
@@ -5791,7 +6083,7 @@ async function promptConnection(existingNames) {
|
|
|
5791
6083
|
message: "Connection name:"
|
|
5792
6084
|
}).run();
|
|
5793
6085
|
if (existingNames.includes(name)) {
|
|
5794
|
-
console.error(
|
|
6086
|
+
console.error(chalk65.red(`Connection "${name}" already exists.`));
|
|
5795
6087
|
process.exit(1);
|
|
5796
6088
|
}
|
|
5797
6089
|
const url = await new Input2({
|
|
@@ -5803,22 +6095,22 @@ async function promptConnection(existingNames) {
|
|
|
5803
6095
|
message: "Database name:"
|
|
5804
6096
|
}).run();
|
|
5805
6097
|
if (!name || !url || !database) {
|
|
5806
|
-
console.error(
|
|
6098
|
+
console.error(chalk65.red("All fields are required."));
|
|
5807
6099
|
process.exit(1);
|
|
5808
6100
|
}
|
|
5809
6101
|
const apiKeyRef = await selectOpSecret();
|
|
5810
|
-
console.log(
|
|
6102
|
+
console.log(chalk65.dim(`Using: ${apiKeyRef}`));
|
|
5811
6103
|
return { name, url, database, apiKeyRef };
|
|
5812
6104
|
}
|
|
5813
6105
|
|
|
5814
6106
|
// src/commands/ravendb/ravendbSetConnection.ts
|
|
5815
|
-
import
|
|
6107
|
+
import chalk66 from "chalk";
|
|
5816
6108
|
function ravendbSetConnection(name) {
|
|
5817
6109
|
const raw = loadGlobalConfigRaw();
|
|
5818
6110
|
const ravendb = raw.ravendb ?? {};
|
|
5819
6111
|
const connections = ravendb.connections ?? [];
|
|
5820
6112
|
if (!connections.some((c) => c.name === name)) {
|
|
5821
|
-
console.error(
|
|
6113
|
+
console.error(chalk66.red(`Connection "${name}" not found.`));
|
|
5822
6114
|
console.error(
|
|
5823
6115
|
`Available: ${connections.map((c) => c.name).join(", ") || "(none)"}`
|
|
5824
6116
|
);
|
|
@@ -5840,7 +6132,7 @@ async function ravendbAuth(options2) {
|
|
|
5840
6132
|
}
|
|
5841
6133
|
for (const c of connections) {
|
|
5842
6134
|
console.log(
|
|
5843
|
-
`${
|
|
6135
|
+
`${chalk67.bold(c.name)} ${c.url} db=${c.database} key=${c.apiKeyRef}`
|
|
5844
6136
|
);
|
|
5845
6137
|
}
|
|
5846
6138
|
return;
|
|
@@ -5848,7 +6140,7 @@ async function ravendbAuth(options2) {
|
|
|
5848
6140
|
if (options2.remove) {
|
|
5849
6141
|
const filtered = connections.filter((c) => c.name !== options2.remove);
|
|
5850
6142
|
if (filtered.length === connections.length) {
|
|
5851
|
-
console.error(
|
|
6143
|
+
console.error(chalk67.red(`Connection "${options2.remove}" not found.`));
|
|
5852
6144
|
process.exit(1);
|
|
5853
6145
|
}
|
|
5854
6146
|
saveConnections(filtered);
|
|
@@ -5866,10 +6158,10 @@ async function ravendbAuth(options2) {
|
|
|
5866
6158
|
}
|
|
5867
6159
|
|
|
5868
6160
|
// src/commands/ravendb/ravendbCollections.ts
|
|
5869
|
-
import
|
|
6161
|
+
import chalk71 from "chalk";
|
|
5870
6162
|
|
|
5871
6163
|
// src/commands/ravendb/ravenFetch.ts
|
|
5872
|
-
import
|
|
6164
|
+
import chalk69 from "chalk";
|
|
5873
6165
|
|
|
5874
6166
|
// src/commands/ravendb/getAccessToken.ts
|
|
5875
6167
|
var OAUTH_URL = "https://amazon-useast-1-oauth.ravenhq.com/ApiKeys/OAuth/AccessToken";
|
|
@@ -5905,21 +6197,21 @@ ${errorText}`
|
|
|
5905
6197
|
}
|
|
5906
6198
|
|
|
5907
6199
|
// src/commands/ravendb/resolveOpSecret.ts
|
|
5908
|
-
import { execSync as
|
|
5909
|
-
import
|
|
6200
|
+
import { execSync as execSync32 } from "child_process";
|
|
6201
|
+
import chalk68 from "chalk";
|
|
5910
6202
|
function resolveOpSecret(reference) {
|
|
5911
6203
|
if (!reference.startsWith("op://")) {
|
|
5912
|
-
console.error(
|
|
6204
|
+
console.error(chalk68.red(`Invalid secret reference: must start with op://`));
|
|
5913
6205
|
process.exit(1);
|
|
5914
6206
|
}
|
|
5915
6207
|
try {
|
|
5916
|
-
return
|
|
6208
|
+
return execSync32(`op read "${reference}"`, {
|
|
5917
6209
|
encoding: "utf-8",
|
|
5918
6210
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5919
6211
|
}).trim();
|
|
5920
6212
|
} catch {
|
|
5921
6213
|
console.error(
|
|
5922
|
-
|
|
6214
|
+
chalk68.red(
|
|
5923
6215
|
"Failed to resolve secret reference. Ensure 1Password CLI is installed and you are signed in."
|
|
5924
6216
|
)
|
|
5925
6217
|
);
|
|
@@ -5928,10 +6220,10 @@ function resolveOpSecret(reference) {
|
|
|
5928
6220
|
}
|
|
5929
6221
|
|
|
5930
6222
|
// src/commands/ravendb/ravenFetch.ts
|
|
5931
|
-
async function ravenFetch(connection,
|
|
6223
|
+
async function ravenFetch(connection, path44) {
|
|
5932
6224
|
const apiKey = resolveOpSecret(connection.apiKeyRef);
|
|
5933
6225
|
let accessToken = await getAccessToken(apiKey);
|
|
5934
|
-
const url = `${connection.url}${
|
|
6226
|
+
const url = `${connection.url}${path44}`;
|
|
5935
6227
|
const headers = {
|
|
5936
6228
|
Authorization: `Bearer ${accessToken}`,
|
|
5937
6229
|
"Content-Type": "application/json"
|
|
@@ -5946,7 +6238,7 @@ async function ravenFetch(connection, path42) {
|
|
|
5946
6238
|
if (!response.ok) {
|
|
5947
6239
|
const body = await response.text();
|
|
5948
6240
|
console.error(
|
|
5949
|
-
|
|
6241
|
+
chalk69.red(`RavenDB error: ${response.status} ${response.statusText}`)
|
|
5950
6242
|
);
|
|
5951
6243
|
console.error(body.substring(0, 500));
|
|
5952
6244
|
process.exit(1);
|
|
@@ -5955,7 +6247,7 @@ async function ravenFetch(connection, path42) {
|
|
|
5955
6247
|
}
|
|
5956
6248
|
|
|
5957
6249
|
// src/commands/ravendb/resolveConnection.ts
|
|
5958
|
-
import
|
|
6250
|
+
import chalk70 from "chalk";
|
|
5959
6251
|
function loadRavendb() {
|
|
5960
6252
|
const raw = loadGlobalConfigRaw();
|
|
5961
6253
|
const ravendb = raw.ravendb;
|
|
@@ -5969,7 +6261,7 @@ function resolveConnection(name) {
|
|
|
5969
6261
|
const connectionName = name ?? defaultConnection;
|
|
5970
6262
|
if (!connectionName) {
|
|
5971
6263
|
console.error(
|
|
5972
|
-
|
|
6264
|
+
chalk70.red(
|
|
5973
6265
|
"No connection specified and no default set. Use assist ravendb set-connection <name> or pass a connection name."
|
|
5974
6266
|
)
|
|
5975
6267
|
);
|
|
@@ -5977,7 +6269,7 @@ function resolveConnection(name) {
|
|
|
5977
6269
|
}
|
|
5978
6270
|
const connection = connections.find((c) => c.name === connectionName);
|
|
5979
6271
|
if (!connection) {
|
|
5980
|
-
console.error(
|
|
6272
|
+
console.error(chalk70.red(`Connection "${connectionName}" not found.`));
|
|
5981
6273
|
console.error(
|
|
5982
6274
|
`Available: ${connections.map((c) => c.name).join(", ") || "(none)"}`
|
|
5983
6275
|
);
|
|
@@ -6008,29 +6300,29 @@ async function ravendbCollections(connectionName) {
|
|
|
6008
6300
|
return;
|
|
6009
6301
|
}
|
|
6010
6302
|
for (const c of collections) {
|
|
6011
|
-
console.log(`${
|
|
6303
|
+
console.log(`${chalk71.bold(c.Name)} ${c.CountOfDocuments} docs`);
|
|
6012
6304
|
}
|
|
6013
6305
|
}
|
|
6014
6306
|
|
|
6015
6307
|
// src/commands/ravendb/ravendbQuery.ts
|
|
6016
|
-
import
|
|
6308
|
+
import chalk73 from "chalk";
|
|
6017
6309
|
|
|
6018
6310
|
// src/commands/ravendb/fetchAllPages.ts
|
|
6019
|
-
import
|
|
6311
|
+
import chalk72 from "chalk";
|
|
6020
6312
|
|
|
6021
6313
|
// src/commands/ravendb/buildQueryPath.ts
|
|
6022
6314
|
function buildQueryPath(opts) {
|
|
6023
6315
|
const db = encodeURIComponent(opts.db);
|
|
6024
|
-
let
|
|
6316
|
+
let path44;
|
|
6025
6317
|
if (opts.collection) {
|
|
6026
|
-
|
|
6318
|
+
path44 = `/databases/${db}/indexes/dynamic/${encodeURIComponent(opts.collection)}?start=${opts.start}&pageSize=${opts.pageSize}&sort=${encodeURIComponent(opts.sort)}`;
|
|
6027
6319
|
} else {
|
|
6028
|
-
|
|
6320
|
+
path44 = `/databases/${db}/queries?start=${opts.start}&pageSize=${opts.pageSize}`;
|
|
6029
6321
|
}
|
|
6030
6322
|
if (opts.query) {
|
|
6031
|
-
|
|
6323
|
+
path44 += `&query=${encodeURIComponent(opts.query)}`;
|
|
6032
6324
|
}
|
|
6033
|
-
return
|
|
6325
|
+
return path44;
|
|
6034
6326
|
}
|
|
6035
6327
|
|
|
6036
6328
|
// src/commands/ravendb/fetchAllPages.ts
|
|
@@ -6039,7 +6331,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
6039
6331
|
let start3 = 0;
|
|
6040
6332
|
while (true) {
|
|
6041
6333
|
const effectivePageSize = opts.limit !== void 0 ? Math.min(opts.pageSize, opts.limit - allResults.length) : opts.pageSize;
|
|
6042
|
-
const
|
|
6334
|
+
const path44 = buildQueryPath({
|
|
6043
6335
|
db: connection.database,
|
|
6044
6336
|
collection: opts.collection,
|
|
6045
6337
|
start: start3,
|
|
@@ -6047,14 +6339,14 @@ async function fetchAllPages(connection, opts) {
|
|
|
6047
6339
|
sort: opts.sort,
|
|
6048
6340
|
query: opts.query
|
|
6049
6341
|
});
|
|
6050
|
-
const data = await ravenFetch(connection,
|
|
6342
|
+
const data = await ravenFetch(connection, path44);
|
|
6051
6343
|
const results = data.Results ?? [];
|
|
6052
6344
|
const totalResults = data.TotalResults ?? 0;
|
|
6053
6345
|
if (results.length === 0) break;
|
|
6054
6346
|
allResults.push(...results);
|
|
6055
6347
|
start3 += results.length;
|
|
6056
6348
|
process.stderr.write(
|
|
6057
|
-
`\r${
|
|
6349
|
+
`\r${chalk72.dim(`Fetched ${allResults.length}/${totalResults}`)}`
|
|
6058
6350
|
);
|
|
6059
6351
|
if (start3 >= totalResults) break;
|
|
6060
6352
|
if (opts.limit !== void 0 && allResults.length >= opts.limit) break;
|
|
@@ -6069,7 +6361,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
6069
6361
|
async function ravendbQuery(connectionName, collection, options2) {
|
|
6070
6362
|
const resolved = resolveArgs(connectionName, collection);
|
|
6071
6363
|
if (!resolved.collection && !options2.query) {
|
|
6072
|
-
console.error(
|
|
6364
|
+
console.error(chalk73.red("Provide a collection name or --query filter."));
|
|
6073
6365
|
process.exit(1);
|
|
6074
6366
|
}
|
|
6075
6367
|
const { collection: col } = resolved;
|
|
@@ -6101,10 +6393,10 @@ function registerRavendb(program2) {
|
|
|
6101
6393
|
|
|
6102
6394
|
// src/commands/refactor/check/index.ts
|
|
6103
6395
|
import { spawn as spawn3 } from "child_process";
|
|
6104
|
-
import * as
|
|
6396
|
+
import * as path27 from "path";
|
|
6105
6397
|
|
|
6106
6398
|
// src/commands/refactor/logViolations.ts
|
|
6107
|
-
import
|
|
6399
|
+
import chalk74 from "chalk";
|
|
6108
6400
|
var DEFAULT_MAX_LINES = 100;
|
|
6109
6401
|
function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
|
|
6110
6402
|
if (violations.length === 0) {
|
|
@@ -6113,43 +6405,43 @@ function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
|
|
|
6113
6405
|
}
|
|
6114
6406
|
return;
|
|
6115
6407
|
}
|
|
6116
|
-
console.error(
|
|
6408
|
+
console.error(chalk74.red(`
|
|
6117
6409
|
Refactor check failed:
|
|
6118
6410
|
`));
|
|
6119
|
-
console.error(
|
|
6411
|
+
console.error(chalk74.red(` The following files exceed ${maxLines} lines:
|
|
6120
6412
|
`));
|
|
6121
6413
|
for (const violation of violations) {
|
|
6122
|
-
console.error(
|
|
6414
|
+
console.error(chalk74.red(` ${violation.file} (${violation.lines} lines)`));
|
|
6123
6415
|
}
|
|
6124
6416
|
console.error(
|
|
6125
|
-
|
|
6417
|
+
chalk74.yellow(
|
|
6126
6418
|
`
|
|
6127
6419
|
Each file needs to be sensibly refactored, or if there is no sensible
|
|
6128
6420
|
way to refactor it, ignore it with:
|
|
6129
6421
|
`
|
|
6130
6422
|
)
|
|
6131
6423
|
);
|
|
6132
|
-
console.error(
|
|
6424
|
+
console.error(chalk74.gray(` assist refactor ignore <file>
|
|
6133
6425
|
`));
|
|
6134
6426
|
if (process.env.CLAUDECODE) {
|
|
6135
|
-
console.error(
|
|
6427
|
+
console.error(chalk74.cyan(`
|
|
6136
6428
|
## Extracting Code to New Files
|
|
6137
6429
|
`));
|
|
6138
6430
|
console.error(
|
|
6139
|
-
|
|
6431
|
+
chalk74.cyan(
|
|
6140
6432
|
` When extracting logic from one file to another, consider where the extracted code belongs:
|
|
6141
6433
|
`
|
|
6142
6434
|
)
|
|
6143
6435
|
);
|
|
6144
6436
|
console.error(
|
|
6145
|
-
|
|
6437
|
+
chalk74.cyan(
|
|
6146
6438
|
` 1. Keep related logic together: If the extracted code is tightly coupled to the
|
|
6147
6439
|
original file's domain, create a new folder containing both the original and extracted files.
|
|
6148
6440
|
`
|
|
6149
6441
|
)
|
|
6150
6442
|
);
|
|
6151
6443
|
console.error(
|
|
6152
|
-
|
|
6444
|
+
chalk74.cyan(
|
|
6153
6445
|
` 2. Share common utilities: If the extracted code can be reused across multiple
|
|
6154
6446
|
domains, move it to a common/shared folder.
|
|
6155
6447
|
`
|
|
@@ -6159,7 +6451,7 @@ Refactor check failed:
|
|
|
6159
6451
|
}
|
|
6160
6452
|
|
|
6161
6453
|
// src/commands/refactor/check/getViolations/index.ts
|
|
6162
|
-
import { execSync as
|
|
6454
|
+
import { execSync as execSync33 } from "child_process";
|
|
6163
6455
|
import fs16 from "fs";
|
|
6164
6456
|
import { minimatch as minimatch4 } from "minimatch";
|
|
6165
6457
|
|
|
@@ -6209,7 +6501,7 @@ function getGitFiles(options2) {
|
|
|
6209
6501
|
}
|
|
6210
6502
|
const files = /* @__PURE__ */ new Set();
|
|
6211
6503
|
if (options2.staged || options2.modified) {
|
|
6212
|
-
const staged =
|
|
6504
|
+
const staged = execSync33("git diff --cached --name-only", {
|
|
6213
6505
|
encoding: "utf-8"
|
|
6214
6506
|
});
|
|
6215
6507
|
for (const file of staged.trim().split("\n").filter(Boolean)) {
|
|
@@ -6217,7 +6509,7 @@ function getGitFiles(options2) {
|
|
|
6217
6509
|
}
|
|
6218
6510
|
}
|
|
6219
6511
|
if (options2.unstaged || options2.modified) {
|
|
6220
|
-
const unstaged =
|
|
6512
|
+
const unstaged = execSync33("git diff --name-only", { encoding: "utf-8" });
|
|
6221
6513
|
for (const file of unstaged.trim().split("\n").filter(Boolean)) {
|
|
6222
6514
|
files.add(file);
|
|
6223
6515
|
}
|
|
@@ -6278,7 +6570,7 @@ ${failed.length} verify script(s) failed:`);
|
|
|
6278
6570
|
async function runVerifyQuietly() {
|
|
6279
6571
|
const result = findPackageJsonWithVerifyScripts(process.cwd());
|
|
6280
6572
|
if (!result) return true;
|
|
6281
|
-
const packageDir =
|
|
6573
|
+
const packageDir = path27.dirname(result.packageJsonPath);
|
|
6282
6574
|
const results = await Promise.all(
|
|
6283
6575
|
result.verifyScripts.map((script) => runScript(script, packageDir))
|
|
6284
6576
|
);
|
|
@@ -6305,11 +6597,11 @@ async function check(pattern2, options2) {
|
|
|
6305
6597
|
|
|
6306
6598
|
// src/commands/refactor/ignore.ts
|
|
6307
6599
|
import fs17 from "fs";
|
|
6308
|
-
import
|
|
6600
|
+
import chalk75 from "chalk";
|
|
6309
6601
|
var REFACTOR_YML_PATH2 = "refactor.yml";
|
|
6310
6602
|
function ignore(file) {
|
|
6311
6603
|
if (!fs17.existsSync(file)) {
|
|
6312
|
-
console.error(
|
|
6604
|
+
console.error(chalk75.red(`Error: File does not exist: ${file}`));
|
|
6313
6605
|
process.exit(1);
|
|
6314
6606
|
}
|
|
6315
6607
|
const content = fs17.readFileSync(file, "utf-8");
|
|
@@ -6325,43 +6617,43 @@ function ignore(file) {
|
|
|
6325
6617
|
fs17.writeFileSync(REFACTOR_YML_PATH2, entry);
|
|
6326
6618
|
}
|
|
6327
6619
|
console.log(
|
|
6328
|
-
|
|
6620
|
+
chalk75.green(
|
|
6329
6621
|
`Added ${file} to refactor ignore list (max ${maxLines} lines)`
|
|
6330
6622
|
)
|
|
6331
6623
|
);
|
|
6332
6624
|
}
|
|
6333
6625
|
|
|
6334
6626
|
// src/commands/refactor/rename/index.ts
|
|
6335
|
-
import
|
|
6336
|
-
import
|
|
6627
|
+
import path28 from "path";
|
|
6628
|
+
import chalk76 from "chalk";
|
|
6337
6629
|
import { Project as Project2 } from "ts-morph";
|
|
6338
6630
|
async function rename(source, destination, options2 = {}) {
|
|
6339
|
-
const sourcePath =
|
|
6340
|
-
const destPath =
|
|
6631
|
+
const sourcePath = path28.resolve(source);
|
|
6632
|
+
const destPath = path28.resolve(destination);
|
|
6341
6633
|
const cwd = process.cwd();
|
|
6342
|
-
const relSource =
|
|
6343
|
-
const relDest =
|
|
6634
|
+
const relSource = path28.relative(cwd, sourcePath);
|
|
6635
|
+
const relDest = path28.relative(cwd, destPath);
|
|
6344
6636
|
const project = new Project2({
|
|
6345
|
-
tsConfigFilePath:
|
|
6637
|
+
tsConfigFilePath: path28.resolve("tsconfig.json")
|
|
6346
6638
|
});
|
|
6347
6639
|
const sourceFile = project.getSourceFile(sourcePath);
|
|
6348
6640
|
if (!sourceFile) {
|
|
6349
|
-
console.log(
|
|
6641
|
+
console.log(chalk76.red(`File not found in project: ${source}`));
|
|
6350
6642
|
process.exit(1);
|
|
6351
6643
|
}
|
|
6352
|
-
console.log(
|
|
6644
|
+
console.log(chalk76.bold(`Rename: ${relSource} \u2192 ${relDest}`));
|
|
6353
6645
|
if (options2.apply) {
|
|
6354
6646
|
sourceFile.move(destPath);
|
|
6355
6647
|
await project.save();
|
|
6356
|
-
console.log(
|
|
6648
|
+
console.log(chalk76.green("Done"));
|
|
6357
6649
|
} else {
|
|
6358
|
-
console.log(
|
|
6650
|
+
console.log(chalk76.dim("Dry run. Use --apply to execute."));
|
|
6359
6651
|
}
|
|
6360
6652
|
}
|
|
6361
6653
|
|
|
6362
6654
|
// src/commands/refactor/renameSymbol/index.ts
|
|
6363
|
-
import
|
|
6364
|
-
import
|
|
6655
|
+
import path30 from "path";
|
|
6656
|
+
import chalk77 from "chalk";
|
|
6365
6657
|
import { Project as Project3 } from "ts-morph";
|
|
6366
6658
|
|
|
6367
6659
|
// src/commands/refactor/renameSymbol/findSymbol.ts
|
|
@@ -6389,12 +6681,12 @@ function findSymbol(sourceFile, symbolName) {
|
|
|
6389
6681
|
}
|
|
6390
6682
|
|
|
6391
6683
|
// src/commands/refactor/renameSymbol/groupReferences.ts
|
|
6392
|
-
import
|
|
6684
|
+
import path29 from "path";
|
|
6393
6685
|
function groupReferences(symbol, cwd) {
|
|
6394
6686
|
const refs = symbol.findReferencesAsNodes();
|
|
6395
6687
|
const grouped = /* @__PURE__ */ new Map();
|
|
6396
6688
|
for (const ref of refs) {
|
|
6397
|
-
const refFile =
|
|
6689
|
+
const refFile = path29.relative(cwd, ref.getSourceFile().getFilePath());
|
|
6398
6690
|
const lines = grouped.get(refFile) ?? [];
|
|
6399
6691
|
if (!grouped.has(refFile)) grouped.set(refFile, lines);
|
|
6400
6692
|
lines.push(ref.getStartLineNumber());
|
|
@@ -6404,47 +6696,47 @@ function groupReferences(symbol, cwd) {
|
|
|
6404
6696
|
|
|
6405
6697
|
// src/commands/refactor/renameSymbol/index.ts
|
|
6406
6698
|
async function renameSymbol(file, oldName, newName, options2 = {}) {
|
|
6407
|
-
const filePath =
|
|
6408
|
-
const tsConfigPath =
|
|
6699
|
+
const filePath = path30.resolve(file);
|
|
6700
|
+
const tsConfigPath = path30.resolve("tsconfig.json");
|
|
6409
6701
|
const cwd = process.cwd();
|
|
6410
6702
|
const project = new Project3({ tsConfigFilePath: tsConfigPath });
|
|
6411
6703
|
const sourceFile = project.getSourceFile(filePath);
|
|
6412
6704
|
if (!sourceFile) {
|
|
6413
|
-
console.log(
|
|
6705
|
+
console.log(chalk77.red(`File not found in project: ${file}`));
|
|
6414
6706
|
process.exit(1);
|
|
6415
6707
|
}
|
|
6416
6708
|
const symbol = findSymbol(sourceFile, oldName);
|
|
6417
6709
|
if (!symbol) {
|
|
6418
|
-
console.log(
|
|
6710
|
+
console.log(chalk77.red(`Symbol "${oldName}" not found in ${file}`));
|
|
6419
6711
|
process.exit(1);
|
|
6420
6712
|
}
|
|
6421
6713
|
const grouped = groupReferences(symbol, cwd);
|
|
6422
6714
|
const totalRefs = [...grouped.values()].reduce((s, l) => s + l.length, 0);
|
|
6423
6715
|
console.log(
|
|
6424
|
-
|
|
6716
|
+
chalk77.bold(`Rename: ${oldName} \u2192 ${newName} (${totalRefs} references)
|
|
6425
6717
|
`)
|
|
6426
6718
|
);
|
|
6427
6719
|
for (const [refFile, lines] of grouped) {
|
|
6428
6720
|
console.log(
|
|
6429
|
-
` ${
|
|
6721
|
+
` ${chalk77.dim(refFile)}: lines ${chalk77.cyan(lines.join(", "))}`
|
|
6430
6722
|
);
|
|
6431
6723
|
}
|
|
6432
6724
|
if (options2.apply) {
|
|
6433
6725
|
symbol.rename(newName);
|
|
6434
6726
|
await project.save();
|
|
6435
|
-
console.log(
|
|
6727
|
+
console.log(chalk77.green(`
|
|
6436
6728
|
Renamed ${oldName} \u2192 ${newName}`));
|
|
6437
6729
|
} else {
|
|
6438
|
-
console.log(
|
|
6730
|
+
console.log(chalk77.dim("\nDry run. Use --apply to execute."));
|
|
6439
6731
|
}
|
|
6440
6732
|
}
|
|
6441
6733
|
|
|
6442
6734
|
// src/commands/refactor/restructure/index.ts
|
|
6443
|
-
import
|
|
6444
|
-
import
|
|
6735
|
+
import path39 from "path";
|
|
6736
|
+
import chalk80 from "chalk";
|
|
6445
6737
|
|
|
6446
6738
|
// src/commands/refactor/restructure/buildImportGraph/index.ts
|
|
6447
|
-
import
|
|
6739
|
+
import path31 from "path";
|
|
6448
6740
|
import ts7 from "typescript";
|
|
6449
6741
|
|
|
6450
6742
|
// src/commands/refactor/restructure/buildImportGraph/getImportSpecifiers.ts
|
|
@@ -6471,7 +6763,7 @@ function loadParsedConfig(tsConfigPath) {
|
|
|
6471
6763
|
return ts7.parseJsonConfigFileContent(
|
|
6472
6764
|
configFile.config,
|
|
6473
6765
|
ts7.sys,
|
|
6474
|
-
|
|
6766
|
+
path31.dirname(tsConfigPath)
|
|
6475
6767
|
);
|
|
6476
6768
|
}
|
|
6477
6769
|
function addToSetMap(map, key, value) {
|
|
@@ -6487,7 +6779,7 @@ function resolveImport(specifier, filePath, options2) {
|
|
|
6487
6779
|
const resolved = ts7.resolveModuleName(specifier, filePath, options2, ts7.sys);
|
|
6488
6780
|
const resolvedPath = resolved.resolvedModule?.resolvedFileName;
|
|
6489
6781
|
if (!resolvedPath || resolvedPath.includes("node_modules")) return null;
|
|
6490
|
-
return
|
|
6782
|
+
return path31.resolve(resolvedPath);
|
|
6491
6783
|
}
|
|
6492
6784
|
function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
6493
6785
|
const parsed = loadParsedConfig(tsConfigPath);
|
|
@@ -6496,7 +6788,7 @@ function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
|
6496
6788
|
const importedBy = /* @__PURE__ */ new Map();
|
|
6497
6789
|
const imports = /* @__PURE__ */ new Map();
|
|
6498
6790
|
for (const sourceFile of program2.getSourceFiles()) {
|
|
6499
|
-
const filePath =
|
|
6791
|
+
const filePath = path31.resolve(sourceFile.fileName);
|
|
6500
6792
|
if (filePath.includes("node_modules")) continue;
|
|
6501
6793
|
for (const specifier of getImportSpecifiers(sourceFile)) {
|
|
6502
6794
|
const absTarget = resolveImport(specifier, filePath, parsed.options);
|
|
@@ -6510,12 +6802,12 @@ function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
|
6510
6802
|
}
|
|
6511
6803
|
|
|
6512
6804
|
// src/commands/refactor/restructure/clusterDirectories.ts
|
|
6513
|
-
import
|
|
6805
|
+
import path32 from "path";
|
|
6514
6806
|
function clusterDirectories(graph) {
|
|
6515
6807
|
const dirImportedBy = /* @__PURE__ */ new Map();
|
|
6516
6808
|
for (const edge of graph.edges) {
|
|
6517
|
-
const sourceDir =
|
|
6518
|
-
const targetDir =
|
|
6809
|
+
const sourceDir = path32.dirname(edge.source);
|
|
6810
|
+
const targetDir = path32.dirname(edge.target);
|
|
6519
6811
|
if (sourceDir === targetDir) continue;
|
|
6520
6812
|
if (!graph.files.has(edge.target)) continue;
|
|
6521
6813
|
const existing = dirImportedBy.get(targetDir) ?? /* @__PURE__ */ new Set();
|
|
@@ -6543,20 +6835,20 @@ function clusterDirectories(graph) {
|
|
|
6543
6835
|
return clusters;
|
|
6544
6836
|
}
|
|
6545
6837
|
function isAncestor(ancestor, descendant) {
|
|
6546
|
-
const rel =
|
|
6838
|
+
const rel = path32.relative(ancestor, descendant);
|
|
6547
6839
|
return !rel.startsWith("..") && rel !== "";
|
|
6548
6840
|
}
|
|
6549
6841
|
|
|
6550
6842
|
// src/commands/refactor/restructure/clusterFiles.ts
|
|
6551
|
-
import
|
|
6843
|
+
import path33 from "path";
|
|
6552
6844
|
function findRootParent(file, importedBy, visited) {
|
|
6553
6845
|
const importers = importedBy.get(file);
|
|
6554
6846
|
if (!importers || importers.size !== 1) return file;
|
|
6555
6847
|
const parent = [...importers][0];
|
|
6556
|
-
const parentDir =
|
|
6557
|
-
const fileDir =
|
|
6848
|
+
const parentDir = path33.dirname(parent);
|
|
6849
|
+
const fileDir = path33.dirname(file);
|
|
6558
6850
|
if (parentDir !== fileDir) return file;
|
|
6559
|
-
if (
|
|
6851
|
+
if (path33.basename(parent, path33.extname(parent)) === "index") return file;
|
|
6560
6852
|
if (visited.has(parent)) return file;
|
|
6561
6853
|
visited.add(parent);
|
|
6562
6854
|
return findRootParent(parent, importedBy, visited);
|
|
@@ -6564,16 +6856,16 @@ function findRootParent(file, importedBy, visited) {
|
|
|
6564
6856
|
function clusterFiles(graph) {
|
|
6565
6857
|
const clusters = /* @__PURE__ */ new Map();
|
|
6566
6858
|
for (const file of graph.files) {
|
|
6567
|
-
const basename7 =
|
|
6859
|
+
const basename7 = path33.basename(file, path33.extname(file));
|
|
6568
6860
|
if (basename7 === "index") continue;
|
|
6569
6861
|
const importers = graph.importedBy.get(file);
|
|
6570
6862
|
if (!importers || importers.size !== 1) continue;
|
|
6571
6863
|
const parent = [...importers][0];
|
|
6572
6864
|
if (!graph.files.has(parent)) continue;
|
|
6573
|
-
const parentDir =
|
|
6574
|
-
const fileDir =
|
|
6865
|
+
const parentDir = path33.dirname(parent);
|
|
6866
|
+
const fileDir = path33.dirname(file);
|
|
6575
6867
|
if (parentDir !== fileDir) continue;
|
|
6576
|
-
const parentBasename =
|
|
6868
|
+
const parentBasename = path33.basename(parent, path33.extname(parent));
|
|
6577
6869
|
if (parentBasename === "index") continue;
|
|
6578
6870
|
const root = findRootParent(parent, graph.importedBy, /* @__PURE__ */ new Set([file]));
|
|
6579
6871
|
if (!root || root === file) continue;
|
|
@@ -6585,7 +6877,7 @@ function clusterFiles(graph) {
|
|
|
6585
6877
|
}
|
|
6586
6878
|
|
|
6587
6879
|
// src/commands/refactor/restructure/computeRewrites/index.ts
|
|
6588
|
-
import
|
|
6880
|
+
import path34 from "path";
|
|
6589
6881
|
|
|
6590
6882
|
// src/commands/refactor/restructure/computeRewrites/applyRewrites.ts
|
|
6591
6883
|
import fs18 from "fs";
|
|
@@ -6594,7 +6886,7 @@ function getOrCreateList(map, key) {
|
|
|
6594
6886
|
if (!map.has(key)) map.set(key, list4);
|
|
6595
6887
|
return list4;
|
|
6596
6888
|
}
|
|
6597
|
-
function
|
|
6889
|
+
function groupByFile2(rewrites) {
|
|
6598
6890
|
const grouped = /* @__PURE__ */ new Map();
|
|
6599
6891
|
for (const rewrite of rewrites) {
|
|
6600
6892
|
getOrCreateList(grouped, rewrite.file).push(rewrite);
|
|
@@ -6615,7 +6907,7 @@ function applyFileRewrites(file, fileRewrites) {
|
|
|
6615
6907
|
}
|
|
6616
6908
|
function applyRewrites(rewrites) {
|
|
6617
6909
|
const updatedContents = /* @__PURE__ */ new Map();
|
|
6618
|
-
for (const [file, fileRewrites] of
|
|
6910
|
+
for (const [file, fileRewrites] of groupByFile2(rewrites)) {
|
|
6619
6911
|
updatedContents.set(file, applyFileRewrites(file, fileRewrites));
|
|
6620
6912
|
}
|
|
6621
6913
|
return updatedContents;
|
|
@@ -6639,7 +6931,7 @@ function normalizeSpecifier(rel) {
|
|
|
6639
6931
|
);
|
|
6640
6932
|
}
|
|
6641
6933
|
function computeSpecifier(fromFile, toFile) {
|
|
6642
|
-
return normalizeSpecifier(
|
|
6934
|
+
return normalizeSpecifier(path34.relative(path34.dirname(fromFile), toFile));
|
|
6643
6935
|
}
|
|
6644
6936
|
function isAffected(edge, moveMap) {
|
|
6645
6937
|
return moveMap.has(edge.target) || moveMap.has(edge.source);
|
|
@@ -6683,51 +6975,51 @@ function computeRewrites(moves, edges, allProjectFiles) {
|
|
|
6683
6975
|
}
|
|
6684
6976
|
|
|
6685
6977
|
// src/commands/refactor/restructure/displayPlan.ts
|
|
6686
|
-
import
|
|
6687
|
-
import
|
|
6978
|
+
import path35 from "path";
|
|
6979
|
+
import chalk78 from "chalk";
|
|
6688
6980
|
function relPath(filePath) {
|
|
6689
|
-
return
|
|
6981
|
+
return path35.relative(process.cwd(), filePath);
|
|
6690
6982
|
}
|
|
6691
6983
|
function displayMoves(plan) {
|
|
6692
6984
|
if (plan.moves.length === 0) return;
|
|
6693
|
-
console.log(
|
|
6985
|
+
console.log(chalk78.bold("\nFile moves:"));
|
|
6694
6986
|
for (const move of plan.moves) {
|
|
6695
6987
|
console.log(
|
|
6696
|
-
` ${
|
|
6988
|
+
` ${chalk78.red(relPath(move.from))} \u2192 ${chalk78.green(relPath(move.to))}`
|
|
6697
6989
|
);
|
|
6698
|
-
console.log(
|
|
6990
|
+
console.log(chalk78.dim(` ${move.reason}`));
|
|
6699
6991
|
}
|
|
6700
6992
|
}
|
|
6701
6993
|
function displayRewrites(rewrites) {
|
|
6702
6994
|
if (rewrites.length === 0) return;
|
|
6703
6995
|
const affectedFiles = new Set(rewrites.map((r) => r.file));
|
|
6704
|
-
console.log(
|
|
6996
|
+
console.log(chalk78.bold(`
|
|
6705
6997
|
Import rewrites (${affectedFiles.size} files):`));
|
|
6706
6998
|
for (const file of affectedFiles) {
|
|
6707
|
-
console.log(` ${
|
|
6999
|
+
console.log(` ${chalk78.cyan(relPath(file))}:`);
|
|
6708
7000
|
for (const { oldSpecifier, newSpecifier } of rewrites.filter(
|
|
6709
7001
|
(r) => r.file === file
|
|
6710
7002
|
)) {
|
|
6711
7003
|
console.log(
|
|
6712
|
-
` ${
|
|
7004
|
+
` ${chalk78.red(`"${oldSpecifier}"`)} \u2192 ${chalk78.green(`"${newSpecifier}"`)}`
|
|
6713
7005
|
);
|
|
6714
7006
|
}
|
|
6715
7007
|
}
|
|
6716
7008
|
}
|
|
6717
7009
|
function displayPlan(plan) {
|
|
6718
7010
|
if (plan.warnings.length > 0) {
|
|
6719
|
-
console.log(
|
|
6720
|
-
for (const w of plan.warnings) console.log(
|
|
7011
|
+
console.log(chalk78.yellow("\nWarnings:"));
|
|
7012
|
+
for (const w of plan.warnings) console.log(chalk78.yellow(` ${w}`));
|
|
6721
7013
|
}
|
|
6722
7014
|
if (plan.newDirectories.length > 0) {
|
|
6723
|
-
console.log(
|
|
7015
|
+
console.log(chalk78.bold("\nNew directories:"));
|
|
6724
7016
|
for (const dir of plan.newDirectories)
|
|
6725
|
-
console.log(
|
|
7017
|
+
console.log(chalk78.green(` ${dir}/`));
|
|
6726
7018
|
}
|
|
6727
7019
|
displayMoves(plan);
|
|
6728
7020
|
displayRewrites(plan.rewrites);
|
|
6729
7021
|
console.log(
|
|
6730
|
-
|
|
7022
|
+
chalk78.dim(
|
|
6731
7023
|
`
|
|
6732
7024
|
Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rewritten`
|
|
6733
7025
|
)
|
|
@@ -6736,33 +7028,33 @@ Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rew
|
|
|
6736
7028
|
|
|
6737
7029
|
// src/commands/refactor/restructure/executePlan.ts
|
|
6738
7030
|
import fs19 from "fs";
|
|
6739
|
-
import
|
|
6740
|
-
import
|
|
7031
|
+
import path36 from "path";
|
|
7032
|
+
import chalk79 from "chalk";
|
|
6741
7033
|
function executePlan(plan) {
|
|
6742
7034
|
const updatedContents = applyRewrites(plan.rewrites);
|
|
6743
7035
|
for (const [file, content] of updatedContents) {
|
|
6744
7036
|
fs19.writeFileSync(file, content, "utf-8");
|
|
6745
7037
|
console.log(
|
|
6746
|
-
|
|
7038
|
+
chalk79.cyan(` Rewrote imports in ${path36.relative(process.cwd(), file)}`)
|
|
6747
7039
|
);
|
|
6748
7040
|
}
|
|
6749
7041
|
for (const dir of plan.newDirectories) {
|
|
6750
7042
|
fs19.mkdirSync(dir, { recursive: true });
|
|
6751
|
-
console.log(
|
|
7043
|
+
console.log(chalk79.green(` Created ${path36.relative(process.cwd(), dir)}/`));
|
|
6752
7044
|
}
|
|
6753
7045
|
for (const move of plan.moves) {
|
|
6754
|
-
const targetDir =
|
|
7046
|
+
const targetDir = path36.dirname(move.to);
|
|
6755
7047
|
if (!fs19.existsSync(targetDir)) {
|
|
6756
7048
|
fs19.mkdirSync(targetDir, { recursive: true });
|
|
6757
7049
|
}
|
|
6758
7050
|
fs19.renameSync(move.from, move.to);
|
|
6759
7051
|
console.log(
|
|
6760
|
-
|
|
6761
|
-
` Moved ${
|
|
7052
|
+
chalk79.white(
|
|
7053
|
+
` Moved ${path36.relative(process.cwd(), move.from)} \u2192 ${path36.relative(process.cwd(), move.to)}`
|
|
6762
7054
|
)
|
|
6763
7055
|
);
|
|
6764
7056
|
}
|
|
6765
|
-
removeEmptyDirectories(plan.moves.map((m) =>
|
|
7057
|
+
removeEmptyDirectories(plan.moves.map((m) => path36.dirname(m.from)));
|
|
6766
7058
|
}
|
|
6767
7059
|
function removeEmptyDirectories(dirs) {
|
|
6768
7060
|
const unique = [...new Set(dirs)];
|
|
@@ -6772,8 +7064,8 @@ function removeEmptyDirectories(dirs) {
|
|
|
6772
7064
|
if (entries.length === 0) {
|
|
6773
7065
|
fs19.rmdirSync(dir);
|
|
6774
7066
|
console.log(
|
|
6775
|
-
|
|
6776
|
-
` Removed empty directory ${
|
|
7067
|
+
chalk79.dim(
|
|
7068
|
+
` Removed empty directory ${path36.relative(process.cwd(), dir)}`
|
|
6777
7069
|
)
|
|
6778
7070
|
);
|
|
6779
7071
|
}
|
|
@@ -6781,7 +7073,7 @@ function removeEmptyDirectories(dirs) {
|
|
|
6781
7073
|
}
|
|
6782
7074
|
|
|
6783
7075
|
// src/commands/refactor/restructure/planFileMoves/index.ts
|
|
6784
|
-
import
|
|
7076
|
+
import path38 from "path";
|
|
6785
7077
|
|
|
6786
7078
|
// src/commands/refactor/restructure/planFileMoves/shared.ts
|
|
6787
7079
|
import fs20 from "fs";
|
|
@@ -6796,9 +7088,9 @@ function checkDirConflict(result, label2, dir) {
|
|
|
6796
7088
|
|
|
6797
7089
|
// src/commands/refactor/restructure/planFileMoves/planDirectoryMoves.ts
|
|
6798
7090
|
import fs21 from "fs";
|
|
6799
|
-
import
|
|
7091
|
+
import path37 from "path";
|
|
6800
7092
|
function collectEntry(results, dir, entry) {
|
|
6801
|
-
const full =
|
|
7093
|
+
const full = path37.join(dir, entry.name);
|
|
6802
7094
|
const items = entry.isDirectory() ? listFilesRecursive(full) : [full];
|
|
6803
7095
|
results.push(...items);
|
|
6804
7096
|
}
|
|
@@ -6812,15 +7104,15 @@ function listFilesRecursive(dir) {
|
|
|
6812
7104
|
}
|
|
6813
7105
|
function addDirectoryFileMoves(moves, childDir, newLocation, reason) {
|
|
6814
7106
|
for (const file of listFilesRecursive(childDir)) {
|
|
6815
|
-
const rel =
|
|
6816
|
-
moves.push({ from: file, to:
|
|
7107
|
+
const rel = path37.relative(childDir, file);
|
|
7108
|
+
moves.push({ from: file, to: path37.join(newLocation, rel), reason });
|
|
6817
7109
|
}
|
|
6818
7110
|
}
|
|
6819
7111
|
function resolveChildDest(parentDir, childDir) {
|
|
6820
|
-
return
|
|
7112
|
+
return path37.join(parentDir, path37.basename(childDir));
|
|
6821
7113
|
}
|
|
6822
7114
|
function childMoveReason(parentDir) {
|
|
6823
|
-
return `Directory only imported from ${
|
|
7115
|
+
return `Directory only imported from ${path37.basename(parentDir)}/`;
|
|
6824
7116
|
}
|
|
6825
7117
|
function registerDirectoryMove(result, childDir, dest, parentDir) {
|
|
6826
7118
|
result.directories.push(dest);
|
|
@@ -6845,7 +7137,7 @@ function planDirectoryMoves(clusters) {
|
|
|
6845
7137
|
|
|
6846
7138
|
// src/commands/refactor/restructure/planFileMoves/index.ts
|
|
6847
7139
|
function childMoveData(child, newDir, parentBase) {
|
|
6848
|
-
const to =
|
|
7140
|
+
const to = path38.join(newDir, path38.basename(child));
|
|
6849
7141
|
return { from: child, to, reason: `Only imported by ${parentBase}` };
|
|
6850
7142
|
}
|
|
6851
7143
|
function addChildMoves(moves, children, newDir, parentBase) {
|
|
@@ -6853,15 +7145,15 @@ function addChildMoves(moves, children, newDir, parentBase) {
|
|
|
6853
7145
|
moves.push(childMoveData(child, newDir, parentBase));
|
|
6854
7146
|
}
|
|
6855
7147
|
function getBaseName(filePath) {
|
|
6856
|
-
return
|
|
7148
|
+
return path38.basename(filePath, path38.extname(filePath));
|
|
6857
7149
|
}
|
|
6858
7150
|
function resolveClusterDir(parent) {
|
|
6859
|
-
return
|
|
7151
|
+
return path38.join(path38.dirname(parent), getBaseName(parent));
|
|
6860
7152
|
}
|
|
6861
7153
|
function createParentMove(parent, newDir) {
|
|
6862
7154
|
return {
|
|
6863
7155
|
from: parent,
|
|
6864
|
-
to:
|
|
7156
|
+
to: path38.join(newDir, `index${path38.extname(parent)}`),
|
|
6865
7157
|
reason: `Main module of new ${getBaseName(parent)}/ directory`
|
|
6866
7158
|
};
|
|
6867
7159
|
}
|
|
@@ -6885,7 +7177,7 @@ function planFileMoves(clusters) {
|
|
|
6885
7177
|
|
|
6886
7178
|
// src/commands/refactor/restructure/index.ts
|
|
6887
7179
|
function buildPlan(candidateFiles, tsConfigPath) {
|
|
6888
|
-
const candidates = new Set(candidateFiles.map((f) =>
|
|
7180
|
+
const candidates = new Set(candidateFiles.map((f) => path39.resolve(f)));
|
|
6889
7181
|
const graph = buildImportGraph(candidates, tsConfigPath);
|
|
6890
7182
|
const allProjectFiles = /* @__PURE__ */ new Set([
|
|
6891
7183
|
...graph.importedBy.keys(),
|
|
@@ -6905,22 +7197,22 @@ async function restructure(pattern2, options2 = {}) {
|
|
|
6905
7197
|
const targetPattern = pattern2 ?? "src";
|
|
6906
7198
|
const files = findSourceFiles2(targetPattern);
|
|
6907
7199
|
if (files.length === 0) {
|
|
6908
|
-
console.log(
|
|
7200
|
+
console.log(chalk80.yellow("No files found matching pattern"));
|
|
6909
7201
|
return;
|
|
6910
7202
|
}
|
|
6911
|
-
const tsConfigPath =
|
|
7203
|
+
const tsConfigPath = path39.resolve("tsconfig.json");
|
|
6912
7204
|
const plan = buildPlan(files, tsConfigPath);
|
|
6913
7205
|
if (plan.moves.length === 0) {
|
|
6914
|
-
console.log(
|
|
7206
|
+
console.log(chalk80.green("No restructuring needed"));
|
|
6915
7207
|
return;
|
|
6916
7208
|
}
|
|
6917
7209
|
displayPlan(plan);
|
|
6918
7210
|
if (options2.apply) {
|
|
6919
|
-
console.log(
|
|
7211
|
+
console.log(chalk80.bold("\nApplying changes..."));
|
|
6920
7212
|
executePlan(plan);
|
|
6921
|
-
console.log(
|
|
7213
|
+
console.log(chalk80.green("\nRestructuring complete"));
|
|
6922
7214
|
} else {
|
|
6923
|
-
console.log(
|
|
7215
|
+
console.log(chalk80.dim("\nDry run. Use --apply to execute."));
|
|
6924
7216
|
}
|
|
6925
7217
|
}
|
|
6926
7218
|
|
|
@@ -6948,8 +7240,8 @@ function registerRefactor(program2) {
|
|
|
6948
7240
|
}
|
|
6949
7241
|
|
|
6950
7242
|
// src/commands/transcript/shared.ts
|
|
6951
|
-
import { existsSync as
|
|
6952
|
-
import { basename as basename4, join as
|
|
7243
|
+
import { existsSync as existsSync27, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
|
|
7244
|
+
import { basename as basename4, join as join24, relative } from "path";
|
|
6953
7245
|
import * as readline2 from "readline";
|
|
6954
7246
|
var DATE_PREFIX_REGEX = /^\d{4}-\d{2}-\d{2}/;
|
|
6955
7247
|
function getDatePrefix(daysOffset = 0) {
|
|
@@ -6964,10 +7256,10 @@ function isValidDatePrefix(filename) {
|
|
|
6964
7256
|
return DATE_PREFIX_REGEX.test(filename);
|
|
6965
7257
|
}
|
|
6966
7258
|
function collectFiles(dir, extension) {
|
|
6967
|
-
if (!
|
|
7259
|
+
if (!existsSync27(dir)) return [];
|
|
6968
7260
|
const results = [];
|
|
6969
|
-
for (const entry of
|
|
6970
|
-
const fullPath =
|
|
7261
|
+
for (const entry of readdirSync5(dir)) {
|
|
7262
|
+
const fullPath = join24(dir, entry);
|
|
6971
7263
|
if (statSync2(fullPath).isDirectory()) {
|
|
6972
7264
|
results.push(...collectFiles(fullPath, extension));
|
|
6973
7265
|
} else if (entry.endsWith(extension)) {
|
|
@@ -7061,14 +7353,14 @@ async function configure() {
|
|
|
7061
7353
|
}
|
|
7062
7354
|
|
|
7063
7355
|
// src/commands/transcript/format/index.ts
|
|
7064
|
-
import { existsSync as
|
|
7356
|
+
import { existsSync as existsSync29 } from "fs";
|
|
7065
7357
|
|
|
7066
7358
|
// src/commands/transcript/format/fixInvalidDatePrefixes/index.ts
|
|
7067
|
-
import { dirname as
|
|
7359
|
+
import { dirname as dirname18, join as join26 } from "path";
|
|
7068
7360
|
|
|
7069
7361
|
// src/commands/transcript/format/fixInvalidDatePrefixes/promptForDateFix.ts
|
|
7070
7362
|
import { renameSync } from "fs";
|
|
7071
|
-
import { join as
|
|
7363
|
+
import { join as join25 } from "path";
|
|
7072
7364
|
async function resolveDate(rl, choice) {
|
|
7073
7365
|
if (choice === "1") return getDatePrefix(0);
|
|
7074
7366
|
if (choice === "2") return getDatePrefix(-1);
|
|
@@ -7083,7 +7375,7 @@ async function resolveDate(rl, choice) {
|
|
|
7083
7375
|
}
|
|
7084
7376
|
function renameWithPrefix(vttDir, vttFile, prefix2) {
|
|
7085
7377
|
const newFilename = `${prefix2}.${vttFile}`;
|
|
7086
|
-
renameSync(
|
|
7378
|
+
renameSync(join25(vttDir, vttFile), join25(vttDir, newFilename));
|
|
7087
7379
|
console.log(`Renamed to: ${newFilename}`);
|
|
7088
7380
|
return newFilename;
|
|
7089
7381
|
}
|
|
@@ -7114,15 +7406,15 @@ async function fixInvalidDatePrefixes(vttFiles) {
|
|
|
7114
7406
|
for (let i = 0; i < vttFiles.length; i++) {
|
|
7115
7407
|
const vttFile = vttFiles[i];
|
|
7116
7408
|
if (!isValidDatePrefix(vttFile.filename)) {
|
|
7117
|
-
const vttFileDir =
|
|
7409
|
+
const vttFileDir = dirname18(vttFile.absolutePath);
|
|
7118
7410
|
const newFilename = await promptForDateFix(vttFile.filename, vttFileDir);
|
|
7119
7411
|
if (newFilename) {
|
|
7120
|
-
const newRelativePath =
|
|
7121
|
-
|
|
7412
|
+
const newRelativePath = join26(
|
|
7413
|
+
dirname18(vttFile.relativePath),
|
|
7122
7414
|
newFilename
|
|
7123
7415
|
);
|
|
7124
7416
|
vttFiles[i] = {
|
|
7125
|
-
absolutePath:
|
|
7417
|
+
absolutePath: join26(vttFileDir, newFilename),
|
|
7126
7418
|
relativePath: newRelativePath,
|
|
7127
7419
|
filename: newFilename
|
|
7128
7420
|
};
|
|
@@ -7135,8 +7427,8 @@ async function fixInvalidDatePrefixes(vttFiles) {
|
|
|
7135
7427
|
}
|
|
7136
7428
|
|
|
7137
7429
|
// src/commands/transcript/format/processVttFile/index.ts
|
|
7138
|
-
import { existsSync as
|
|
7139
|
-
import { basename as basename5, dirname as
|
|
7430
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync7, readFileSync as readFileSync23, writeFileSync as writeFileSync22 } from "fs";
|
|
7431
|
+
import { basename as basename5, dirname as dirname19, join as join27 } from "path";
|
|
7140
7432
|
|
|
7141
7433
|
// src/commands/transcript/cleanText.ts
|
|
7142
7434
|
function cleanText(text) {
|
|
@@ -7346,21 +7638,21 @@ function toMdFilename(vttFilename) {
|
|
|
7346
7638
|
return `${basename5(vttFilename, ".vtt").replace(/\s*Transcription\s*/g, " ").trim()}.md`;
|
|
7347
7639
|
}
|
|
7348
7640
|
function resolveOutputDir(relativeDir, transcriptsDir) {
|
|
7349
|
-
return relativeDir === "." ? transcriptsDir :
|
|
7641
|
+
return relativeDir === "." ? transcriptsDir : join27(transcriptsDir, relativeDir);
|
|
7350
7642
|
}
|
|
7351
7643
|
function buildOutputPaths(vttFile, transcriptsDir) {
|
|
7352
7644
|
const mdFile = toMdFilename(vttFile.filename);
|
|
7353
|
-
const relativeDir =
|
|
7645
|
+
const relativeDir = dirname19(vttFile.relativePath);
|
|
7354
7646
|
const outputDir = resolveOutputDir(relativeDir, transcriptsDir);
|
|
7355
|
-
const outputPath =
|
|
7647
|
+
const outputPath = join27(outputDir, mdFile);
|
|
7356
7648
|
return { outputDir, outputPath, mdFile, relativeDir };
|
|
7357
7649
|
}
|
|
7358
7650
|
function logSkipped(relativeDir, mdFile) {
|
|
7359
|
-
console.log(`Skipping (already exists): ${
|
|
7651
|
+
console.log(`Skipping (already exists): ${join27(relativeDir, mdFile)}`);
|
|
7360
7652
|
return "skipped";
|
|
7361
7653
|
}
|
|
7362
7654
|
function ensureDirectory(dir, label2) {
|
|
7363
|
-
if (!
|
|
7655
|
+
if (!existsSync28(dir)) {
|
|
7364
7656
|
mkdirSync7(dir, { recursive: true });
|
|
7365
7657
|
console.log(`Created ${label2}: ${dir}`);
|
|
7366
7658
|
}
|
|
@@ -7383,7 +7675,7 @@ function logReduction(cueCount, messageCount) {
|
|
|
7383
7675
|
}
|
|
7384
7676
|
function readAndParseCues(inputPath) {
|
|
7385
7677
|
console.log(`Reading: ${inputPath}`);
|
|
7386
|
-
return processCues(
|
|
7678
|
+
return processCues(readFileSync23(inputPath, "utf-8"));
|
|
7387
7679
|
}
|
|
7388
7680
|
function writeFormatted(outputPath, content) {
|
|
7389
7681
|
writeFileSync22(outputPath, content, "utf-8");
|
|
@@ -7396,7 +7688,7 @@ function convertVttToMarkdown(inputPath, outputPath) {
|
|
|
7396
7688
|
logReduction(cues.length, chatMessages.length);
|
|
7397
7689
|
}
|
|
7398
7690
|
function tryProcessVtt(vttFile, paths) {
|
|
7399
|
-
if (
|
|
7691
|
+
if (existsSync28(paths.outputPath))
|
|
7400
7692
|
return logSkipped(paths.relativeDir, paths.mdFile);
|
|
7401
7693
|
convertVttToMarkdown(vttFile.absolutePath, paths.outputPath);
|
|
7402
7694
|
return "processed";
|
|
@@ -7422,7 +7714,7 @@ function processAllFiles(vttFiles, transcriptsDir) {
|
|
|
7422
7714
|
logSummary(counts);
|
|
7423
7715
|
}
|
|
7424
7716
|
function requireVttDir(vttDir) {
|
|
7425
|
-
if (!
|
|
7717
|
+
if (!existsSync29(vttDir)) {
|
|
7426
7718
|
console.error(`VTT directory not found: ${vttDir}`);
|
|
7427
7719
|
process.exit(1);
|
|
7428
7720
|
}
|
|
@@ -7454,28 +7746,28 @@ async function format() {
|
|
|
7454
7746
|
}
|
|
7455
7747
|
|
|
7456
7748
|
// src/commands/transcript/summarise/index.ts
|
|
7457
|
-
import { existsSync as
|
|
7458
|
-
import { basename as basename6, dirname as
|
|
7749
|
+
import { existsSync as existsSync31 } from "fs";
|
|
7750
|
+
import { basename as basename6, dirname as dirname21, join as join29, relative as relative2 } from "path";
|
|
7459
7751
|
|
|
7460
7752
|
// src/commands/transcript/summarise/processStagedFile/index.ts
|
|
7461
7753
|
import {
|
|
7462
|
-
existsSync as
|
|
7754
|
+
existsSync as existsSync30,
|
|
7463
7755
|
mkdirSync as mkdirSync8,
|
|
7464
|
-
readFileSync as
|
|
7756
|
+
readFileSync as readFileSync24,
|
|
7465
7757
|
renameSync as renameSync2,
|
|
7466
7758
|
rmSync
|
|
7467
7759
|
} from "fs";
|
|
7468
|
-
import { dirname as
|
|
7760
|
+
import { dirname as dirname20, join as join28 } from "path";
|
|
7469
7761
|
|
|
7470
7762
|
// src/commands/transcript/summarise/processStagedFile/validateStagedContent.ts
|
|
7471
|
-
import
|
|
7763
|
+
import chalk81 from "chalk";
|
|
7472
7764
|
var FULL_TRANSCRIPT_REGEX = /^\[Full Transcript\]\(([^)]+)\)/;
|
|
7473
7765
|
function validateStagedContent(filename, content) {
|
|
7474
7766
|
const firstLine = content.split("\n")[0];
|
|
7475
7767
|
const match = firstLine.match(FULL_TRANSCRIPT_REGEX);
|
|
7476
7768
|
if (!match) {
|
|
7477
7769
|
console.error(
|
|
7478
|
-
|
|
7770
|
+
chalk81.red(
|
|
7479
7771
|
`Staged file ${filename} missing [Full Transcript](<path>) link on first line.`
|
|
7480
7772
|
)
|
|
7481
7773
|
);
|
|
@@ -7484,7 +7776,7 @@ function validateStagedContent(filename, content) {
|
|
|
7484
7776
|
const contentAfterLink = content.slice(firstLine.length).trim();
|
|
7485
7777
|
if (!contentAfterLink) {
|
|
7486
7778
|
console.error(
|
|
7487
|
-
|
|
7779
|
+
chalk81.red(
|
|
7488
7780
|
`Staged file ${filename} has no summary content after the transcript link.`
|
|
7489
7781
|
)
|
|
7490
7782
|
);
|
|
@@ -7494,9 +7786,9 @@ function validateStagedContent(filename, content) {
|
|
|
7494
7786
|
}
|
|
7495
7787
|
|
|
7496
7788
|
// src/commands/transcript/summarise/processStagedFile/index.ts
|
|
7497
|
-
var STAGING_DIR =
|
|
7789
|
+
var STAGING_DIR = join28(process.cwd(), ".assist", "transcript");
|
|
7498
7790
|
function processStagedFile() {
|
|
7499
|
-
if (!
|
|
7791
|
+
if (!existsSync30(STAGING_DIR)) {
|
|
7500
7792
|
return false;
|
|
7501
7793
|
}
|
|
7502
7794
|
const stagedFiles = findMdFilesRecursive(STAGING_DIR);
|
|
@@ -7505,7 +7797,7 @@ function processStagedFile() {
|
|
|
7505
7797
|
}
|
|
7506
7798
|
const { transcriptsDir, summaryDir } = getTranscriptConfig();
|
|
7507
7799
|
const stagedFile = stagedFiles[0];
|
|
7508
|
-
const content =
|
|
7800
|
+
const content = readFileSync24(stagedFile.absolutePath, "utf-8");
|
|
7509
7801
|
validateStagedContent(stagedFile.filename, content);
|
|
7510
7802
|
const stagedBaseName = getTranscriptBaseName(stagedFile.filename);
|
|
7511
7803
|
const transcriptFiles = findMdFilesRecursive(transcriptsDir);
|
|
@@ -7518,9 +7810,9 @@ function processStagedFile() {
|
|
|
7518
7810
|
);
|
|
7519
7811
|
process.exit(1);
|
|
7520
7812
|
}
|
|
7521
|
-
const destPath =
|
|
7522
|
-
const destDir =
|
|
7523
|
-
if (!
|
|
7813
|
+
const destPath = join28(summaryDir, matchingTranscript.relativePath);
|
|
7814
|
+
const destDir = dirname20(destPath);
|
|
7815
|
+
if (!existsSync30(destDir)) {
|
|
7524
7816
|
mkdirSync8(destDir, { recursive: true });
|
|
7525
7817
|
}
|
|
7526
7818
|
renameSync2(stagedFile.absolutePath, destPath);
|
|
@@ -7533,8 +7825,8 @@ function processStagedFile() {
|
|
|
7533
7825
|
|
|
7534
7826
|
// src/commands/transcript/summarise/index.ts
|
|
7535
7827
|
function buildRelativeKey(relativePath, baseName) {
|
|
7536
|
-
const relDir =
|
|
7537
|
-
return relDir === "." ? baseName :
|
|
7828
|
+
const relDir = dirname21(relativePath);
|
|
7829
|
+
return relDir === "." ? baseName : join29(relDir, baseName);
|
|
7538
7830
|
}
|
|
7539
7831
|
function buildSummaryIndex(summaryDir) {
|
|
7540
7832
|
const summaryFiles = findMdFilesRecursive(summaryDir);
|
|
@@ -7547,7 +7839,7 @@ function buildSummaryIndex(summaryDir) {
|
|
|
7547
7839
|
function summarise2() {
|
|
7548
7840
|
processStagedFile();
|
|
7549
7841
|
const { transcriptsDir, summaryDir } = getTranscriptConfig();
|
|
7550
|
-
if (!
|
|
7842
|
+
if (!existsSync31(transcriptsDir)) {
|
|
7551
7843
|
console.log("No transcripts directory found.");
|
|
7552
7844
|
return;
|
|
7553
7845
|
}
|
|
@@ -7568,8 +7860,8 @@ function summarise2() {
|
|
|
7568
7860
|
}
|
|
7569
7861
|
const next2 = missing[0];
|
|
7570
7862
|
const outputFilename = `${getTranscriptBaseName(next2.filename)}.md`;
|
|
7571
|
-
const outputPath =
|
|
7572
|
-
const summaryFileDir =
|
|
7863
|
+
const outputPath = join29(STAGING_DIR, outputFilename);
|
|
7864
|
+
const summaryFileDir = join29(summaryDir, dirname21(next2.relativePath));
|
|
7573
7865
|
const relativeTranscriptPath = encodeURI(
|
|
7574
7866
|
relative2(summaryFileDir, next2.absolutePath).replace(/\\/g, "/")
|
|
7575
7867
|
);
|
|
@@ -7615,50 +7907,50 @@ function registerVerify(program2) {
|
|
|
7615
7907
|
|
|
7616
7908
|
// src/commands/voice/devices.ts
|
|
7617
7909
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
7618
|
-
import { join as
|
|
7910
|
+
import { join as join31 } from "path";
|
|
7619
7911
|
|
|
7620
7912
|
// src/commands/voice/shared.ts
|
|
7621
7913
|
import { homedir as homedir7 } from "os";
|
|
7622
|
-
import { dirname as
|
|
7914
|
+
import { dirname as dirname22, join as join30 } from "path";
|
|
7623
7915
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
7624
|
-
var __dirname6 =
|
|
7625
|
-
var VOICE_DIR =
|
|
7916
|
+
var __dirname6 = dirname22(fileURLToPath6(import.meta.url));
|
|
7917
|
+
var VOICE_DIR = join30(homedir7(), ".assist", "voice");
|
|
7626
7918
|
var voicePaths = {
|
|
7627
7919
|
dir: VOICE_DIR,
|
|
7628
|
-
pid:
|
|
7629
|
-
log:
|
|
7630
|
-
venv:
|
|
7631
|
-
lock:
|
|
7920
|
+
pid: join30(VOICE_DIR, "voice.pid"),
|
|
7921
|
+
log: join30(VOICE_DIR, "voice.log"),
|
|
7922
|
+
venv: join30(VOICE_DIR, ".venv"),
|
|
7923
|
+
lock: join30(VOICE_DIR, "voice.lock")
|
|
7632
7924
|
};
|
|
7633
7925
|
function getPythonDir() {
|
|
7634
|
-
return
|
|
7926
|
+
return join30(__dirname6, "commands", "voice", "python");
|
|
7635
7927
|
}
|
|
7636
7928
|
function getVenvPython() {
|
|
7637
|
-
return process.platform === "win32" ?
|
|
7929
|
+
return process.platform === "win32" ? join30(voicePaths.venv, "Scripts", "python.exe") : join30(voicePaths.venv, "bin", "python");
|
|
7638
7930
|
}
|
|
7639
7931
|
function getLockDir() {
|
|
7640
7932
|
const config = loadConfig();
|
|
7641
7933
|
return config.voice?.lockDir ?? VOICE_DIR;
|
|
7642
7934
|
}
|
|
7643
7935
|
function getLockFile() {
|
|
7644
|
-
return
|
|
7936
|
+
return join30(getLockDir(), "voice.lock");
|
|
7645
7937
|
}
|
|
7646
7938
|
|
|
7647
7939
|
// src/commands/voice/devices.ts
|
|
7648
7940
|
function devices() {
|
|
7649
|
-
const script =
|
|
7941
|
+
const script = join31(getPythonDir(), "list_devices.py");
|
|
7650
7942
|
spawnSync3(getVenvPython(), [script], { stdio: "inherit" });
|
|
7651
7943
|
}
|
|
7652
7944
|
|
|
7653
7945
|
// src/commands/voice/logs.ts
|
|
7654
|
-
import { existsSync as
|
|
7946
|
+
import { existsSync as existsSync32, readFileSync as readFileSync25 } from "fs";
|
|
7655
7947
|
function logs(options2) {
|
|
7656
|
-
if (!
|
|
7948
|
+
if (!existsSync32(voicePaths.log)) {
|
|
7657
7949
|
console.log("No voice log file found");
|
|
7658
7950
|
return;
|
|
7659
7951
|
}
|
|
7660
7952
|
const count = Number.parseInt(options2.lines ?? "150", 10);
|
|
7661
|
-
const content =
|
|
7953
|
+
const content = readFileSync25(voicePaths.log, "utf-8").trim();
|
|
7662
7954
|
if (!content) {
|
|
7663
7955
|
console.log("Voice log is empty");
|
|
7664
7956
|
return;
|
|
@@ -7681,12 +7973,12 @@ function logs(options2) {
|
|
|
7681
7973
|
// src/commands/voice/setup.ts
|
|
7682
7974
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
7683
7975
|
import { mkdirSync as mkdirSync10 } from "fs";
|
|
7684
|
-
import { join as
|
|
7976
|
+
import { join as join33 } from "path";
|
|
7685
7977
|
|
|
7686
7978
|
// src/commands/voice/checkLockFile.ts
|
|
7687
|
-
import { execSync as
|
|
7688
|
-
import { existsSync as
|
|
7689
|
-
import { join as
|
|
7979
|
+
import { execSync as execSync34 } from "child_process";
|
|
7980
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync9, readFileSync as readFileSync26, writeFileSync as writeFileSync23 } from "fs";
|
|
7981
|
+
import { join as join32 } from "path";
|
|
7690
7982
|
function isProcessAlive(pid) {
|
|
7691
7983
|
try {
|
|
7692
7984
|
process.kill(pid, 0);
|
|
@@ -7697,9 +7989,9 @@ function isProcessAlive(pid) {
|
|
|
7697
7989
|
}
|
|
7698
7990
|
function checkLockFile() {
|
|
7699
7991
|
const lockFile = getLockFile();
|
|
7700
|
-
if (!
|
|
7992
|
+
if (!existsSync33(lockFile)) return;
|
|
7701
7993
|
try {
|
|
7702
|
-
const lock = JSON.parse(
|
|
7994
|
+
const lock = JSON.parse(readFileSync26(lockFile, "utf-8"));
|
|
7703
7995
|
if (lock.pid && isProcessAlive(lock.pid)) {
|
|
7704
7996
|
console.error(
|
|
7705
7997
|
`Voice daemon already running (PID ${lock.pid}, env: ${lock.env}). Stop it first with: assist voice stop`
|
|
@@ -7710,10 +8002,10 @@ function checkLockFile() {
|
|
|
7710
8002
|
}
|
|
7711
8003
|
}
|
|
7712
8004
|
function bootstrapVenv() {
|
|
7713
|
-
if (
|
|
8005
|
+
if (existsSync33(getVenvPython())) return;
|
|
7714
8006
|
console.log("Setting up Python environment...");
|
|
7715
8007
|
const pythonDir = getPythonDir();
|
|
7716
|
-
|
|
8008
|
+
execSync34(
|
|
7717
8009
|
`uv sync --project "${pythonDir}" --extra runtime --no-install-project`,
|
|
7718
8010
|
{
|
|
7719
8011
|
stdio: "inherit",
|
|
@@ -7723,7 +8015,7 @@ function bootstrapVenv() {
|
|
|
7723
8015
|
}
|
|
7724
8016
|
function writeLockFile(pid) {
|
|
7725
8017
|
const lockFile = getLockFile();
|
|
7726
|
-
mkdirSync9(
|
|
8018
|
+
mkdirSync9(join32(lockFile, ".."), { recursive: true });
|
|
7727
8019
|
writeFileSync23(
|
|
7728
8020
|
lockFile,
|
|
7729
8021
|
JSON.stringify({
|
|
@@ -7739,7 +8031,7 @@ function setup() {
|
|
|
7739
8031
|
mkdirSync10(voicePaths.dir, { recursive: true });
|
|
7740
8032
|
bootstrapVenv();
|
|
7741
8033
|
console.log("\nDownloading models...\n");
|
|
7742
|
-
const script =
|
|
8034
|
+
const script = join33(getPythonDir(), "setup_models.py");
|
|
7743
8035
|
const result = spawnSync4(getVenvPython(), [script], {
|
|
7744
8036
|
stdio: "inherit",
|
|
7745
8037
|
env: { ...process.env, VOICE_LOG_FILE: voicePaths.log }
|
|
@@ -7753,7 +8045,7 @@ function setup() {
|
|
|
7753
8045
|
// src/commands/voice/start.ts
|
|
7754
8046
|
import { spawn as spawn4 } from "child_process";
|
|
7755
8047
|
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync24 } from "fs";
|
|
7756
|
-
import { join as
|
|
8048
|
+
import { join as join34 } from "path";
|
|
7757
8049
|
|
|
7758
8050
|
// src/commands/voice/buildDaemonEnv.ts
|
|
7759
8051
|
function buildDaemonEnv(options2) {
|
|
@@ -7791,7 +8083,7 @@ function start2(options2) {
|
|
|
7791
8083
|
bootstrapVenv();
|
|
7792
8084
|
const debug = options2.debug || options2.foreground || process.platform === "win32";
|
|
7793
8085
|
const env = buildDaemonEnv({ debug });
|
|
7794
|
-
const script =
|
|
8086
|
+
const script = join34(getPythonDir(), "voice_daemon.py");
|
|
7795
8087
|
const python = getVenvPython();
|
|
7796
8088
|
if (options2.foreground) {
|
|
7797
8089
|
spawnForeground(python, script, env);
|
|
@@ -7801,7 +8093,7 @@ function start2(options2) {
|
|
|
7801
8093
|
}
|
|
7802
8094
|
|
|
7803
8095
|
// src/commands/voice/status.ts
|
|
7804
|
-
import { existsSync as
|
|
8096
|
+
import { existsSync as existsSync34, readFileSync as readFileSync27 } from "fs";
|
|
7805
8097
|
function isProcessAlive2(pid) {
|
|
7806
8098
|
try {
|
|
7807
8099
|
process.kill(pid, 0);
|
|
@@ -7811,16 +8103,16 @@ function isProcessAlive2(pid) {
|
|
|
7811
8103
|
}
|
|
7812
8104
|
}
|
|
7813
8105
|
function readRecentLogs(count) {
|
|
7814
|
-
if (!
|
|
7815
|
-
const lines =
|
|
8106
|
+
if (!existsSync34(voicePaths.log)) return [];
|
|
8107
|
+
const lines = readFileSync27(voicePaths.log, "utf-8").trim().split("\n");
|
|
7816
8108
|
return lines.slice(-count);
|
|
7817
8109
|
}
|
|
7818
8110
|
function status() {
|
|
7819
|
-
if (!
|
|
8111
|
+
if (!existsSync34(voicePaths.pid)) {
|
|
7820
8112
|
console.log("Voice daemon: not running (no PID file)");
|
|
7821
8113
|
return;
|
|
7822
8114
|
}
|
|
7823
|
-
const pid = Number.parseInt(
|
|
8115
|
+
const pid = Number.parseInt(readFileSync27(voicePaths.pid, "utf-8").trim(), 10);
|
|
7824
8116
|
const alive = isProcessAlive2(pid);
|
|
7825
8117
|
console.log(`Voice daemon: ${alive ? "running" : "dead"} (PID ${pid})`);
|
|
7826
8118
|
const recent = readRecentLogs(5);
|
|
@@ -7839,13 +8131,13 @@ function status() {
|
|
|
7839
8131
|
}
|
|
7840
8132
|
|
|
7841
8133
|
// src/commands/voice/stop.ts
|
|
7842
|
-
import { existsSync as
|
|
8134
|
+
import { existsSync as existsSync35, readFileSync as readFileSync28, unlinkSync as unlinkSync8 } from "fs";
|
|
7843
8135
|
function stop() {
|
|
7844
|
-
if (!
|
|
8136
|
+
if (!existsSync35(voicePaths.pid)) {
|
|
7845
8137
|
console.log("Voice daemon is not running (no PID file)");
|
|
7846
8138
|
return;
|
|
7847
8139
|
}
|
|
7848
|
-
const pid = Number.parseInt(
|
|
8140
|
+
const pid = Number.parseInt(readFileSync28(voicePaths.pid, "utf-8").trim(), 10);
|
|
7849
8141
|
try {
|
|
7850
8142
|
process.kill(pid, "SIGTERM");
|
|
7851
8143
|
console.log(`Sent SIGTERM to voice daemon (PID ${pid})`);
|
|
@@ -7853,12 +8145,12 @@ function stop() {
|
|
|
7853
8145
|
console.log(`Voice daemon (PID ${pid}) is not running`);
|
|
7854
8146
|
}
|
|
7855
8147
|
try {
|
|
7856
|
-
|
|
8148
|
+
unlinkSync8(voicePaths.pid);
|
|
7857
8149
|
} catch {
|
|
7858
8150
|
}
|
|
7859
8151
|
try {
|
|
7860
8152
|
const lockFile = getLockFile();
|
|
7861
|
-
if (
|
|
8153
|
+
if (existsSync35(lockFile)) unlinkSync8(lockFile);
|
|
7862
8154
|
} catch {
|
|
7863
8155
|
}
|
|
7864
8156
|
console.log("Voice daemon stopped");
|
|
@@ -7877,14 +8169,14 @@ function registerVoice(program2) {
|
|
|
7877
8169
|
|
|
7878
8170
|
// src/commands/roam/auth.ts
|
|
7879
8171
|
import { randomBytes } from "crypto";
|
|
7880
|
-
import
|
|
8172
|
+
import chalk82 from "chalk";
|
|
7881
8173
|
|
|
7882
8174
|
// src/lib/openBrowser.ts
|
|
7883
|
-
import { execSync as
|
|
8175
|
+
import { execSync as execSync35 } from "child_process";
|
|
7884
8176
|
function tryExec(commands) {
|
|
7885
8177
|
for (const cmd of commands) {
|
|
7886
8178
|
try {
|
|
7887
|
-
|
|
8179
|
+
execSync35(cmd);
|
|
7888
8180
|
return true;
|
|
7889
8181
|
} catch {
|
|
7890
8182
|
}
|
|
@@ -8052,13 +8344,13 @@ async function auth() {
|
|
|
8052
8344
|
saveGlobalConfig(config);
|
|
8053
8345
|
const state = randomBytes(16).toString("hex");
|
|
8054
8346
|
console.log(
|
|
8055
|
-
|
|
8347
|
+
chalk82.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
|
|
8056
8348
|
);
|
|
8057
|
-
console.log(
|
|
8058
|
-
console.log(
|
|
8059
|
-
console.log(
|
|
8349
|
+
console.log(chalk82.white("http://localhost:14523/callback\n"));
|
|
8350
|
+
console.log(chalk82.blue("Opening browser for authorization..."));
|
|
8351
|
+
console.log(chalk82.dim("Waiting for authorization callback..."));
|
|
8060
8352
|
const { code, redirectUri } = await authorizeInBrowser(clientId, state);
|
|
8061
|
-
console.log(
|
|
8353
|
+
console.log(chalk82.dim("Exchanging code for tokens..."));
|
|
8062
8354
|
const tokens = await exchangeToken({
|
|
8063
8355
|
code,
|
|
8064
8356
|
clientId,
|
|
@@ -8074,7 +8366,7 @@ async function auth() {
|
|
|
8074
8366
|
};
|
|
8075
8367
|
saveGlobalConfig(config);
|
|
8076
8368
|
console.log(
|
|
8077
|
-
|
|
8369
|
+
chalk82.green("Roam credentials and tokens saved to ~/.assist.yml")
|
|
8078
8370
|
);
|
|
8079
8371
|
}
|
|
8080
8372
|
|
|
@@ -8085,16 +8377,7 @@ function registerRoam(program2) {
|
|
|
8085
8377
|
}
|
|
8086
8378
|
|
|
8087
8379
|
// src/commands/run/index.ts
|
|
8088
|
-
import {
|
|
8089
|
-
|
|
8090
|
-
// src/shared/formatElapsed.ts
|
|
8091
|
-
function formatElapsed(ms) {
|
|
8092
|
-
const secs = ms / 1e3;
|
|
8093
|
-
if (secs < 60) return `${secs.toFixed(1)}s`;
|
|
8094
|
-
const mins = Math.floor(secs / 60);
|
|
8095
|
-
const remainSecs = secs - mins * 60;
|
|
8096
|
-
return `${mins}m ${remainSecs.toFixed(1)}s`;
|
|
8097
|
-
}
|
|
8380
|
+
import { execSync as execSync36 } from "child_process";
|
|
8098
8381
|
|
|
8099
8382
|
// src/commands/run/resolveParams.ts
|
|
8100
8383
|
function resolveParams(params, cliArgs) {
|
|
@@ -8120,9 +8403,30 @@ function resolveParams(params, cliArgs) {
|
|
|
8120
8403
|
return resolved;
|
|
8121
8404
|
}
|
|
8122
8405
|
|
|
8406
|
+
// src/commands/run/spawnRunCommand.ts
|
|
8407
|
+
import { spawn as spawn5 } from "child_process";
|
|
8408
|
+
function spawnRunCommand(fullCommand, env) {
|
|
8409
|
+
const start3 = Date.now();
|
|
8410
|
+
const child = spawn5(fullCommand, [], {
|
|
8411
|
+
stdio: "inherit",
|
|
8412
|
+
shell: true,
|
|
8413
|
+
env: env ? { ...process.env, ...expandEnv(env) } : void 0
|
|
8414
|
+
});
|
|
8415
|
+
child.on("close", (code) => {
|
|
8416
|
+
const elapsed = formatElapsed(Date.now() - start3);
|
|
8417
|
+
console.log(`
|
|
8418
|
+
Done in ${elapsed}`);
|
|
8419
|
+
process.exit(code ?? 0);
|
|
8420
|
+
});
|
|
8421
|
+
child.on("error", (err) => {
|
|
8422
|
+
console.error(`Failed to execute command: ${err.message}`);
|
|
8423
|
+
process.exit(1);
|
|
8424
|
+
});
|
|
8425
|
+
}
|
|
8426
|
+
|
|
8123
8427
|
// src/commands/run/add.ts
|
|
8124
8428
|
import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync25 } from "fs";
|
|
8125
|
-
import { join as
|
|
8429
|
+
import { join as join35 } from "path";
|
|
8126
8430
|
function findAddIndex() {
|
|
8127
8431
|
const addIndex = process.argv.indexOf("add");
|
|
8128
8432
|
if (addIndex === -1 || addIndex + 2 >= process.argv.length) return -1;
|
|
@@ -8176,7 +8480,7 @@ function saveNewRunConfig(name, command, args) {
|
|
|
8176
8480
|
saveConfig(config);
|
|
8177
8481
|
}
|
|
8178
8482
|
function createCommandFile(name) {
|
|
8179
|
-
const dir =
|
|
8483
|
+
const dir = join35(".claude", "commands");
|
|
8180
8484
|
mkdirSync12(dir, { recursive: true });
|
|
8181
8485
|
const content = `---
|
|
8182
8486
|
description: Run ${name}
|
|
@@ -8184,7 +8488,7 @@ description: Run ${name}
|
|
|
8184
8488
|
|
|
8185
8489
|
Run \`assist run ${name} $ARGUMENTS 2>&1\`.
|
|
8186
8490
|
`;
|
|
8187
|
-
const filePath =
|
|
8491
|
+
const filePath = join35(dir, `${name}.md`);
|
|
8188
8492
|
writeFileSync25(filePath, content);
|
|
8189
8493
|
console.log(`Created command file: ${filePath}`);
|
|
8190
8494
|
}
|
|
@@ -8226,25 +8530,6 @@ function findRunConfig(name) {
|
|
|
8226
8530
|
const configs = requireRunConfigs();
|
|
8227
8531
|
return configs.find((r) => r.name === name) ?? exitWithConfigNotFound(name, configs);
|
|
8228
8532
|
}
|
|
8229
|
-
function onSpawnError(err) {
|
|
8230
|
-
console.error(`Failed to execute command: ${err.message}`);
|
|
8231
|
-
process.exit(1);
|
|
8232
|
-
}
|
|
8233
|
-
function spawnCommand2(fullCommand, env) {
|
|
8234
|
-
const start3 = Date.now();
|
|
8235
|
-
const child = spawn5(fullCommand, [], {
|
|
8236
|
-
stdio: "inherit",
|
|
8237
|
-
shell: true,
|
|
8238
|
-
env: env ? { ...process.env, ...expandEnv(env) } : void 0
|
|
8239
|
-
});
|
|
8240
|
-
child.on("close", (code) => {
|
|
8241
|
-
const elapsed = formatElapsed(Date.now() - start3);
|
|
8242
|
-
console.log(`
|
|
8243
|
-
Done in ${elapsed}`);
|
|
8244
|
-
process.exit(code ?? 0);
|
|
8245
|
-
});
|
|
8246
|
-
child.on("error", onSpawnError);
|
|
8247
|
-
}
|
|
8248
8533
|
function listRunConfigs() {
|
|
8249
8534
|
const configs = requireRunConfigs();
|
|
8250
8535
|
for (const config of configs) {
|
|
@@ -8252,17 +8537,28 @@ function listRunConfigs() {
|
|
|
8252
8537
|
console.log(`${config.name}: ${config.command}${args}`);
|
|
8253
8538
|
}
|
|
8254
8539
|
}
|
|
8540
|
+
function runPreCommands(pre) {
|
|
8541
|
+
for (const cmd of pre) {
|
|
8542
|
+
try {
|
|
8543
|
+
execSync36(cmd, { stdio: "inherit" });
|
|
8544
|
+
} catch (err) {
|
|
8545
|
+
const code = err && typeof err === "object" && "status" in err ? err.status : 1;
|
|
8546
|
+
process.exit(code);
|
|
8547
|
+
}
|
|
8548
|
+
}
|
|
8549
|
+
}
|
|
8255
8550
|
function run2(name, args) {
|
|
8256
8551
|
const runConfig = findRunConfig(name);
|
|
8552
|
+
if (runConfig.pre) runPreCommands(runConfig.pre);
|
|
8257
8553
|
const resolved = resolveParams(runConfig.params, args);
|
|
8258
|
-
|
|
8554
|
+
spawnRunCommand(
|
|
8259
8555
|
buildCommand(runConfig.command, runConfig.args ?? [], resolved),
|
|
8260
8556
|
runConfig.env
|
|
8261
8557
|
);
|
|
8262
8558
|
}
|
|
8263
8559
|
|
|
8264
8560
|
// src/commands/statusLine.ts
|
|
8265
|
-
import
|
|
8561
|
+
import chalk83 from "chalk";
|
|
8266
8562
|
function formatNumber(num) {
|
|
8267
8563
|
return num.toLocaleString("en-US");
|
|
8268
8564
|
}
|
|
@@ -8277,8 +8573,8 @@ function formatTimeLeft(resetsAt) {
|
|
|
8277
8573
|
}
|
|
8278
8574
|
function colorizePercent(pct) {
|
|
8279
8575
|
const label2 = `${Math.round(pct)}%`;
|
|
8280
|
-
if (pct > 80) return
|
|
8281
|
-
if (pct > 40) return
|
|
8576
|
+
if (pct > 80) return chalk83.red(label2);
|
|
8577
|
+
if (pct > 40) return chalk83.yellow(label2);
|
|
8282
8578
|
return label2;
|
|
8283
8579
|
}
|
|
8284
8580
|
function formatLimit(limit, fallbackLabel) {
|
|
@@ -8313,27 +8609,27 @@ async function statusLine() {
|
|
|
8313
8609
|
// src/commands/sync.ts
|
|
8314
8610
|
import * as fs24 from "fs";
|
|
8315
8611
|
import * as os from "os";
|
|
8316
|
-
import * as
|
|
8612
|
+
import * as path42 from "path";
|
|
8317
8613
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
8318
8614
|
|
|
8319
8615
|
// src/commands/sync/syncClaudeMd.ts
|
|
8320
8616
|
import * as fs22 from "fs";
|
|
8321
|
-
import * as
|
|
8322
|
-
import
|
|
8617
|
+
import * as path40 from "path";
|
|
8618
|
+
import chalk84 from "chalk";
|
|
8323
8619
|
async function syncClaudeMd(claudeDir, targetBase) {
|
|
8324
|
-
const source =
|
|
8325
|
-
const target =
|
|
8620
|
+
const source = path40.join(claudeDir, "CLAUDE.md");
|
|
8621
|
+
const target = path40.join(targetBase, "CLAUDE.md");
|
|
8326
8622
|
const sourceContent = fs22.readFileSync(source, "utf-8");
|
|
8327
8623
|
if (fs22.existsSync(target)) {
|
|
8328
8624
|
const targetContent = fs22.readFileSync(target, "utf-8");
|
|
8329
8625
|
if (sourceContent !== targetContent) {
|
|
8330
8626
|
console.log(
|
|
8331
|
-
|
|
8627
|
+
chalk84.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
|
|
8332
8628
|
);
|
|
8333
8629
|
console.log();
|
|
8334
8630
|
printDiff(targetContent, sourceContent);
|
|
8335
8631
|
const confirm = await promptConfirm(
|
|
8336
|
-
|
|
8632
|
+
chalk84.red("Overwrite existing CLAUDE.md?"),
|
|
8337
8633
|
false
|
|
8338
8634
|
);
|
|
8339
8635
|
if (!confirm) {
|
|
@@ -8348,11 +8644,11 @@ async function syncClaudeMd(claudeDir, targetBase) {
|
|
|
8348
8644
|
|
|
8349
8645
|
// src/commands/sync/syncSettings.ts
|
|
8350
8646
|
import * as fs23 from "fs";
|
|
8351
|
-
import * as
|
|
8352
|
-
import
|
|
8647
|
+
import * as path41 from "path";
|
|
8648
|
+
import chalk85 from "chalk";
|
|
8353
8649
|
async function syncSettings(claudeDir, targetBase, options2) {
|
|
8354
|
-
const source =
|
|
8355
|
-
const target =
|
|
8650
|
+
const source = path41.join(claudeDir, "settings.json");
|
|
8651
|
+
const target = path41.join(targetBase, "settings.json");
|
|
8356
8652
|
const sourceContent = fs23.readFileSync(source, "utf-8");
|
|
8357
8653
|
const mergedContent = JSON.stringify(JSON.parse(sourceContent), null, " ");
|
|
8358
8654
|
if (fs23.existsSync(target)) {
|
|
@@ -8365,14 +8661,14 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
8365
8661
|
if (mergedContent !== normalizedTarget) {
|
|
8366
8662
|
if (!options2?.yes) {
|
|
8367
8663
|
console.log(
|
|
8368
|
-
|
|
8664
|
+
chalk85.yellow(
|
|
8369
8665
|
"\n\u26A0\uFE0F Warning: settings.json differs from existing file"
|
|
8370
8666
|
)
|
|
8371
8667
|
);
|
|
8372
8668
|
console.log();
|
|
8373
8669
|
printDiff(targetContent, mergedContent);
|
|
8374
8670
|
const confirm = await promptConfirm(
|
|
8375
|
-
|
|
8671
|
+
chalk85.red("Overwrite existing settings.json?"),
|
|
8376
8672
|
false
|
|
8377
8673
|
);
|
|
8378
8674
|
if (!confirm) {
|
|
@@ -8388,37 +8684,37 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
8388
8684
|
|
|
8389
8685
|
// src/commands/sync.ts
|
|
8390
8686
|
var __filename4 = fileURLToPath7(import.meta.url);
|
|
8391
|
-
var __dirname7 =
|
|
8687
|
+
var __dirname7 = path42.dirname(__filename4);
|
|
8392
8688
|
async function sync(options2) {
|
|
8393
|
-
const claudeDir =
|
|
8394
|
-
const targetBase =
|
|
8689
|
+
const claudeDir = path42.join(__dirname7, "..", "claude");
|
|
8690
|
+
const targetBase = path42.join(os.homedir(), ".claude");
|
|
8395
8691
|
syncCommands(claudeDir, targetBase);
|
|
8396
8692
|
await syncSettings(claudeDir, targetBase, { yes: options2?.yes });
|
|
8397
8693
|
await syncClaudeMd(claudeDir, targetBase);
|
|
8398
8694
|
}
|
|
8399
8695
|
function syncCommands(claudeDir, targetBase) {
|
|
8400
|
-
const sourceDir =
|
|
8401
|
-
const targetDir =
|
|
8696
|
+
const sourceDir = path42.join(claudeDir, "commands");
|
|
8697
|
+
const targetDir = path42.join(targetBase, "commands");
|
|
8402
8698
|
fs24.mkdirSync(targetDir, { recursive: true });
|
|
8403
8699
|
const files = fs24.readdirSync(sourceDir);
|
|
8404
8700
|
for (const file of files) {
|
|
8405
|
-
fs24.copyFileSync(
|
|
8701
|
+
fs24.copyFileSync(path42.join(sourceDir, file), path42.join(targetDir, file));
|
|
8406
8702
|
console.log(`Copied ${file} to ${targetDir}`);
|
|
8407
8703
|
}
|
|
8408
8704
|
console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
|
|
8409
8705
|
}
|
|
8410
8706
|
|
|
8411
8707
|
// src/commands/update.ts
|
|
8412
|
-
import { execSync as
|
|
8413
|
-
import * as
|
|
8708
|
+
import { execSync as execSync37 } from "child_process";
|
|
8709
|
+
import * as path43 from "path";
|
|
8414
8710
|
function isGlobalNpmInstall(dir) {
|
|
8415
8711
|
try {
|
|
8416
|
-
const resolved =
|
|
8417
|
-
if (resolved.split(
|
|
8712
|
+
const resolved = path43.resolve(dir);
|
|
8713
|
+
if (resolved.split(path43.sep).includes("node_modules")) {
|
|
8418
8714
|
return true;
|
|
8419
8715
|
}
|
|
8420
|
-
const globalPrefix =
|
|
8421
|
-
return resolved.toLowerCase().startsWith(
|
|
8716
|
+
const globalPrefix = execSync37("npm prefix -g", { stdio: "pipe" }).toString().trim();
|
|
8717
|
+
return resolved.toLowerCase().startsWith(path43.resolve(globalPrefix).toLowerCase());
|
|
8422
8718
|
} catch {
|
|
8423
8719
|
return false;
|
|
8424
8720
|
}
|
|
@@ -8428,18 +8724,18 @@ async function update() {
|
|
|
8428
8724
|
console.log(`Assist is installed at: ${installDir}`);
|
|
8429
8725
|
if (isGitRepo(installDir)) {
|
|
8430
8726
|
console.log("Detected git repo installation, pulling latest...");
|
|
8431
|
-
|
|
8727
|
+
execSync37("git pull", { cwd: installDir, stdio: "inherit" });
|
|
8432
8728
|
console.log("Installing dependencies...");
|
|
8433
|
-
|
|
8729
|
+
execSync37("npm i", { cwd: installDir, stdio: "inherit" });
|
|
8434
8730
|
console.log("Building...");
|
|
8435
|
-
|
|
8731
|
+
execSync37("npm run build", { cwd: installDir, stdio: "inherit" });
|
|
8436
8732
|
console.log("Syncing commands...");
|
|
8437
|
-
|
|
8733
|
+
execSync37("assist sync", { stdio: "inherit" });
|
|
8438
8734
|
} else if (isGlobalNpmInstall(installDir)) {
|
|
8439
8735
|
console.log("Detected global npm installation, updating...");
|
|
8440
|
-
|
|
8736
|
+
execSync37("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
|
|
8441
8737
|
console.log("Syncing commands...");
|
|
8442
|
-
|
|
8738
|
+
execSync37("assist sync", { stdio: "inherit" });
|
|
8443
8739
|
} else {
|
|
8444
8740
|
console.error(
|
|
8445
8741
|
"Could not determine installation method. Expected a git repo or global npm install."
|
|
@@ -8486,7 +8782,7 @@ registerRefactor(program);
|
|
|
8486
8782
|
registerDevlog(program);
|
|
8487
8783
|
registerDeploy(program);
|
|
8488
8784
|
registerComplexity(program);
|
|
8489
|
-
|
|
8785
|
+
registerDotnet(program);
|
|
8490
8786
|
registerNews(program);
|
|
8491
8787
|
registerRavendb(program);
|
|
8492
8788
|
registerTranscript(program);
|