@staff0rd/assist 0.120.1 → 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 +960 -673
- 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: {
|
|
@@ -90,10 +90,10 @@ import { stringify as stringifyYaml } from "yaml";
|
|
|
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(),
|
|
@@ -351,9 +352,9 @@ function isTraversable(value) {
|
|
|
351
352
|
function stepInto(current, key) {
|
|
352
353
|
return isTraversable(current) ? current[key] : void 0;
|
|
353
354
|
}
|
|
354
|
-
function getNestedValue(obj,
|
|
355
|
+
function getNestedValue(obj, path44) {
|
|
355
356
|
let current = obj;
|
|
356
|
-
for (const key of
|
|
357
|
+
for (const key of path44.split(".")) current = stepInto(current, key);
|
|
357
358
|
return current;
|
|
358
359
|
}
|
|
359
360
|
|
|
@@ -394,8 +395,8 @@ function stepIntoNested(container, key, nextKey) {
|
|
|
394
395
|
}
|
|
395
396
|
return ensureObject(container, resolved);
|
|
396
397
|
}
|
|
397
|
-
function setNestedValue(obj,
|
|
398
|
-
const keys =
|
|
398
|
+
function setNestedValue(obj, path44, value) {
|
|
399
|
+
const keys = path44.split(".");
|
|
399
400
|
const result = { ...obj };
|
|
400
401
|
let current = result;
|
|
401
402
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
@@ -2841,12 +2842,12 @@ function getCliReadsPath() {
|
|
|
2841
2842
|
var cachedLines;
|
|
2842
2843
|
function getCliReadsLines() {
|
|
2843
2844
|
if (cachedLines) return cachedLines;
|
|
2844
|
-
const
|
|
2845
|
-
if (!existsSync16(
|
|
2845
|
+
const path44 = getCliReadsPath();
|
|
2846
|
+
if (!existsSync16(path44)) {
|
|
2846
2847
|
cachedLines = [];
|
|
2847
2848
|
return cachedLines;
|
|
2848
2849
|
}
|
|
2849
|
-
cachedLines = readFileSync13(
|
|
2850
|
+
cachedLines = readFileSync13(path44, "utf-8").split("\n").filter((line) => line.trim() !== "");
|
|
2850
2851
|
return cachedLines;
|
|
2851
2852
|
}
|
|
2852
2853
|
function loadCliReads() {
|
|
@@ -3198,14 +3199,14 @@ function showProgress(p, label2) {
|
|
|
3198
3199
|
const pct = Math.round(p.done / p.total * 100);
|
|
3199
3200
|
process.stderr.write(`\r\x1B[K[${pct}%] Scanning ${label2}...`);
|
|
3200
3201
|
}
|
|
3201
|
-
async function resolveCommand(cli,
|
|
3202
|
-
showProgress(p,
|
|
3203
|
-
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]);
|
|
3204
3205
|
if (!subHelp || !hasSubcommands(subHelp)) {
|
|
3205
|
-
return [{ path:
|
|
3206
|
+
return [{ path: path44, description }];
|
|
3206
3207
|
}
|
|
3207
|
-
const children = await discoverAt(cli,
|
|
3208
|
-
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 }];
|
|
3209
3210
|
}
|
|
3210
3211
|
async function discoverAt(cli, parentPath, depth, p) {
|
|
3211
3212
|
if (depth > SAFETY_DEPTH) return [];
|
|
@@ -3353,9 +3354,9 @@ function logPath(cli) {
|
|
|
3353
3354
|
return join12(homedir4(), ".assist", `cli-discover-${safeName}.log`);
|
|
3354
3355
|
}
|
|
3355
3356
|
function readCache(cli) {
|
|
3356
|
-
const
|
|
3357
|
-
if (!existsSync18(
|
|
3358
|
-
return readFileSync15(
|
|
3357
|
+
const path44 = logPath(cli);
|
|
3358
|
+
if (!existsSync18(path44)) return void 0;
|
|
3359
|
+
return readFileSync15(path44, "utf-8");
|
|
3359
3360
|
}
|
|
3360
3361
|
function writeCache(cli, output) {
|
|
3361
3362
|
const dir = join12(homedir4(), ".assist");
|
|
@@ -4437,234 +4438,88 @@ function registerDevlog(program2) {
|
|
|
4437
4438
|
).option("--all", "Show all non-archived repos regardless of push date").action(repos);
|
|
4438
4439
|
}
|
|
4439
4440
|
|
|
4440
|
-
// src/commands/
|
|
4441
|
-
import
|
|
4442
|
-
|
|
4443
|
-
// src/commands/jira/adfToText.ts
|
|
4444
|
-
function renderInline(node) {
|
|
4445
|
-
const text = node.text ?? "";
|
|
4446
|
-
if (node.marks?.some((m) => m.type === "code")) return `\`${text}\``;
|
|
4447
|
-
return text;
|
|
4448
|
-
}
|
|
4449
|
-
function renderChildren(node, indent) {
|
|
4450
|
-
return renderNodes(node.content ?? [], indent);
|
|
4451
|
-
}
|
|
4452
|
-
function renderOrderedList(node, indent) {
|
|
4453
|
-
let counter = 0;
|
|
4454
|
-
return (node.content ?? []).map((item) => {
|
|
4455
|
-
counter++;
|
|
4456
|
-
return renderListItem(item, indent, `${counter}.`);
|
|
4457
|
-
}).join("\n");
|
|
4458
|
-
}
|
|
4459
|
-
function renderBulletList(node, indent) {
|
|
4460
|
-
return (node.content ?? []).map((item) => renderListItem(item, indent, "-")).join("\n");
|
|
4461
|
-
}
|
|
4462
|
-
function renderHeading(node, indent) {
|
|
4463
|
-
const level = node.attrs?.level ?? 1;
|
|
4464
|
-
return `${"#".repeat(level)} ${renderChildren(node, indent)}`;
|
|
4465
|
-
}
|
|
4466
|
-
var renderers = {
|
|
4467
|
-
text: (node) => renderInline(node),
|
|
4468
|
-
paragraph: renderChildren,
|
|
4469
|
-
orderedList: renderOrderedList,
|
|
4470
|
-
bulletList: renderBulletList,
|
|
4471
|
-
listItem: (node, indent) => renderListItem(node, indent, "-"),
|
|
4472
|
-
heading: renderHeading,
|
|
4473
|
-
doc: renderChildren
|
|
4474
|
-
};
|
|
4475
|
-
function renderNode(node, indent) {
|
|
4476
|
-
const renderer = renderers[node.type];
|
|
4477
|
-
if (renderer) return renderer(node, indent);
|
|
4478
|
-
return node.content ? renderChildren(node, indent) : "";
|
|
4479
|
-
}
|
|
4480
|
-
function renderNodes(nodes, indent) {
|
|
4481
|
-
return nodes.map((node) => renderNode(node, indent)).join("");
|
|
4482
|
-
}
|
|
4483
|
-
function isListNode(node) {
|
|
4484
|
-
return node.type === "orderedList" || node.type === "bulletList";
|
|
4485
|
-
}
|
|
4486
|
-
function renderListChild(child, indent, pad, marker, isFirst) {
|
|
4487
|
-
if (isListNode(child)) return renderNodes([child], indent + 1);
|
|
4488
|
-
if (child.type !== "paragraph") return renderNode(child, indent);
|
|
4489
|
-
const text = renderChildren(child, indent);
|
|
4490
|
-
return isFirst ? `${pad}${marker} ${text}` : `${pad} ${text}`;
|
|
4491
|
-
}
|
|
4492
|
-
function renderListItem(node, indent, marker) {
|
|
4493
|
-
const pad = " ".repeat(indent);
|
|
4494
|
-
return (node.content ?? []).map((child, i) => renderListChild(child, indent, pad, marker, i === 0)).join("\n");
|
|
4495
|
-
}
|
|
4496
|
-
function adfToText(doc) {
|
|
4497
|
-
return renderNodes([doc], 0);
|
|
4498
|
-
}
|
|
4499
|
-
|
|
4500
|
-
// src/commands/jira/fetchIssue.ts
|
|
4501
|
-
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";
|
|
4502
4444
|
import chalk48 from "chalk";
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
const stderr = error.stderr;
|
|
4513
|
-
if (stderr.includes("unauthorized")) {
|
|
4514
|
-
console.error(
|
|
4515
|
-
chalk48.red("Jira authentication expired."),
|
|
4516
|
-
"Run",
|
|
4517
|
-
chalk48.cyan("assist jira auth"),
|
|
4518
|
-
"to re-authenticate."
|
|
4519
|
-
);
|
|
4520
|
-
process.exit(1);
|
|
4521
|
-
}
|
|
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;
|
|
4522
4454
|
}
|
|
4523
|
-
|
|
4524
|
-
process.exit(1);
|
|
4455
|
+
current = path21.dirname(current);
|
|
4525
4456
|
}
|
|
4526
|
-
return
|
|
4457
|
+
return null;
|
|
4527
4458
|
}
|
|
4528
4459
|
|
|
4529
|
-
// src/commands/
|
|
4530
|
-
var
|
|
4531
|
-
function
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
console.log(chalk49.yellow(`No acceptance criteria found on ${issueKey}.`));
|
|
4538
|
-
return;
|
|
4539
|
-
}
|
|
4540
|
-
if (typeof acValue === "string") {
|
|
4541
|
-
console.log(acValue);
|
|
4542
|
-
return;
|
|
4543
|
-
}
|
|
4544
|
-
if (acValue.type === "doc") {
|
|
4545
|
-
console.log(adfToText(acValue));
|
|
4546
|
-
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;
|
|
4547
4468
|
}
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
// src/commands/jira/jiraAuth.ts
|
|
4552
|
-
import { execSync as execSync21 } from "child_process";
|
|
4553
|
-
import Enquirer from "enquirer";
|
|
4554
|
-
|
|
4555
|
-
// src/shared/loadJson.ts
|
|
4556
|
-
import { existsSync as existsSync20, mkdirSync as mkdirSync5, readFileSync as readFileSync18, writeFileSync as writeFileSync17 } from "fs";
|
|
4557
|
-
import { homedir as homedir6 } from "os";
|
|
4558
|
-
import { join as join16 } from "path";
|
|
4559
|
-
function getStoreDir() {
|
|
4560
|
-
return join16(homedir6(), ".assist");
|
|
4561
|
-
}
|
|
4562
|
-
function getStorePath(filename) {
|
|
4563
|
-
return join16(getStoreDir(), filename);
|
|
4564
|
-
}
|
|
4565
|
-
function loadJson(filename) {
|
|
4566
|
-
const path42 = getStorePath(filename);
|
|
4567
|
-
if (existsSync20(path42)) {
|
|
4469
|
+
for (const file of files) {
|
|
4470
|
+
if (!file.toLowerCase().endsWith(".dll")) continue;
|
|
4471
|
+
const dllPath = join16(debugDir, file);
|
|
4568
4472
|
try {
|
|
4569
|
-
|
|
4473
|
+
const fd = openSync(dllPath, "r+");
|
|
4474
|
+
closeSync(fd);
|
|
4570
4475
|
} catch {
|
|
4571
|
-
return
|
|
4476
|
+
return dllPath;
|
|
4572
4477
|
}
|
|
4573
4478
|
}
|
|
4574
|
-
return
|
|
4575
|
-
}
|
|
4576
|
-
function saveJson(filename, data) {
|
|
4577
|
-
const dir = getStoreDir();
|
|
4578
|
-
if (!existsSync20(dir)) {
|
|
4579
|
-
mkdirSync5(dir, { recursive: true });
|
|
4580
|
-
}
|
|
4581
|
-
writeFileSync17(getStorePath(filename), JSON.stringify(data, null, 2));
|
|
4582
|
-
}
|
|
4583
|
-
|
|
4584
|
-
// src/commands/jira/jiraAuth.ts
|
|
4585
|
-
var CONFIG_FILE = "jira.json";
|
|
4586
|
-
async function promptCredentials(config) {
|
|
4587
|
-
const { Input: Input2, Password } = Enquirer;
|
|
4588
|
-
const site = await new Input2({
|
|
4589
|
-
name: "site",
|
|
4590
|
-
message: "Jira site (e.g., mycompany.atlassian.net):",
|
|
4591
|
-
initial: config.site
|
|
4592
|
-
}).run();
|
|
4593
|
-
const email = await new Input2({
|
|
4594
|
-
name: "email",
|
|
4595
|
-
message: "Email:",
|
|
4596
|
-
initial: config.email
|
|
4597
|
-
}).run();
|
|
4598
|
-
const token = await new Password({
|
|
4599
|
-
name: "token",
|
|
4600
|
-
message: "API token (https://id.atlassian.com/manage-profile/security/api-tokens):"
|
|
4601
|
-
}).run();
|
|
4602
|
-
return { site, email, token };
|
|
4479
|
+
return null;
|
|
4603
4480
|
}
|
|
4604
|
-
|
|
4605
|
-
|
|
4481
|
+
function findFirstLockedDll(dir) {
|
|
4482
|
+
let entries;
|
|
4606
4483
|
try {
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
process.exit(1);
|
|
4611
|
-
}
|
|
4612
|
-
execSync21(`acli jira auth login --site ${site} --email "${email}" --token`, {
|
|
4613
|
-
encoding: "utf-8",
|
|
4614
|
-
input: token,
|
|
4615
|
-
stdio: ["pipe", "inherit", "inherit"]
|
|
4616
|
-
});
|
|
4617
|
-
saveJson(CONFIG_FILE, { site, email });
|
|
4618
|
-
console.log("Successfully authenticated with Jira.");
|
|
4619
|
-
} catch (error) {
|
|
4620
|
-
if (error instanceof Error) {
|
|
4621
|
-
console.error("Error authenticating with Jira:", error.message);
|
|
4622
|
-
}
|
|
4623
|
-
process.exit(1);
|
|
4484
|
+
entries = readdirSync2(dir);
|
|
4485
|
+
} catch {
|
|
4486
|
+
return null;
|
|
4624
4487
|
}
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
import chalk50 from "chalk";
|
|
4629
|
-
function viewIssue(issueKey) {
|
|
4630
|
-
const parsed = fetchIssue(issueKey, "summary,description");
|
|
4631
|
-
const fields = parsed?.fields;
|
|
4632
|
-
const summary = fields?.summary;
|
|
4633
|
-
const description = fields?.description;
|
|
4634
|
-
if (summary) {
|
|
4635
|
-
console.log(chalk50.bold(summary));
|
|
4488
|
+
if (entries.includes("bin")) {
|
|
4489
|
+
const locked = isLockedDll(join16(dir, "bin", "Debug"));
|
|
4490
|
+
if (locked) return locked;
|
|
4636
4491
|
}
|
|
4637
|
-
|
|
4638
|
-
if (
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
console.log(adfToText(description));
|
|
4643
|
-
} else {
|
|
4644
|
-
console.log(JSON.stringify(description, null, 2));
|
|
4645
|
-
}
|
|
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;
|
|
4646
4497
|
}
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
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
|
|
4650
4508
|
);
|
|
4509
|
+
process.exit(1);
|
|
4651
4510
|
}
|
|
4652
4511
|
}
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
const jiraCommand = program2.command("jira").description("Jira utilities");
|
|
4657
|
-
jiraCommand.command("auth").description("Authenticate with Jira via API token").action(() => jiraAuth());
|
|
4658
|
-
jiraCommand.command("ac <issue-key>").description("Print acceptance criteria for a Jira issue").action((issueKey) => acceptanceCriteria(issueKey));
|
|
4659
|
-
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"));
|
|
4660
4515
|
}
|
|
4661
4516
|
|
|
4662
|
-
// src/commands/
|
|
4663
|
-
import { readFileSync as
|
|
4664
|
-
import
|
|
4517
|
+
// src/commands/dotnet/buildTree.ts
|
|
4518
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
4519
|
+
import path22 from "path";
|
|
4665
4520
|
var PROJECT_REF_RE = /<ProjectReference\s+Include="([^"]+)"/g;
|
|
4666
4521
|
function getProjectRefs(csprojPath) {
|
|
4667
|
-
const content =
|
|
4522
|
+
const content = readFileSync18(csprojPath, "utf-8");
|
|
4668
4523
|
const refs = [];
|
|
4669
4524
|
for (const match of content.matchAll(PROJECT_REF_RE)) {
|
|
4670
4525
|
refs.push(match[1].replace(/\\/g, "/"));
|
|
@@ -4672,16 +4527,16 @@ function getProjectRefs(csprojPath) {
|
|
|
4672
4527
|
return refs;
|
|
4673
4528
|
}
|
|
4674
4529
|
function buildTree(csprojPath, repoRoot, visited = /* @__PURE__ */ new Set()) {
|
|
4675
|
-
const abs =
|
|
4676
|
-
const rel =
|
|
4530
|
+
const abs = path22.resolve(csprojPath);
|
|
4531
|
+
const rel = path22.relative(repoRoot, abs);
|
|
4677
4532
|
const node = { path: abs, relativePath: rel, children: [] };
|
|
4678
4533
|
if (visited.has(abs)) return node;
|
|
4679
4534
|
visited.add(abs);
|
|
4680
|
-
const dir =
|
|
4535
|
+
const dir = path22.dirname(abs);
|
|
4681
4536
|
for (const ref of getProjectRefs(abs)) {
|
|
4682
|
-
const childAbs =
|
|
4537
|
+
const childAbs = path22.resolve(dir, ref);
|
|
4683
4538
|
try {
|
|
4684
|
-
|
|
4539
|
+
readFileSync18(childAbs);
|
|
4685
4540
|
node.children.push(buildTree(childAbs, repoRoot, visited));
|
|
4686
4541
|
} catch {
|
|
4687
4542
|
node.children.push({
|
|
@@ -4705,22 +4560,22 @@ function collectAllDeps(node) {
|
|
|
4705
4560
|
return result;
|
|
4706
4561
|
}
|
|
4707
4562
|
|
|
4708
|
-
// src/commands/
|
|
4709
|
-
import { readdirSync as
|
|
4710
|
-
import
|
|
4563
|
+
// src/commands/dotnet/findContainingSolutions.ts
|
|
4564
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync19, statSync } from "fs";
|
|
4565
|
+
import path23 from "path";
|
|
4711
4566
|
function findSlnFiles(dir, maxDepth, depth = 0) {
|
|
4712
4567
|
if (depth > maxDepth) return [];
|
|
4713
4568
|
const results = [];
|
|
4714
4569
|
let entries;
|
|
4715
4570
|
try {
|
|
4716
|
-
entries =
|
|
4571
|
+
entries = readdirSync3(dir);
|
|
4717
4572
|
} catch {
|
|
4718
4573
|
return results;
|
|
4719
4574
|
}
|
|
4720
4575
|
for (const entry of entries) {
|
|
4721
4576
|
if (entry.startsWith(".") || entry === "node_modules" || entry === "packages")
|
|
4722
4577
|
continue;
|
|
4723
|
-
const full =
|
|
4578
|
+
const full = path23.join(dir, entry);
|
|
4724
4579
|
try {
|
|
4725
4580
|
const stat = statSync(full);
|
|
4726
4581
|
if (stat.isFile() && entry.endsWith(".sln")) {
|
|
@@ -4734,16 +4589,16 @@ function findSlnFiles(dir, maxDepth, depth = 0) {
|
|
|
4734
4589
|
return results;
|
|
4735
4590
|
}
|
|
4736
4591
|
function findContainingSolutions(csprojPath, repoRoot) {
|
|
4737
|
-
const csprojAbs =
|
|
4738
|
-
const csprojBasename =
|
|
4592
|
+
const csprojAbs = path23.resolve(csprojPath);
|
|
4593
|
+
const csprojBasename = path23.basename(csprojAbs);
|
|
4739
4594
|
const slnFiles = findSlnFiles(repoRoot, 3);
|
|
4740
4595
|
const matches = [];
|
|
4741
4596
|
const pattern2 = new RegExp(`[\\\\"/]${escapeRegex(csprojBasename)}"`);
|
|
4742
4597
|
for (const sln of slnFiles) {
|
|
4743
4598
|
try {
|
|
4744
|
-
const content =
|
|
4599
|
+
const content = readFileSync19(sln, "utf-8");
|
|
4745
4600
|
if (pattern2.test(content)) {
|
|
4746
|
-
matches.push(
|
|
4601
|
+
matches.push(path23.relative(repoRoot, sln));
|
|
4747
4602
|
}
|
|
4748
4603
|
} catch {
|
|
4749
4604
|
}
|
|
@@ -4754,31 +4609,31 @@ function escapeRegex(s) {
|
|
|
4754
4609
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4755
4610
|
}
|
|
4756
4611
|
|
|
4757
|
-
// src/commands/
|
|
4758
|
-
import
|
|
4612
|
+
// src/commands/dotnet/printTree.ts
|
|
4613
|
+
import chalk49 from "chalk";
|
|
4759
4614
|
function printNodes(nodes, prefix2) {
|
|
4760
4615
|
for (let i = 0; i < nodes.length; i++) {
|
|
4761
4616
|
const isLast = i === nodes.length - 1;
|
|
4762
4617
|
const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
4763
4618
|
const childPrefix = isLast ? " " : "\u2502 ";
|
|
4764
4619
|
const isMissing = nodes[i].relativePath.startsWith("[MISSING]");
|
|
4765
|
-
const label2 = isMissing ?
|
|
4620
|
+
const label2 = isMissing ? chalk49.red(nodes[i].relativePath) : nodes[i].relativePath;
|
|
4766
4621
|
console.log(`${prefix2}${connector}${label2}`);
|
|
4767
4622
|
printNodes(nodes[i].children, prefix2 + childPrefix);
|
|
4768
4623
|
}
|
|
4769
4624
|
}
|
|
4770
4625
|
function printTree(tree, totalCount, solutions) {
|
|
4771
|
-
console.log(
|
|
4772
|
-
console.log(
|
|
4626
|
+
console.log(chalk49.bold("\nProject Dependency Tree"));
|
|
4627
|
+
console.log(chalk49.cyan(tree.relativePath));
|
|
4773
4628
|
printNodes(tree.children, "");
|
|
4774
|
-
console.log(
|
|
4629
|
+
console.log(chalk49.dim(`
|
|
4775
4630
|
${totalCount} projects total (including root)`));
|
|
4776
|
-
console.log(
|
|
4631
|
+
console.log(chalk49.bold("\nSolution Membership"));
|
|
4777
4632
|
if (solutions.length === 0) {
|
|
4778
|
-
console.log(
|
|
4633
|
+
console.log(chalk49.yellow(" Not found in any .sln"));
|
|
4779
4634
|
} else {
|
|
4780
4635
|
for (const sln of solutions) {
|
|
4781
|
-
console.log(` ${
|
|
4636
|
+
console.log(` ${chalk49.green(sln)}`);
|
|
4782
4637
|
}
|
|
4783
4638
|
}
|
|
4784
4639
|
console.log();
|
|
@@ -4804,76 +4659,504 @@ function printJson(tree, totalCount, solutions) {
|
|
|
4804
4659
|
);
|
|
4805
4660
|
}
|
|
4806
4661
|
|
|
4807
|
-
// src/commands/
|
|
4808
|
-
import { existsSync as
|
|
4662
|
+
// src/commands/dotnet/resolveCsproj.ts
|
|
4663
|
+
import { existsSync as existsSync21 } from "fs";
|
|
4809
4664
|
import path24 from "path";
|
|
4665
|
+
import chalk50 from "chalk";
|
|
4666
|
+
function resolveCsproj(csprojPath) {
|
|
4667
|
+
const resolved = path24.resolve(csprojPath);
|
|
4668
|
+
if (!existsSync21(resolved)) {
|
|
4669
|
+
console.error(chalk50.red(`File not found: ${resolved}`));
|
|
4670
|
+
process.exit(1);
|
|
4671
|
+
}
|
|
4672
|
+
const repoRoot = findRepoRoot(path24.dirname(resolved));
|
|
4673
|
+
if (!repoRoot) {
|
|
4674
|
+
console.error(chalk50.red("Could not find git repository root"));
|
|
4675
|
+
process.exit(1);
|
|
4676
|
+
}
|
|
4677
|
+
return { resolved, repoRoot };
|
|
4678
|
+
}
|
|
4679
|
+
|
|
4680
|
+
// src/commands/dotnet/deps.ts
|
|
4681
|
+
async function deps(csprojPath, options2) {
|
|
4682
|
+
const { resolved, repoRoot } = resolveCsproj(csprojPath);
|
|
4683
|
+
const tree = buildTree(resolved, repoRoot);
|
|
4684
|
+
const totalCount = collectAllDeps(tree).size + 1;
|
|
4685
|
+
const solutions = findContainingSolutions(resolved, repoRoot);
|
|
4686
|
+
if (options2.json) {
|
|
4687
|
+
printJson(tree, totalCount, solutions);
|
|
4688
|
+
} else {
|
|
4689
|
+
printTree(tree, totalCount, solutions);
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
|
|
4693
|
+
// src/commands/dotnet/inSln.ts
|
|
4694
|
+
import chalk51 from "chalk";
|
|
4695
|
+
async function inSln(csprojPath) {
|
|
4696
|
+
const { resolved, repoRoot } = resolveCsproj(csprojPath);
|
|
4697
|
+
const solutions = findContainingSolutions(resolved, repoRoot);
|
|
4698
|
+
if (solutions.length === 0) {
|
|
4699
|
+
console.log(chalk51.yellow("Not found in any .sln file"));
|
|
4700
|
+
process.exit(1);
|
|
4701
|
+
}
|
|
4702
|
+
for (const sln of solutions) {
|
|
4703
|
+
console.log(sln);
|
|
4704
|
+
}
|
|
4705
|
+
}
|
|
4706
|
+
|
|
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`;
|
|
4719
|
+
}
|
|
4720
|
+
|
|
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
|
|
4810
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
|
+
}
|
|
4811
4775
|
|
|
4812
|
-
// src/
|
|
4813
|
-
import {
|
|
4814
|
-
import
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
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);
|
|
4820
4801
|
}
|
|
4821
|
-
current
|
|
4802
|
+
if (current === ceiling) break;
|
|
4803
|
+
current = dirname16(current);
|
|
4822
4804
|
}
|
|
4823
|
-
|
|
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 };
|
|
4824
5099
|
}
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
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
|
+
}
|
|
4836
5119
|
process.exit(1);
|
|
4837
5120
|
}
|
|
4838
|
-
return { resolved, repoRoot };
|
|
4839
5121
|
}
|
|
4840
5122
|
|
|
4841
|
-
// src/commands/
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
const
|
|
4845
|
-
const
|
|
4846
|
-
const
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
printTree(tree, totalCount, solutions);
|
|
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));
|
|
4851
5132
|
}
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
process.exit(1);
|
|
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
|
+
}
|
|
4862
5142
|
}
|
|
4863
|
-
|
|
4864
|
-
console.log(
|
|
5143
|
+
if (!summary && !description) {
|
|
5144
|
+
console.log(
|
|
5145
|
+
chalk58.yellow(`No summary or description found on ${issueKey}.`)
|
|
5146
|
+
);
|
|
4865
5147
|
}
|
|
4866
5148
|
}
|
|
4867
5149
|
|
|
4868
|
-
// src/commands/
|
|
4869
|
-
function
|
|
4870
|
-
const
|
|
4871
|
-
|
|
4872
|
-
|
|
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));
|
|
4873
5156
|
}
|
|
4874
5157
|
|
|
4875
5158
|
// src/commands/news/add/index.ts
|
|
4876
|
-
import
|
|
5159
|
+
import chalk59 from "chalk";
|
|
4877
5160
|
import enquirer5 from "enquirer";
|
|
4878
5161
|
async function add2(url) {
|
|
4879
5162
|
if (!url) {
|
|
@@ -4896,17 +5179,17 @@ async function add2(url) {
|
|
|
4896
5179
|
const news = config.news ?? {};
|
|
4897
5180
|
const feeds = news.feeds ?? [];
|
|
4898
5181
|
if (feeds.includes(url)) {
|
|
4899
|
-
console.log(
|
|
5182
|
+
console.log(chalk59.yellow("Feed already exists in config"));
|
|
4900
5183
|
return;
|
|
4901
5184
|
}
|
|
4902
5185
|
feeds.push(url);
|
|
4903
5186
|
config.news = { ...news, feeds };
|
|
4904
5187
|
saveGlobalConfig(config);
|
|
4905
|
-
console.log(
|
|
5188
|
+
console.log(chalk59.green(`Added feed: ${url}`));
|
|
4906
5189
|
}
|
|
4907
5190
|
|
|
4908
5191
|
// src/commands/news/web/handleRequest.ts
|
|
4909
|
-
import
|
|
5192
|
+
import chalk60 from "chalk";
|
|
4910
5193
|
|
|
4911
5194
|
// src/commands/news/web/shared.ts
|
|
4912
5195
|
import { decodeHTML } from "entities";
|
|
@@ -5042,17 +5325,17 @@ function prefetch() {
|
|
|
5042
5325
|
const config = loadConfig();
|
|
5043
5326
|
const total = config.news.feeds.length;
|
|
5044
5327
|
if (total === 0) return;
|
|
5045
|
-
process.stdout.write(
|
|
5328
|
+
process.stdout.write(chalk60.dim(`Fetching ${total} feed(s)\u2026 `));
|
|
5046
5329
|
prefetchPromise = fetchFeeds(config.news.feeds, (done2, t) => {
|
|
5047
5330
|
const width = 20;
|
|
5048
5331
|
const filled = Math.round(done2 / t * width);
|
|
5049
5332
|
const bar = `${"\u2588".repeat(filled)}${"\u2591".repeat(width - filled)}`;
|
|
5050
5333
|
process.stdout.write(
|
|
5051
|
-
`\r${
|
|
5334
|
+
`\r${chalk60.dim(`Fetching feeds ${bar} ${done2}/${t}`)}`
|
|
5052
5335
|
);
|
|
5053
5336
|
}).then((items) => {
|
|
5054
5337
|
process.stdout.write(
|
|
5055
|
-
`\r${
|
|
5338
|
+
`\r${chalk60.green(`Fetched ${items.length} items from ${total} feed(s)`)}
|
|
5056
5339
|
`
|
|
5057
5340
|
);
|
|
5058
5341
|
cachedItems = items;
|
|
@@ -5098,12 +5381,12 @@ function registerNews(program2) {
|
|
|
5098
5381
|
|
|
5099
5382
|
// src/commands/prs/comment.ts
|
|
5100
5383
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
5101
|
-
import { unlinkSync as
|
|
5102
|
-
import { tmpdir as
|
|
5103
|
-
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";
|
|
5104
5387
|
|
|
5105
5388
|
// src/commands/prs/shared.ts
|
|
5106
|
-
import { execSync as
|
|
5389
|
+
import { execSync as execSync24 } from "child_process";
|
|
5107
5390
|
function isGhNotInstalled(error) {
|
|
5108
5391
|
if (error instanceof Error) {
|
|
5109
5392
|
const msg = error.message.toLowerCase();
|
|
@@ -5119,14 +5402,14 @@ function isNotFound(error) {
|
|
|
5119
5402
|
}
|
|
5120
5403
|
function getRepoInfo() {
|
|
5121
5404
|
const repoInfo = JSON.parse(
|
|
5122
|
-
|
|
5405
|
+
execSync24("gh repo view --json owner,name", { encoding: "utf-8" })
|
|
5123
5406
|
);
|
|
5124
5407
|
return { org: repoInfo.owner.login, repo: repoInfo.name };
|
|
5125
5408
|
}
|
|
5126
5409
|
function getCurrentPrNumber() {
|
|
5127
5410
|
try {
|
|
5128
5411
|
const prInfo = JSON.parse(
|
|
5129
|
-
|
|
5412
|
+
execSync24("gh pr view --json number", { encoding: "utf-8" })
|
|
5130
5413
|
);
|
|
5131
5414
|
return prInfo.number;
|
|
5132
5415
|
} catch (error) {
|
|
@@ -5140,7 +5423,7 @@ function getCurrentPrNumber() {
|
|
|
5140
5423
|
function getCurrentPrNodeId() {
|
|
5141
5424
|
try {
|
|
5142
5425
|
const prInfo = JSON.parse(
|
|
5143
|
-
|
|
5426
|
+
execSync24("gh pr view --json id", { encoding: "utf-8" })
|
|
5144
5427
|
);
|
|
5145
5428
|
return prInfo.id;
|
|
5146
5429
|
} catch (error) {
|
|
@@ -5167,12 +5450,12 @@ function validateLine(line) {
|
|
|
5167
5450
|
process.exit(1);
|
|
5168
5451
|
}
|
|
5169
5452
|
}
|
|
5170
|
-
function comment(
|
|
5453
|
+
function comment(path44, line, body) {
|
|
5171
5454
|
validateBody(body);
|
|
5172
5455
|
validateLine(line);
|
|
5173
5456
|
try {
|
|
5174
5457
|
const prId = getCurrentPrNodeId();
|
|
5175
|
-
const queryFile =
|
|
5458
|
+
const queryFile = join19(tmpdir3(), `gh-query-${Date.now()}.graphql`);
|
|
5176
5459
|
writeFileSync18(queryFile, MUTATION);
|
|
5177
5460
|
try {
|
|
5178
5461
|
const result = spawnSync2(
|
|
@@ -5187,7 +5470,7 @@ function comment(path42, line, body) {
|
|
|
5187
5470
|
"-f",
|
|
5188
5471
|
`body=${body}`,
|
|
5189
5472
|
"-f",
|
|
5190
|
-
`path=${
|
|
5473
|
+
`path=${path44}`,
|
|
5191
5474
|
"-F",
|
|
5192
5475
|
`line=${line}`
|
|
5193
5476
|
],
|
|
@@ -5196,9 +5479,9 @@ function comment(path42, line, body) {
|
|
|
5196
5479
|
if (result.status !== 0) {
|
|
5197
5480
|
throw new Error(result.stderr || result.stdout);
|
|
5198
5481
|
}
|
|
5199
|
-
console.log(`Added review comment on ${
|
|
5482
|
+
console.log(`Added review comment on ${path44}:${line}`);
|
|
5200
5483
|
} finally {
|
|
5201
|
-
|
|
5484
|
+
unlinkSync4(queryFile);
|
|
5202
5485
|
}
|
|
5203
5486
|
} catch (error) {
|
|
5204
5487
|
if (isGhNotInstalled(error)) {
|
|
@@ -5211,55 +5494,55 @@ function comment(path42, line, body) {
|
|
|
5211
5494
|
}
|
|
5212
5495
|
|
|
5213
5496
|
// src/commands/prs/fixed.ts
|
|
5214
|
-
import { execSync as
|
|
5497
|
+
import { execSync as execSync26 } from "child_process";
|
|
5215
5498
|
|
|
5216
5499
|
// src/commands/prs/resolveCommentWithReply.ts
|
|
5217
|
-
import { execSync as
|
|
5218
|
-
import { unlinkSync as
|
|
5219
|
-
import { tmpdir as
|
|
5220
|
-
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";
|
|
5221
5504
|
|
|
5222
5505
|
// src/commands/prs/loadCommentsCache.ts
|
|
5223
|
-
import { existsSync as
|
|
5224
|
-
import { join as
|
|
5506
|
+
import { existsSync as existsSync25, readFileSync as readFileSync22, unlinkSync as unlinkSync5 } from "fs";
|
|
5507
|
+
import { join as join20 } from "path";
|
|
5225
5508
|
import { parse as parse2 } from "yaml";
|
|
5226
5509
|
function getCachePath(prNumber) {
|
|
5227
|
-
return
|
|
5510
|
+
return join20(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`);
|
|
5228
5511
|
}
|
|
5229
5512
|
function loadCommentsCache(prNumber) {
|
|
5230
5513
|
const cachePath = getCachePath(prNumber);
|
|
5231
|
-
if (!
|
|
5514
|
+
if (!existsSync25(cachePath)) {
|
|
5232
5515
|
return null;
|
|
5233
5516
|
}
|
|
5234
|
-
const content =
|
|
5517
|
+
const content = readFileSync22(cachePath, "utf-8");
|
|
5235
5518
|
return parse2(content);
|
|
5236
5519
|
}
|
|
5237
5520
|
function deleteCommentsCache(prNumber) {
|
|
5238
5521
|
const cachePath = getCachePath(prNumber);
|
|
5239
|
-
if (
|
|
5240
|
-
|
|
5522
|
+
if (existsSync25(cachePath)) {
|
|
5523
|
+
unlinkSync5(cachePath);
|
|
5241
5524
|
console.log("No more unresolved line comments. Cache dropped.");
|
|
5242
5525
|
}
|
|
5243
5526
|
}
|
|
5244
5527
|
|
|
5245
5528
|
// src/commands/prs/resolveCommentWithReply.ts
|
|
5246
5529
|
function replyToComment(org, repo, prNumber, commentId, message) {
|
|
5247
|
-
|
|
5530
|
+
execSync25(
|
|
5248
5531
|
`gh api repos/${org}/${repo}/pulls/${prNumber}/comments -f body="${message.replace(/"/g, '\\"')}" -F in_reply_to=${commentId}`,
|
|
5249
5532
|
{ stdio: ["inherit", "pipe", "inherit"] }
|
|
5250
5533
|
);
|
|
5251
5534
|
}
|
|
5252
5535
|
function resolveThread(threadId) {
|
|
5253
5536
|
const mutation = `mutation($threadId: ID!) { resolveReviewThread(input: {threadId: $threadId}) { thread { isResolved } } }`;
|
|
5254
|
-
const queryFile =
|
|
5537
|
+
const queryFile = join21(tmpdir4(), `gh-mutation-${Date.now()}.graphql`);
|
|
5255
5538
|
writeFileSync19(queryFile, mutation);
|
|
5256
5539
|
try {
|
|
5257
|
-
|
|
5540
|
+
execSync25(
|
|
5258
5541
|
`gh api graphql -F query=@${queryFile} -f threadId="${threadId}"`,
|
|
5259
5542
|
{ stdio: ["inherit", "pipe", "inherit"] }
|
|
5260
5543
|
);
|
|
5261
5544
|
} finally {
|
|
5262
|
-
|
|
5545
|
+
unlinkSync6(queryFile);
|
|
5263
5546
|
}
|
|
5264
5547
|
}
|
|
5265
5548
|
function requireCache(prNumber) {
|
|
@@ -5306,7 +5589,7 @@ function resolveCommentWithReply(commentId, message) {
|
|
|
5306
5589
|
// src/commands/prs/fixed.ts
|
|
5307
5590
|
function verifySha(sha) {
|
|
5308
5591
|
try {
|
|
5309
|
-
return
|
|
5592
|
+
return execSync26(`git rev-parse --verify ${sha}`, {
|
|
5310
5593
|
encoding: "utf-8"
|
|
5311
5594
|
}).trim();
|
|
5312
5595
|
} catch {
|
|
@@ -5320,7 +5603,7 @@ function fixed(commentId, sha) {
|
|
|
5320
5603
|
const { org, repo } = getRepoInfo();
|
|
5321
5604
|
const repoUrl = `https://github.com/${org}/${repo}`;
|
|
5322
5605
|
const message = `Fixed in [${fullSha}](${repoUrl}/commit/${fullSha})`;
|
|
5323
|
-
|
|
5606
|
+
execSync26("git push", { stdio: "inherit" });
|
|
5324
5607
|
resolveCommentWithReply(commentId, message);
|
|
5325
5608
|
} catch (error) {
|
|
5326
5609
|
if (isGhNotInstalled(error)) {
|
|
@@ -5333,21 +5616,21 @@ function fixed(commentId, sha) {
|
|
|
5333
5616
|
}
|
|
5334
5617
|
|
|
5335
5618
|
// src/commands/prs/listComments/index.ts
|
|
5336
|
-
import { existsSync as
|
|
5337
|
-
import { join as
|
|
5619
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync6, writeFileSync as writeFileSync21 } from "fs";
|
|
5620
|
+
import { join as join23 } from "path";
|
|
5338
5621
|
import { stringify } from "yaml";
|
|
5339
5622
|
|
|
5340
5623
|
// src/commands/prs/fetchThreadIds.ts
|
|
5341
|
-
import { execSync as
|
|
5342
|
-
import { unlinkSync as
|
|
5343
|
-
import { tmpdir as
|
|
5344
|
-
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";
|
|
5345
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 } } } } } } }`;
|
|
5346
5629
|
function fetchThreadIds(org, repo, prNumber) {
|
|
5347
|
-
const queryFile =
|
|
5630
|
+
const queryFile = join22(tmpdir5(), `gh-query-${Date.now()}.graphql`);
|
|
5348
5631
|
writeFileSync20(queryFile, THREAD_QUERY);
|
|
5349
5632
|
try {
|
|
5350
|
-
const result =
|
|
5633
|
+
const result = execSync27(
|
|
5351
5634
|
`gh api graphql -F query=@${queryFile} -F owner="${org}" -F repo="${repo}" -F prNumber=${prNumber}`,
|
|
5352
5635
|
{ encoding: "utf-8" }
|
|
5353
5636
|
);
|
|
@@ -5364,14 +5647,14 @@ function fetchThreadIds(org, repo, prNumber) {
|
|
|
5364
5647
|
}
|
|
5365
5648
|
return { threadMap, resolvedThreadIds };
|
|
5366
5649
|
} finally {
|
|
5367
|
-
|
|
5650
|
+
unlinkSync7(queryFile);
|
|
5368
5651
|
}
|
|
5369
5652
|
}
|
|
5370
5653
|
|
|
5371
5654
|
// src/commands/prs/listComments/fetchReviewComments.ts
|
|
5372
|
-
import { execSync as
|
|
5655
|
+
import { execSync as execSync28 } from "child_process";
|
|
5373
5656
|
function fetchJson(endpoint) {
|
|
5374
|
-
const result =
|
|
5657
|
+
const result = execSync28(`gh api --paginate ${endpoint}`, {
|
|
5375
5658
|
encoding: "utf-8"
|
|
5376
5659
|
});
|
|
5377
5660
|
if (!result.trim()) return [];
|
|
@@ -5413,20 +5696,20 @@ function fetchLineComments(org, repo, prNumber, threadInfo) {
|
|
|
5413
5696
|
}
|
|
5414
5697
|
|
|
5415
5698
|
// src/commands/prs/listComments/printComments.ts
|
|
5416
|
-
import
|
|
5699
|
+
import chalk61 from "chalk";
|
|
5417
5700
|
function formatForHuman(comment2) {
|
|
5418
5701
|
if (comment2.type === "review") {
|
|
5419
|
-
const stateColor = comment2.state === "APPROVED" ?
|
|
5702
|
+
const stateColor = comment2.state === "APPROVED" ? chalk61.green : comment2.state === "CHANGES_REQUESTED" ? chalk61.red : chalk61.yellow;
|
|
5420
5703
|
return [
|
|
5421
|
-
`${
|
|
5704
|
+
`${chalk61.cyan("Review")} by ${chalk61.bold(comment2.user)} ${stateColor(`[${comment2.state}]`)}`,
|
|
5422
5705
|
comment2.body,
|
|
5423
5706
|
""
|
|
5424
5707
|
].join("\n");
|
|
5425
5708
|
}
|
|
5426
5709
|
const location = comment2.line ? `:${comment2.line}` : "";
|
|
5427
5710
|
return [
|
|
5428
|
-
`${
|
|
5429
|
-
|
|
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")),
|
|
5430
5713
|
comment2.body,
|
|
5431
5714
|
""
|
|
5432
5715
|
].join("\n");
|
|
@@ -5458,8 +5741,8 @@ function printComments(result) {
|
|
|
5458
5741
|
|
|
5459
5742
|
// src/commands/prs/listComments/index.ts
|
|
5460
5743
|
function writeCommentsCache(prNumber, comments) {
|
|
5461
|
-
const assistDir =
|
|
5462
|
-
if (!
|
|
5744
|
+
const assistDir = join23(process.cwd(), ".assist");
|
|
5745
|
+
if (!existsSync26(assistDir)) {
|
|
5463
5746
|
mkdirSync6(assistDir, { recursive: true });
|
|
5464
5747
|
}
|
|
5465
5748
|
const cacheData = {
|
|
@@ -5467,7 +5750,7 @@ function writeCommentsCache(prNumber, comments) {
|
|
|
5467
5750
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5468
5751
|
comments
|
|
5469
5752
|
};
|
|
5470
|
-
const cachePath =
|
|
5753
|
+
const cachePath = join23(assistDir, `pr-${prNumber}-comments.yaml`);
|
|
5471
5754
|
writeFileSync21(cachePath, stringify(cacheData));
|
|
5472
5755
|
}
|
|
5473
5756
|
function handleKnownErrors(error) {
|
|
@@ -5500,7 +5783,7 @@ async function listComments() {
|
|
|
5500
5783
|
];
|
|
5501
5784
|
updateCache(prNumber, allComments);
|
|
5502
5785
|
const hasLineComments = allComments.some((c) => c.type === "line");
|
|
5503
|
-
const cachePath = hasLineComments ?
|
|
5786
|
+
const cachePath = hasLineComments ? join23(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`) : null;
|
|
5504
5787
|
return { comments: allComments, cachePath };
|
|
5505
5788
|
} catch (error) {
|
|
5506
5789
|
const handled = handleKnownErrors(error);
|
|
@@ -5510,19 +5793,19 @@ async function listComments() {
|
|
|
5510
5793
|
}
|
|
5511
5794
|
|
|
5512
5795
|
// src/commands/prs/prs/index.ts
|
|
5513
|
-
import { execSync as
|
|
5796
|
+
import { execSync as execSync29 } from "child_process";
|
|
5514
5797
|
|
|
5515
5798
|
// src/commands/prs/prs/displayPaginated/index.ts
|
|
5516
5799
|
import enquirer6 from "enquirer";
|
|
5517
5800
|
|
|
5518
5801
|
// src/commands/prs/prs/displayPaginated/printPr.ts
|
|
5519
|
-
import
|
|
5802
|
+
import chalk62 from "chalk";
|
|
5520
5803
|
var STATUS_MAP = {
|
|
5521
|
-
MERGED: (pr) => pr.mergedAt ? { label:
|
|
5522
|
-
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
|
|
5523
5806
|
};
|
|
5524
5807
|
function defaultStatus(pr) {
|
|
5525
|
-
return { label:
|
|
5808
|
+
return { label: chalk62.green("opened"), date: pr.createdAt };
|
|
5526
5809
|
}
|
|
5527
5810
|
function getStatus2(pr) {
|
|
5528
5811
|
return STATUS_MAP[pr.state]?.(pr) ?? defaultStatus(pr);
|
|
@@ -5531,11 +5814,11 @@ function formatDate(dateStr) {
|
|
|
5531
5814
|
return new Date(dateStr).toISOString().split("T")[0];
|
|
5532
5815
|
}
|
|
5533
5816
|
function formatPrHeader(pr, status2) {
|
|
5534
|
-
return `${
|
|
5817
|
+
return `${chalk62.cyan(`#${pr.number}`)} ${pr.title} ${chalk62.dim(`(${pr.author.login},`)} ${status2.label} ${chalk62.dim(`${formatDate(status2.date)})`)}`;
|
|
5535
5818
|
}
|
|
5536
5819
|
function logPrDetails(pr) {
|
|
5537
5820
|
console.log(
|
|
5538
|
-
|
|
5821
|
+
chalk62.dim(` ${pr.changedFiles.toLocaleString()} files | ${pr.url}`)
|
|
5539
5822
|
);
|
|
5540
5823
|
console.log();
|
|
5541
5824
|
}
|
|
@@ -5616,7 +5899,7 @@ async function displayPaginated(pullRequests) {
|
|
|
5616
5899
|
async function prs(options2) {
|
|
5617
5900
|
const state = options2.open ? "open" : options2.closed ? "closed" : "all";
|
|
5618
5901
|
try {
|
|
5619
|
-
const result =
|
|
5902
|
+
const result = execSync29(
|
|
5620
5903
|
`gh pr list --state ${state} --json number,title,url,author,createdAt,mergedAt,closedAt,state,changedFiles --limit 100`,
|
|
5621
5904
|
{ encoding: "utf-8" }
|
|
5622
5905
|
);
|
|
@@ -5639,7 +5922,7 @@ async function prs(options2) {
|
|
|
5639
5922
|
}
|
|
5640
5923
|
|
|
5641
5924
|
// src/commands/prs/wontfix.ts
|
|
5642
|
-
import { execSync as
|
|
5925
|
+
import { execSync as execSync30 } from "child_process";
|
|
5643
5926
|
function validateReason(reason) {
|
|
5644
5927
|
const lowerReason = reason.toLowerCase();
|
|
5645
5928
|
if (lowerReason.includes("claude") || lowerReason.includes("opus")) {
|
|
@@ -5656,7 +5939,7 @@ function validateShaReferences(reason) {
|
|
|
5656
5939
|
const invalidShas = [];
|
|
5657
5940
|
for (const sha of shas) {
|
|
5658
5941
|
try {
|
|
5659
|
-
|
|
5942
|
+
execSync30(`git cat-file -t ${sha}`, { stdio: "pipe" });
|
|
5660
5943
|
} catch {
|
|
5661
5944
|
invalidShas.push(sha);
|
|
5662
5945
|
}
|
|
@@ -5695,13 +5978,13 @@ function registerPrs(program2) {
|
|
|
5695
5978
|
prsCommand.command("wontfix <comment-id> <reason>").description("Reply with reason and resolve thread").action((commentId, reason) => {
|
|
5696
5979
|
wontfix(Number.parseInt(commentId, 10), reason);
|
|
5697
5980
|
});
|
|
5698
|
-
prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((
|
|
5699
|
-
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);
|
|
5700
5983
|
});
|
|
5701
5984
|
}
|
|
5702
5985
|
|
|
5703
5986
|
// src/commands/ravendb/ravendbAuth.ts
|
|
5704
|
-
import
|
|
5987
|
+
import chalk67 from "chalk";
|
|
5705
5988
|
|
|
5706
5989
|
// src/commands/ravendb/loadConnections.ts
|
|
5707
5990
|
function loadConnections() {
|
|
@@ -5718,18 +6001,18 @@ function saveConnections(connections) {
|
|
|
5718
6001
|
}
|
|
5719
6002
|
|
|
5720
6003
|
// src/commands/ravendb/promptConnection.ts
|
|
5721
|
-
import
|
|
6004
|
+
import chalk65 from "chalk";
|
|
5722
6005
|
import Enquirer3 from "enquirer";
|
|
5723
6006
|
|
|
5724
6007
|
// src/commands/ravendb/selectOpSecret.ts
|
|
5725
|
-
import
|
|
6008
|
+
import chalk64 from "chalk";
|
|
5726
6009
|
import Enquirer2 from "enquirer";
|
|
5727
6010
|
|
|
5728
6011
|
// src/commands/ravendb/searchItems.ts
|
|
5729
|
-
import { execSync as
|
|
5730
|
-
import
|
|
6012
|
+
import { execSync as execSync31 } from "child_process";
|
|
6013
|
+
import chalk63 from "chalk";
|
|
5731
6014
|
function opExec(args) {
|
|
5732
|
-
return
|
|
6015
|
+
return execSync31(`op ${args}`, {
|
|
5733
6016
|
encoding: "utf-8",
|
|
5734
6017
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5735
6018
|
}).trim();
|
|
@@ -5740,7 +6023,7 @@ function searchItems(search) {
|
|
|
5740
6023
|
items = JSON.parse(opExec("item list --format=json"));
|
|
5741
6024
|
} catch {
|
|
5742
6025
|
console.error(
|
|
5743
|
-
|
|
6026
|
+
chalk63.red(
|
|
5744
6027
|
"Failed to search 1Password. Ensure the CLI is installed and you are signed in."
|
|
5745
6028
|
)
|
|
5746
6029
|
);
|
|
@@ -5754,7 +6037,7 @@ function getItemFields(itemId) {
|
|
|
5754
6037
|
const item = JSON.parse(opExec(`item get "${itemId}" --format=json`));
|
|
5755
6038
|
return item.fields.filter((f) => f.reference && f.label);
|
|
5756
6039
|
} catch {
|
|
5757
|
-
console.error(
|
|
6040
|
+
console.error(chalk63.red("Failed to get item details from 1Password."));
|
|
5758
6041
|
process.exit(1);
|
|
5759
6042
|
}
|
|
5760
6043
|
}
|
|
@@ -5773,7 +6056,7 @@ async function selectOpSecret(searchTerm) {
|
|
|
5773
6056
|
}).run();
|
|
5774
6057
|
const items = searchItems(search);
|
|
5775
6058
|
if (items.length === 0) {
|
|
5776
|
-
console.error(
|
|
6059
|
+
console.error(chalk64.red(`No items found matching "${search}".`));
|
|
5777
6060
|
process.exit(1);
|
|
5778
6061
|
}
|
|
5779
6062
|
const itemId = await selectOne(
|
|
@@ -5782,7 +6065,7 @@ async function selectOpSecret(searchTerm) {
|
|
|
5782
6065
|
);
|
|
5783
6066
|
const fields = getItemFields(itemId);
|
|
5784
6067
|
if (fields.length === 0) {
|
|
5785
|
-
console.error(
|
|
6068
|
+
console.error(chalk64.red("No fields with references found on this item."));
|
|
5786
6069
|
process.exit(1);
|
|
5787
6070
|
}
|
|
5788
6071
|
const ref = await selectOne(
|
|
@@ -5800,7 +6083,7 @@ async function promptConnection(existingNames) {
|
|
|
5800
6083
|
message: "Connection name:"
|
|
5801
6084
|
}).run();
|
|
5802
6085
|
if (existingNames.includes(name)) {
|
|
5803
|
-
console.error(
|
|
6086
|
+
console.error(chalk65.red(`Connection "${name}" already exists.`));
|
|
5804
6087
|
process.exit(1);
|
|
5805
6088
|
}
|
|
5806
6089
|
const url = await new Input2({
|
|
@@ -5812,22 +6095,22 @@ async function promptConnection(existingNames) {
|
|
|
5812
6095
|
message: "Database name:"
|
|
5813
6096
|
}).run();
|
|
5814
6097
|
if (!name || !url || !database) {
|
|
5815
|
-
console.error(
|
|
6098
|
+
console.error(chalk65.red("All fields are required."));
|
|
5816
6099
|
process.exit(1);
|
|
5817
6100
|
}
|
|
5818
6101
|
const apiKeyRef = await selectOpSecret();
|
|
5819
|
-
console.log(
|
|
6102
|
+
console.log(chalk65.dim(`Using: ${apiKeyRef}`));
|
|
5820
6103
|
return { name, url, database, apiKeyRef };
|
|
5821
6104
|
}
|
|
5822
6105
|
|
|
5823
6106
|
// src/commands/ravendb/ravendbSetConnection.ts
|
|
5824
|
-
import
|
|
6107
|
+
import chalk66 from "chalk";
|
|
5825
6108
|
function ravendbSetConnection(name) {
|
|
5826
6109
|
const raw = loadGlobalConfigRaw();
|
|
5827
6110
|
const ravendb = raw.ravendb ?? {};
|
|
5828
6111
|
const connections = ravendb.connections ?? [];
|
|
5829
6112
|
if (!connections.some((c) => c.name === name)) {
|
|
5830
|
-
console.error(
|
|
6113
|
+
console.error(chalk66.red(`Connection "${name}" not found.`));
|
|
5831
6114
|
console.error(
|
|
5832
6115
|
`Available: ${connections.map((c) => c.name).join(", ") || "(none)"}`
|
|
5833
6116
|
);
|
|
@@ -5849,7 +6132,7 @@ async function ravendbAuth(options2) {
|
|
|
5849
6132
|
}
|
|
5850
6133
|
for (const c of connections) {
|
|
5851
6134
|
console.log(
|
|
5852
|
-
`${
|
|
6135
|
+
`${chalk67.bold(c.name)} ${c.url} db=${c.database} key=${c.apiKeyRef}`
|
|
5853
6136
|
);
|
|
5854
6137
|
}
|
|
5855
6138
|
return;
|
|
@@ -5857,7 +6140,7 @@ async function ravendbAuth(options2) {
|
|
|
5857
6140
|
if (options2.remove) {
|
|
5858
6141
|
const filtered = connections.filter((c) => c.name !== options2.remove);
|
|
5859
6142
|
if (filtered.length === connections.length) {
|
|
5860
|
-
console.error(
|
|
6143
|
+
console.error(chalk67.red(`Connection "${options2.remove}" not found.`));
|
|
5861
6144
|
process.exit(1);
|
|
5862
6145
|
}
|
|
5863
6146
|
saveConnections(filtered);
|
|
@@ -5875,10 +6158,10 @@ async function ravendbAuth(options2) {
|
|
|
5875
6158
|
}
|
|
5876
6159
|
|
|
5877
6160
|
// src/commands/ravendb/ravendbCollections.ts
|
|
5878
|
-
import
|
|
6161
|
+
import chalk71 from "chalk";
|
|
5879
6162
|
|
|
5880
6163
|
// src/commands/ravendb/ravenFetch.ts
|
|
5881
|
-
import
|
|
6164
|
+
import chalk69 from "chalk";
|
|
5882
6165
|
|
|
5883
6166
|
// src/commands/ravendb/getAccessToken.ts
|
|
5884
6167
|
var OAUTH_URL = "https://amazon-useast-1-oauth.ravenhq.com/ApiKeys/OAuth/AccessToken";
|
|
@@ -5914,21 +6197,21 @@ ${errorText}`
|
|
|
5914
6197
|
}
|
|
5915
6198
|
|
|
5916
6199
|
// src/commands/ravendb/resolveOpSecret.ts
|
|
5917
|
-
import { execSync as
|
|
5918
|
-
import
|
|
6200
|
+
import { execSync as execSync32 } from "child_process";
|
|
6201
|
+
import chalk68 from "chalk";
|
|
5919
6202
|
function resolveOpSecret(reference) {
|
|
5920
6203
|
if (!reference.startsWith("op://")) {
|
|
5921
|
-
console.error(
|
|
6204
|
+
console.error(chalk68.red(`Invalid secret reference: must start with op://`));
|
|
5922
6205
|
process.exit(1);
|
|
5923
6206
|
}
|
|
5924
6207
|
try {
|
|
5925
|
-
return
|
|
6208
|
+
return execSync32(`op read "${reference}"`, {
|
|
5926
6209
|
encoding: "utf-8",
|
|
5927
6210
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5928
6211
|
}).trim();
|
|
5929
6212
|
} catch {
|
|
5930
6213
|
console.error(
|
|
5931
|
-
|
|
6214
|
+
chalk68.red(
|
|
5932
6215
|
"Failed to resolve secret reference. Ensure 1Password CLI is installed and you are signed in."
|
|
5933
6216
|
)
|
|
5934
6217
|
);
|
|
@@ -5937,10 +6220,10 @@ function resolveOpSecret(reference) {
|
|
|
5937
6220
|
}
|
|
5938
6221
|
|
|
5939
6222
|
// src/commands/ravendb/ravenFetch.ts
|
|
5940
|
-
async function ravenFetch(connection,
|
|
6223
|
+
async function ravenFetch(connection, path44) {
|
|
5941
6224
|
const apiKey = resolveOpSecret(connection.apiKeyRef);
|
|
5942
6225
|
let accessToken = await getAccessToken(apiKey);
|
|
5943
|
-
const url = `${connection.url}${
|
|
6226
|
+
const url = `${connection.url}${path44}`;
|
|
5944
6227
|
const headers = {
|
|
5945
6228
|
Authorization: `Bearer ${accessToken}`,
|
|
5946
6229
|
"Content-Type": "application/json"
|
|
@@ -5955,7 +6238,7 @@ async function ravenFetch(connection, path42) {
|
|
|
5955
6238
|
if (!response.ok) {
|
|
5956
6239
|
const body = await response.text();
|
|
5957
6240
|
console.error(
|
|
5958
|
-
|
|
6241
|
+
chalk69.red(`RavenDB error: ${response.status} ${response.statusText}`)
|
|
5959
6242
|
);
|
|
5960
6243
|
console.error(body.substring(0, 500));
|
|
5961
6244
|
process.exit(1);
|
|
@@ -5964,7 +6247,7 @@ async function ravenFetch(connection, path42) {
|
|
|
5964
6247
|
}
|
|
5965
6248
|
|
|
5966
6249
|
// src/commands/ravendb/resolveConnection.ts
|
|
5967
|
-
import
|
|
6250
|
+
import chalk70 from "chalk";
|
|
5968
6251
|
function loadRavendb() {
|
|
5969
6252
|
const raw = loadGlobalConfigRaw();
|
|
5970
6253
|
const ravendb = raw.ravendb;
|
|
@@ -5978,7 +6261,7 @@ function resolveConnection(name) {
|
|
|
5978
6261
|
const connectionName = name ?? defaultConnection;
|
|
5979
6262
|
if (!connectionName) {
|
|
5980
6263
|
console.error(
|
|
5981
|
-
|
|
6264
|
+
chalk70.red(
|
|
5982
6265
|
"No connection specified and no default set. Use assist ravendb set-connection <name> or pass a connection name."
|
|
5983
6266
|
)
|
|
5984
6267
|
);
|
|
@@ -5986,7 +6269,7 @@ function resolveConnection(name) {
|
|
|
5986
6269
|
}
|
|
5987
6270
|
const connection = connections.find((c) => c.name === connectionName);
|
|
5988
6271
|
if (!connection) {
|
|
5989
|
-
console.error(
|
|
6272
|
+
console.error(chalk70.red(`Connection "${connectionName}" not found.`));
|
|
5990
6273
|
console.error(
|
|
5991
6274
|
`Available: ${connections.map((c) => c.name).join(", ") || "(none)"}`
|
|
5992
6275
|
);
|
|
@@ -6017,29 +6300,29 @@ async function ravendbCollections(connectionName) {
|
|
|
6017
6300
|
return;
|
|
6018
6301
|
}
|
|
6019
6302
|
for (const c of collections) {
|
|
6020
|
-
console.log(`${
|
|
6303
|
+
console.log(`${chalk71.bold(c.Name)} ${c.CountOfDocuments} docs`);
|
|
6021
6304
|
}
|
|
6022
6305
|
}
|
|
6023
6306
|
|
|
6024
6307
|
// src/commands/ravendb/ravendbQuery.ts
|
|
6025
|
-
import
|
|
6308
|
+
import chalk73 from "chalk";
|
|
6026
6309
|
|
|
6027
6310
|
// src/commands/ravendb/fetchAllPages.ts
|
|
6028
|
-
import
|
|
6311
|
+
import chalk72 from "chalk";
|
|
6029
6312
|
|
|
6030
6313
|
// src/commands/ravendb/buildQueryPath.ts
|
|
6031
6314
|
function buildQueryPath(opts) {
|
|
6032
6315
|
const db = encodeURIComponent(opts.db);
|
|
6033
|
-
let
|
|
6316
|
+
let path44;
|
|
6034
6317
|
if (opts.collection) {
|
|
6035
|
-
|
|
6318
|
+
path44 = `/databases/${db}/indexes/dynamic/${encodeURIComponent(opts.collection)}?start=${opts.start}&pageSize=${opts.pageSize}&sort=${encodeURIComponent(opts.sort)}`;
|
|
6036
6319
|
} else {
|
|
6037
|
-
|
|
6320
|
+
path44 = `/databases/${db}/queries?start=${opts.start}&pageSize=${opts.pageSize}`;
|
|
6038
6321
|
}
|
|
6039
6322
|
if (opts.query) {
|
|
6040
|
-
|
|
6323
|
+
path44 += `&query=${encodeURIComponent(opts.query)}`;
|
|
6041
6324
|
}
|
|
6042
|
-
return
|
|
6325
|
+
return path44;
|
|
6043
6326
|
}
|
|
6044
6327
|
|
|
6045
6328
|
// src/commands/ravendb/fetchAllPages.ts
|
|
@@ -6048,7 +6331,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
6048
6331
|
let start3 = 0;
|
|
6049
6332
|
while (true) {
|
|
6050
6333
|
const effectivePageSize = opts.limit !== void 0 ? Math.min(opts.pageSize, opts.limit - allResults.length) : opts.pageSize;
|
|
6051
|
-
const
|
|
6334
|
+
const path44 = buildQueryPath({
|
|
6052
6335
|
db: connection.database,
|
|
6053
6336
|
collection: opts.collection,
|
|
6054
6337
|
start: start3,
|
|
@@ -6056,14 +6339,14 @@ async function fetchAllPages(connection, opts) {
|
|
|
6056
6339
|
sort: opts.sort,
|
|
6057
6340
|
query: opts.query
|
|
6058
6341
|
});
|
|
6059
|
-
const data = await ravenFetch(connection,
|
|
6342
|
+
const data = await ravenFetch(connection, path44);
|
|
6060
6343
|
const results = data.Results ?? [];
|
|
6061
6344
|
const totalResults = data.TotalResults ?? 0;
|
|
6062
6345
|
if (results.length === 0) break;
|
|
6063
6346
|
allResults.push(...results);
|
|
6064
6347
|
start3 += results.length;
|
|
6065
6348
|
process.stderr.write(
|
|
6066
|
-
`\r${
|
|
6349
|
+
`\r${chalk72.dim(`Fetched ${allResults.length}/${totalResults}`)}`
|
|
6067
6350
|
);
|
|
6068
6351
|
if (start3 >= totalResults) break;
|
|
6069
6352
|
if (opts.limit !== void 0 && allResults.length >= opts.limit) break;
|
|
@@ -6078,7 +6361,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
6078
6361
|
async function ravendbQuery(connectionName, collection, options2) {
|
|
6079
6362
|
const resolved = resolveArgs(connectionName, collection);
|
|
6080
6363
|
if (!resolved.collection && !options2.query) {
|
|
6081
|
-
console.error(
|
|
6364
|
+
console.error(chalk73.red("Provide a collection name or --query filter."));
|
|
6082
6365
|
process.exit(1);
|
|
6083
6366
|
}
|
|
6084
6367
|
const { collection: col } = resolved;
|
|
@@ -6110,10 +6393,10 @@ function registerRavendb(program2) {
|
|
|
6110
6393
|
|
|
6111
6394
|
// src/commands/refactor/check/index.ts
|
|
6112
6395
|
import { spawn as spawn3 } from "child_process";
|
|
6113
|
-
import * as
|
|
6396
|
+
import * as path27 from "path";
|
|
6114
6397
|
|
|
6115
6398
|
// src/commands/refactor/logViolations.ts
|
|
6116
|
-
import
|
|
6399
|
+
import chalk74 from "chalk";
|
|
6117
6400
|
var DEFAULT_MAX_LINES = 100;
|
|
6118
6401
|
function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
|
|
6119
6402
|
if (violations.length === 0) {
|
|
@@ -6122,43 +6405,43 @@ function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
|
|
|
6122
6405
|
}
|
|
6123
6406
|
return;
|
|
6124
6407
|
}
|
|
6125
|
-
console.error(
|
|
6408
|
+
console.error(chalk74.red(`
|
|
6126
6409
|
Refactor check failed:
|
|
6127
6410
|
`));
|
|
6128
|
-
console.error(
|
|
6411
|
+
console.error(chalk74.red(` The following files exceed ${maxLines} lines:
|
|
6129
6412
|
`));
|
|
6130
6413
|
for (const violation of violations) {
|
|
6131
|
-
console.error(
|
|
6414
|
+
console.error(chalk74.red(` ${violation.file} (${violation.lines} lines)`));
|
|
6132
6415
|
}
|
|
6133
6416
|
console.error(
|
|
6134
|
-
|
|
6417
|
+
chalk74.yellow(
|
|
6135
6418
|
`
|
|
6136
6419
|
Each file needs to be sensibly refactored, or if there is no sensible
|
|
6137
6420
|
way to refactor it, ignore it with:
|
|
6138
6421
|
`
|
|
6139
6422
|
)
|
|
6140
6423
|
);
|
|
6141
|
-
console.error(
|
|
6424
|
+
console.error(chalk74.gray(` assist refactor ignore <file>
|
|
6142
6425
|
`));
|
|
6143
6426
|
if (process.env.CLAUDECODE) {
|
|
6144
|
-
console.error(
|
|
6427
|
+
console.error(chalk74.cyan(`
|
|
6145
6428
|
## Extracting Code to New Files
|
|
6146
6429
|
`));
|
|
6147
6430
|
console.error(
|
|
6148
|
-
|
|
6431
|
+
chalk74.cyan(
|
|
6149
6432
|
` When extracting logic from one file to another, consider where the extracted code belongs:
|
|
6150
6433
|
`
|
|
6151
6434
|
)
|
|
6152
6435
|
);
|
|
6153
6436
|
console.error(
|
|
6154
|
-
|
|
6437
|
+
chalk74.cyan(
|
|
6155
6438
|
` 1. Keep related logic together: If the extracted code is tightly coupled to the
|
|
6156
6439
|
original file's domain, create a new folder containing both the original and extracted files.
|
|
6157
6440
|
`
|
|
6158
6441
|
)
|
|
6159
6442
|
);
|
|
6160
6443
|
console.error(
|
|
6161
|
-
|
|
6444
|
+
chalk74.cyan(
|
|
6162
6445
|
` 2. Share common utilities: If the extracted code can be reused across multiple
|
|
6163
6446
|
domains, move it to a common/shared folder.
|
|
6164
6447
|
`
|
|
@@ -6168,7 +6451,7 @@ Refactor check failed:
|
|
|
6168
6451
|
}
|
|
6169
6452
|
|
|
6170
6453
|
// src/commands/refactor/check/getViolations/index.ts
|
|
6171
|
-
import { execSync as
|
|
6454
|
+
import { execSync as execSync33 } from "child_process";
|
|
6172
6455
|
import fs16 from "fs";
|
|
6173
6456
|
import { minimatch as minimatch4 } from "minimatch";
|
|
6174
6457
|
|
|
@@ -6218,7 +6501,7 @@ function getGitFiles(options2) {
|
|
|
6218
6501
|
}
|
|
6219
6502
|
const files = /* @__PURE__ */ new Set();
|
|
6220
6503
|
if (options2.staged || options2.modified) {
|
|
6221
|
-
const staged =
|
|
6504
|
+
const staged = execSync33("git diff --cached --name-only", {
|
|
6222
6505
|
encoding: "utf-8"
|
|
6223
6506
|
});
|
|
6224
6507
|
for (const file of staged.trim().split("\n").filter(Boolean)) {
|
|
@@ -6226,7 +6509,7 @@ function getGitFiles(options2) {
|
|
|
6226
6509
|
}
|
|
6227
6510
|
}
|
|
6228
6511
|
if (options2.unstaged || options2.modified) {
|
|
6229
|
-
const unstaged =
|
|
6512
|
+
const unstaged = execSync33("git diff --name-only", { encoding: "utf-8" });
|
|
6230
6513
|
for (const file of unstaged.trim().split("\n").filter(Boolean)) {
|
|
6231
6514
|
files.add(file);
|
|
6232
6515
|
}
|
|
@@ -6287,7 +6570,7 @@ ${failed.length} verify script(s) failed:`);
|
|
|
6287
6570
|
async function runVerifyQuietly() {
|
|
6288
6571
|
const result = findPackageJsonWithVerifyScripts(process.cwd());
|
|
6289
6572
|
if (!result) return true;
|
|
6290
|
-
const packageDir =
|
|
6573
|
+
const packageDir = path27.dirname(result.packageJsonPath);
|
|
6291
6574
|
const results = await Promise.all(
|
|
6292
6575
|
result.verifyScripts.map((script) => runScript(script, packageDir))
|
|
6293
6576
|
);
|
|
@@ -6314,11 +6597,11 @@ async function check(pattern2, options2) {
|
|
|
6314
6597
|
|
|
6315
6598
|
// src/commands/refactor/ignore.ts
|
|
6316
6599
|
import fs17 from "fs";
|
|
6317
|
-
import
|
|
6600
|
+
import chalk75 from "chalk";
|
|
6318
6601
|
var REFACTOR_YML_PATH2 = "refactor.yml";
|
|
6319
6602
|
function ignore(file) {
|
|
6320
6603
|
if (!fs17.existsSync(file)) {
|
|
6321
|
-
console.error(
|
|
6604
|
+
console.error(chalk75.red(`Error: File does not exist: ${file}`));
|
|
6322
6605
|
process.exit(1);
|
|
6323
6606
|
}
|
|
6324
6607
|
const content = fs17.readFileSync(file, "utf-8");
|
|
@@ -6334,43 +6617,43 @@ function ignore(file) {
|
|
|
6334
6617
|
fs17.writeFileSync(REFACTOR_YML_PATH2, entry);
|
|
6335
6618
|
}
|
|
6336
6619
|
console.log(
|
|
6337
|
-
|
|
6620
|
+
chalk75.green(
|
|
6338
6621
|
`Added ${file} to refactor ignore list (max ${maxLines} lines)`
|
|
6339
6622
|
)
|
|
6340
6623
|
);
|
|
6341
6624
|
}
|
|
6342
6625
|
|
|
6343
6626
|
// src/commands/refactor/rename/index.ts
|
|
6344
|
-
import
|
|
6345
|
-
import
|
|
6627
|
+
import path28 from "path";
|
|
6628
|
+
import chalk76 from "chalk";
|
|
6346
6629
|
import { Project as Project2 } from "ts-morph";
|
|
6347
6630
|
async function rename(source, destination, options2 = {}) {
|
|
6348
|
-
const sourcePath =
|
|
6349
|
-
const destPath =
|
|
6631
|
+
const sourcePath = path28.resolve(source);
|
|
6632
|
+
const destPath = path28.resolve(destination);
|
|
6350
6633
|
const cwd = process.cwd();
|
|
6351
|
-
const relSource =
|
|
6352
|
-
const relDest =
|
|
6634
|
+
const relSource = path28.relative(cwd, sourcePath);
|
|
6635
|
+
const relDest = path28.relative(cwd, destPath);
|
|
6353
6636
|
const project = new Project2({
|
|
6354
|
-
tsConfigFilePath:
|
|
6637
|
+
tsConfigFilePath: path28.resolve("tsconfig.json")
|
|
6355
6638
|
});
|
|
6356
6639
|
const sourceFile = project.getSourceFile(sourcePath);
|
|
6357
6640
|
if (!sourceFile) {
|
|
6358
|
-
console.log(
|
|
6641
|
+
console.log(chalk76.red(`File not found in project: ${source}`));
|
|
6359
6642
|
process.exit(1);
|
|
6360
6643
|
}
|
|
6361
|
-
console.log(
|
|
6644
|
+
console.log(chalk76.bold(`Rename: ${relSource} \u2192 ${relDest}`));
|
|
6362
6645
|
if (options2.apply) {
|
|
6363
6646
|
sourceFile.move(destPath);
|
|
6364
6647
|
await project.save();
|
|
6365
|
-
console.log(
|
|
6648
|
+
console.log(chalk76.green("Done"));
|
|
6366
6649
|
} else {
|
|
6367
|
-
console.log(
|
|
6650
|
+
console.log(chalk76.dim("Dry run. Use --apply to execute."));
|
|
6368
6651
|
}
|
|
6369
6652
|
}
|
|
6370
6653
|
|
|
6371
6654
|
// src/commands/refactor/renameSymbol/index.ts
|
|
6372
|
-
import
|
|
6373
|
-
import
|
|
6655
|
+
import path30 from "path";
|
|
6656
|
+
import chalk77 from "chalk";
|
|
6374
6657
|
import { Project as Project3 } from "ts-morph";
|
|
6375
6658
|
|
|
6376
6659
|
// src/commands/refactor/renameSymbol/findSymbol.ts
|
|
@@ -6398,12 +6681,12 @@ function findSymbol(sourceFile, symbolName) {
|
|
|
6398
6681
|
}
|
|
6399
6682
|
|
|
6400
6683
|
// src/commands/refactor/renameSymbol/groupReferences.ts
|
|
6401
|
-
import
|
|
6684
|
+
import path29 from "path";
|
|
6402
6685
|
function groupReferences(symbol, cwd) {
|
|
6403
6686
|
const refs = symbol.findReferencesAsNodes();
|
|
6404
6687
|
const grouped = /* @__PURE__ */ new Map();
|
|
6405
6688
|
for (const ref of refs) {
|
|
6406
|
-
const refFile =
|
|
6689
|
+
const refFile = path29.relative(cwd, ref.getSourceFile().getFilePath());
|
|
6407
6690
|
const lines = grouped.get(refFile) ?? [];
|
|
6408
6691
|
if (!grouped.has(refFile)) grouped.set(refFile, lines);
|
|
6409
6692
|
lines.push(ref.getStartLineNumber());
|
|
@@ -6413,47 +6696,47 @@ function groupReferences(symbol, cwd) {
|
|
|
6413
6696
|
|
|
6414
6697
|
// src/commands/refactor/renameSymbol/index.ts
|
|
6415
6698
|
async function renameSymbol(file, oldName, newName, options2 = {}) {
|
|
6416
|
-
const filePath =
|
|
6417
|
-
const tsConfigPath =
|
|
6699
|
+
const filePath = path30.resolve(file);
|
|
6700
|
+
const tsConfigPath = path30.resolve("tsconfig.json");
|
|
6418
6701
|
const cwd = process.cwd();
|
|
6419
6702
|
const project = new Project3({ tsConfigFilePath: tsConfigPath });
|
|
6420
6703
|
const sourceFile = project.getSourceFile(filePath);
|
|
6421
6704
|
if (!sourceFile) {
|
|
6422
|
-
console.log(
|
|
6705
|
+
console.log(chalk77.red(`File not found in project: ${file}`));
|
|
6423
6706
|
process.exit(1);
|
|
6424
6707
|
}
|
|
6425
6708
|
const symbol = findSymbol(sourceFile, oldName);
|
|
6426
6709
|
if (!symbol) {
|
|
6427
|
-
console.log(
|
|
6710
|
+
console.log(chalk77.red(`Symbol "${oldName}" not found in ${file}`));
|
|
6428
6711
|
process.exit(1);
|
|
6429
6712
|
}
|
|
6430
6713
|
const grouped = groupReferences(symbol, cwd);
|
|
6431
6714
|
const totalRefs = [...grouped.values()].reduce((s, l) => s + l.length, 0);
|
|
6432
6715
|
console.log(
|
|
6433
|
-
|
|
6716
|
+
chalk77.bold(`Rename: ${oldName} \u2192 ${newName} (${totalRefs} references)
|
|
6434
6717
|
`)
|
|
6435
6718
|
);
|
|
6436
6719
|
for (const [refFile, lines] of grouped) {
|
|
6437
6720
|
console.log(
|
|
6438
|
-
` ${
|
|
6721
|
+
` ${chalk77.dim(refFile)}: lines ${chalk77.cyan(lines.join(", "))}`
|
|
6439
6722
|
);
|
|
6440
6723
|
}
|
|
6441
6724
|
if (options2.apply) {
|
|
6442
6725
|
symbol.rename(newName);
|
|
6443
6726
|
await project.save();
|
|
6444
|
-
console.log(
|
|
6727
|
+
console.log(chalk77.green(`
|
|
6445
6728
|
Renamed ${oldName} \u2192 ${newName}`));
|
|
6446
6729
|
} else {
|
|
6447
|
-
console.log(
|
|
6730
|
+
console.log(chalk77.dim("\nDry run. Use --apply to execute."));
|
|
6448
6731
|
}
|
|
6449
6732
|
}
|
|
6450
6733
|
|
|
6451
6734
|
// src/commands/refactor/restructure/index.ts
|
|
6452
|
-
import
|
|
6453
|
-
import
|
|
6735
|
+
import path39 from "path";
|
|
6736
|
+
import chalk80 from "chalk";
|
|
6454
6737
|
|
|
6455
6738
|
// src/commands/refactor/restructure/buildImportGraph/index.ts
|
|
6456
|
-
import
|
|
6739
|
+
import path31 from "path";
|
|
6457
6740
|
import ts7 from "typescript";
|
|
6458
6741
|
|
|
6459
6742
|
// src/commands/refactor/restructure/buildImportGraph/getImportSpecifiers.ts
|
|
@@ -6480,7 +6763,7 @@ function loadParsedConfig(tsConfigPath) {
|
|
|
6480
6763
|
return ts7.parseJsonConfigFileContent(
|
|
6481
6764
|
configFile.config,
|
|
6482
6765
|
ts7.sys,
|
|
6483
|
-
|
|
6766
|
+
path31.dirname(tsConfigPath)
|
|
6484
6767
|
);
|
|
6485
6768
|
}
|
|
6486
6769
|
function addToSetMap(map, key, value) {
|
|
@@ -6496,7 +6779,7 @@ function resolveImport(specifier, filePath, options2) {
|
|
|
6496
6779
|
const resolved = ts7.resolveModuleName(specifier, filePath, options2, ts7.sys);
|
|
6497
6780
|
const resolvedPath = resolved.resolvedModule?.resolvedFileName;
|
|
6498
6781
|
if (!resolvedPath || resolvedPath.includes("node_modules")) return null;
|
|
6499
|
-
return
|
|
6782
|
+
return path31.resolve(resolvedPath);
|
|
6500
6783
|
}
|
|
6501
6784
|
function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
6502
6785
|
const parsed = loadParsedConfig(tsConfigPath);
|
|
@@ -6505,7 +6788,7 @@ function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
|
6505
6788
|
const importedBy = /* @__PURE__ */ new Map();
|
|
6506
6789
|
const imports = /* @__PURE__ */ new Map();
|
|
6507
6790
|
for (const sourceFile of program2.getSourceFiles()) {
|
|
6508
|
-
const filePath =
|
|
6791
|
+
const filePath = path31.resolve(sourceFile.fileName);
|
|
6509
6792
|
if (filePath.includes("node_modules")) continue;
|
|
6510
6793
|
for (const specifier of getImportSpecifiers(sourceFile)) {
|
|
6511
6794
|
const absTarget = resolveImport(specifier, filePath, parsed.options);
|
|
@@ -6519,12 +6802,12 @@ function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
|
6519
6802
|
}
|
|
6520
6803
|
|
|
6521
6804
|
// src/commands/refactor/restructure/clusterDirectories.ts
|
|
6522
|
-
import
|
|
6805
|
+
import path32 from "path";
|
|
6523
6806
|
function clusterDirectories(graph) {
|
|
6524
6807
|
const dirImportedBy = /* @__PURE__ */ new Map();
|
|
6525
6808
|
for (const edge of graph.edges) {
|
|
6526
|
-
const sourceDir =
|
|
6527
|
-
const targetDir =
|
|
6809
|
+
const sourceDir = path32.dirname(edge.source);
|
|
6810
|
+
const targetDir = path32.dirname(edge.target);
|
|
6528
6811
|
if (sourceDir === targetDir) continue;
|
|
6529
6812
|
if (!graph.files.has(edge.target)) continue;
|
|
6530
6813
|
const existing = dirImportedBy.get(targetDir) ?? /* @__PURE__ */ new Set();
|
|
@@ -6552,20 +6835,20 @@ function clusterDirectories(graph) {
|
|
|
6552
6835
|
return clusters;
|
|
6553
6836
|
}
|
|
6554
6837
|
function isAncestor(ancestor, descendant) {
|
|
6555
|
-
const rel =
|
|
6838
|
+
const rel = path32.relative(ancestor, descendant);
|
|
6556
6839
|
return !rel.startsWith("..") && rel !== "";
|
|
6557
6840
|
}
|
|
6558
6841
|
|
|
6559
6842
|
// src/commands/refactor/restructure/clusterFiles.ts
|
|
6560
|
-
import
|
|
6843
|
+
import path33 from "path";
|
|
6561
6844
|
function findRootParent(file, importedBy, visited) {
|
|
6562
6845
|
const importers = importedBy.get(file);
|
|
6563
6846
|
if (!importers || importers.size !== 1) return file;
|
|
6564
6847
|
const parent = [...importers][0];
|
|
6565
|
-
const parentDir =
|
|
6566
|
-
const fileDir =
|
|
6848
|
+
const parentDir = path33.dirname(parent);
|
|
6849
|
+
const fileDir = path33.dirname(file);
|
|
6567
6850
|
if (parentDir !== fileDir) return file;
|
|
6568
|
-
if (
|
|
6851
|
+
if (path33.basename(parent, path33.extname(parent)) === "index") return file;
|
|
6569
6852
|
if (visited.has(parent)) return file;
|
|
6570
6853
|
visited.add(parent);
|
|
6571
6854
|
return findRootParent(parent, importedBy, visited);
|
|
@@ -6573,16 +6856,16 @@ function findRootParent(file, importedBy, visited) {
|
|
|
6573
6856
|
function clusterFiles(graph) {
|
|
6574
6857
|
const clusters = /* @__PURE__ */ new Map();
|
|
6575
6858
|
for (const file of graph.files) {
|
|
6576
|
-
const basename7 =
|
|
6859
|
+
const basename7 = path33.basename(file, path33.extname(file));
|
|
6577
6860
|
if (basename7 === "index") continue;
|
|
6578
6861
|
const importers = graph.importedBy.get(file);
|
|
6579
6862
|
if (!importers || importers.size !== 1) continue;
|
|
6580
6863
|
const parent = [...importers][0];
|
|
6581
6864
|
if (!graph.files.has(parent)) continue;
|
|
6582
|
-
const parentDir =
|
|
6583
|
-
const fileDir =
|
|
6865
|
+
const parentDir = path33.dirname(parent);
|
|
6866
|
+
const fileDir = path33.dirname(file);
|
|
6584
6867
|
if (parentDir !== fileDir) continue;
|
|
6585
|
-
const parentBasename =
|
|
6868
|
+
const parentBasename = path33.basename(parent, path33.extname(parent));
|
|
6586
6869
|
if (parentBasename === "index") continue;
|
|
6587
6870
|
const root = findRootParent(parent, graph.importedBy, /* @__PURE__ */ new Set([file]));
|
|
6588
6871
|
if (!root || root === file) continue;
|
|
@@ -6594,7 +6877,7 @@ function clusterFiles(graph) {
|
|
|
6594
6877
|
}
|
|
6595
6878
|
|
|
6596
6879
|
// src/commands/refactor/restructure/computeRewrites/index.ts
|
|
6597
|
-
import
|
|
6880
|
+
import path34 from "path";
|
|
6598
6881
|
|
|
6599
6882
|
// src/commands/refactor/restructure/computeRewrites/applyRewrites.ts
|
|
6600
6883
|
import fs18 from "fs";
|
|
@@ -6603,7 +6886,7 @@ function getOrCreateList(map, key) {
|
|
|
6603
6886
|
if (!map.has(key)) map.set(key, list4);
|
|
6604
6887
|
return list4;
|
|
6605
6888
|
}
|
|
6606
|
-
function
|
|
6889
|
+
function groupByFile2(rewrites) {
|
|
6607
6890
|
const grouped = /* @__PURE__ */ new Map();
|
|
6608
6891
|
for (const rewrite of rewrites) {
|
|
6609
6892
|
getOrCreateList(grouped, rewrite.file).push(rewrite);
|
|
@@ -6624,7 +6907,7 @@ function applyFileRewrites(file, fileRewrites) {
|
|
|
6624
6907
|
}
|
|
6625
6908
|
function applyRewrites(rewrites) {
|
|
6626
6909
|
const updatedContents = /* @__PURE__ */ new Map();
|
|
6627
|
-
for (const [file, fileRewrites] of
|
|
6910
|
+
for (const [file, fileRewrites] of groupByFile2(rewrites)) {
|
|
6628
6911
|
updatedContents.set(file, applyFileRewrites(file, fileRewrites));
|
|
6629
6912
|
}
|
|
6630
6913
|
return updatedContents;
|
|
@@ -6648,7 +6931,7 @@ function normalizeSpecifier(rel) {
|
|
|
6648
6931
|
);
|
|
6649
6932
|
}
|
|
6650
6933
|
function computeSpecifier(fromFile, toFile) {
|
|
6651
|
-
return normalizeSpecifier(
|
|
6934
|
+
return normalizeSpecifier(path34.relative(path34.dirname(fromFile), toFile));
|
|
6652
6935
|
}
|
|
6653
6936
|
function isAffected(edge, moveMap) {
|
|
6654
6937
|
return moveMap.has(edge.target) || moveMap.has(edge.source);
|
|
@@ -6692,51 +6975,51 @@ function computeRewrites(moves, edges, allProjectFiles) {
|
|
|
6692
6975
|
}
|
|
6693
6976
|
|
|
6694
6977
|
// src/commands/refactor/restructure/displayPlan.ts
|
|
6695
|
-
import
|
|
6696
|
-
import
|
|
6978
|
+
import path35 from "path";
|
|
6979
|
+
import chalk78 from "chalk";
|
|
6697
6980
|
function relPath(filePath) {
|
|
6698
|
-
return
|
|
6981
|
+
return path35.relative(process.cwd(), filePath);
|
|
6699
6982
|
}
|
|
6700
6983
|
function displayMoves(plan) {
|
|
6701
6984
|
if (plan.moves.length === 0) return;
|
|
6702
|
-
console.log(
|
|
6985
|
+
console.log(chalk78.bold("\nFile moves:"));
|
|
6703
6986
|
for (const move of plan.moves) {
|
|
6704
6987
|
console.log(
|
|
6705
|
-
` ${
|
|
6988
|
+
` ${chalk78.red(relPath(move.from))} \u2192 ${chalk78.green(relPath(move.to))}`
|
|
6706
6989
|
);
|
|
6707
|
-
console.log(
|
|
6990
|
+
console.log(chalk78.dim(` ${move.reason}`));
|
|
6708
6991
|
}
|
|
6709
6992
|
}
|
|
6710
6993
|
function displayRewrites(rewrites) {
|
|
6711
6994
|
if (rewrites.length === 0) return;
|
|
6712
6995
|
const affectedFiles = new Set(rewrites.map((r) => r.file));
|
|
6713
|
-
console.log(
|
|
6996
|
+
console.log(chalk78.bold(`
|
|
6714
6997
|
Import rewrites (${affectedFiles.size} files):`));
|
|
6715
6998
|
for (const file of affectedFiles) {
|
|
6716
|
-
console.log(` ${
|
|
6999
|
+
console.log(` ${chalk78.cyan(relPath(file))}:`);
|
|
6717
7000
|
for (const { oldSpecifier, newSpecifier } of rewrites.filter(
|
|
6718
7001
|
(r) => r.file === file
|
|
6719
7002
|
)) {
|
|
6720
7003
|
console.log(
|
|
6721
|
-
` ${
|
|
7004
|
+
` ${chalk78.red(`"${oldSpecifier}"`)} \u2192 ${chalk78.green(`"${newSpecifier}"`)}`
|
|
6722
7005
|
);
|
|
6723
7006
|
}
|
|
6724
7007
|
}
|
|
6725
7008
|
}
|
|
6726
7009
|
function displayPlan(plan) {
|
|
6727
7010
|
if (plan.warnings.length > 0) {
|
|
6728
|
-
console.log(
|
|
6729
|
-
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}`));
|
|
6730
7013
|
}
|
|
6731
7014
|
if (plan.newDirectories.length > 0) {
|
|
6732
|
-
console.log(
|
|
7015
|
+
console.log(chalk78.bold("\nNew directories:"));
|
|
6733
7016
|
for (const dir of plan.newDirectories)
|
|
6734
|
-
console.log(
|
|
7017
|
+
console.log(chalk78.green(` ${dir}/`));
|
|
6735
7018
|
}
|
|
6736
7019
|
displayMoves(plan);
|
|
6737
7020
|
displayRewrites(plan.rewrites);
|
|
6738
7021
|
console.log(
|
|
6739
|
-
|
|
7022
|
+
chalk78.dim(
|
|
6740
7023
|
`
|
|
6741
7024
|
Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rewritten`
|
|
6742
7025
|
)
|
|
@@ -6745,33 +7028,33 @@ Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rew
|
|
|
6745
7028
|
|
|
6746
7029
|
// src/commands/refactor/restructure/executePlan.ts
|
|
6747
7030
|
import fs19 from "fs";
|
|
6748
|
-
import
|
|
6749
|
-
import
|
|
7031
|
+
import path36 from "path";
|
|
7032
|
+
import chalk79 from "chalk";
|
|
6750
7033
|
function executePlan(plan) {
|
|
6751
7034
|
const updatedContents = applyRewrites(plan.rewrites);
|
|
6752
7035
|
for (const [file, content] of updatedContents) {
|
|
6753
7036
|
fs19.writeFileSync(file, content, "utf-8");
|
|
6754
7037
|
console.log(
|
|
6755
|
-
|
|
7038
|
+
chalk79.cyan(` Rewrote imports in ${path36.relative(process.cwd(), file)}`)
|
|
6756
7039
|
);
|
|
6757
7040
|
}
|
|
6758
7041
|
for (const dir of plan.newDirectories) {
|
|
6759
7042
|
fs19.mkdirSync(dir, { recursive: true });
|
|
6760
|
-
console.log(
|
|
7043
|
+
console.log(chalk79.green(` Created ${path36.relative(process.cwd(), dir)}/`));
|
|
6761
7044
|
}
|
|
6762
7045
|
for (const move of plan.moves) {
|
|
6763
|
-
const targetDir =
|
|
7046
|
+
const targetDir = path36.dirname(move.to);
|
|
6764
7047
|
if (!fs19.existsSync(targetDir)) {
|
|
6765
7048
|
fs19.mkdirSync(targetDir, { recursive: true });
|
|
6766
7049
|
}
|
|
6767
7050
|
fs19.renameSync(move.from, move.to);
|
|
6768
7051
|
console.log(
|
|
6769
|
-
|
|
6770
|
-
` Moved ${
|
|
7052
|
+
chalk79.white(
|
|
7053
|
+
` Moved ${path36.relative(process.cwd(), move.from)} \u2192 ${path36.relative(process.cwd(), move.to)}`
|
|
6771
7054
|
)
|
|
6772
7055
|
);
|
|
6773
7056
|
}
|
|
6774
|
-
removeEmptyDirectories(plan.moves.map((m) =>
|
|
7057
|
+
removeEmptyDirectories(plan.moves.map((m) => path36.dirname(m.from)));
|
|
6775
7058
|
}
|
|
6776
7059
|
function removeEmptyDirectories(dirs) {
|
|
6777
7060
|
const unique = [...new Set(dirs)];
|
|
@@ -6781,8 +7064,8 @@ function removeEmptyDirectories(dirs) {
|
|
|
6781
7064
|
if (entries.length === 0) {
|
|
6782
7065
|
fs19.rmdirSync(dir);
|
|
6783
7066
|
console.log(
|
|
6784
|
-
|
|
6785
|
-
` Removed empty directory ${
|
|
7067
|
+
chalk79.dim(
|
|
7068
|
+
` Removed empty directory ${path36.relative(process.cwd(), dir)}`
|
|
6786
7069
|
)
|
|
6787
7070
|
);
|
|
6788
7071
|
}
|
|
@@ -6790,7 +7073,7 @@ function removeEmptyDirectories(dirs) {
|
|
|
6790
7073
|
}
|
|
6791
7074
|
|
|
6792
7075
|
// src/commands/refactor/restructure/planFileMoves/index.ts
|
|
6793
|
-
import
|
|
7076
|
+
import path38 from "path";
|
|
6794
7077
|
|
|
6795
7078
|
// src/commands/refactor/restructure/planFileMoves/shared.ts
|
|
6796
7079
|
import fs20 from "fs";
|
|
@@ -6805,9 +7088,9 @@ function checkDirConflict(result, label2, dir) {
|
|
|
6805
7088
|
|
|
6806
7089
|
// src/commands/refactor/restructure/planFileMoves/planDirectoryMoves.ts
|
|
6807
7090
|
import fs21 from "fs";
|
|
6808
|
-
import
|
|
7091
|
+
import path37 from "path";
|
|
6809
7092
|
function collectEntry(results, dir, entry) {
|
|
6810
|
-
const full =
|
|
7093
|
+
const full = path37.join(dir, entry.name);
|
|
6811
7094
|
const items = entry.isDirectory() ? listFilesRecursive(full) : [full];
|
|
6812
7095
|
results.push(...items);
|
|
6813
7096
|
}
|
|
@@ -6821,15 +7104,15 @@ function listFilesRecursive(dir) {
|
|
|
6821
7104
|
}
|
|
6822
7105
|
function addDirectoryFileMoves(moves, childDir, newLocation, reason) {
|
|
6823
7106
|
for (const file of listFilesRecursive(childDir)) {
|
|
6824
|
-
const rel =
|
|
6825
|
-
moves.push({ from: file, to:
|
|
7107
|
+
const rel = path37.relative(childDir, file);
|
|
7108
|
+
moves.push({ from: file, to: path37.join(newLocation, rel), reason });
|
|
6826
7109
|
}
|
|
6827
7110
|
}
|
|
6828
7111
|
function resolveChildDest(parentDir, childDir) {
|
|
6829
|
-
return
|
|
7112
|
+
return path37.join(parentDir, path37.basename(childDir));
|
|
6830
7113
|
}
|
|
6831
7114
|
function childMoveReason(parentDir) {
|
|
6832
|
-
return `Directory only imported from ${
|
|
7115
|
+
return `Directory only imported from ${path37.basename(parentDir)}/`;
|
|
6833
7116
|
}
|
|
6834
7117
|
function registerDirectoryMove(result, childDir, dest, parentDir) {
|
|
6835
7118
|
result.directories.push(dest);
|
|
@@ -6854,7 +7137,7 @@ function planDirectoryMoves(clusters) {
|
|
|
6854
7137
|
|
|
6855
7138
|
// src/commands/refactor/restructure/planFileMoves/index.ts
|
|
6856
7139
|
function childMoveData(child, newDir, parentBase) {
|
|
6857
|
-
const to =
|
|
7140
|
+
const to = path38.join(newDir, path38.basename(child));
|
|
6858
7141
|
return { from: child, to, reason: `Only imported by ${parentBase}` };
|
|
6859
7142
|
}
|
|
6860
7143
|
function addChildMoves(moves, children, newDir, parentBase) {
|
|
@@ -6862,15 +7145,15 @@ function addChildMoves(moves, children, newDir, parentBase) {
|
|
|
6862
7145
|
moves.push(childMoveData(child, newDir, parentBase));
|
|
6863
7146
|
}
|
|
6864
7147
|
function getBaseName(filePath) {
|
|
6865
|
-
return
|
|
7148
|
+
return path38.basename(filePath, path38.extname(filePath));
|
|
6866
7149
|
}
|
|
6867
7150
|
function resolveClusterDir(parent) {
|
|
6868
|
-
return
|
|
7151
|
+
return path38.join(path38.dirname(parent), getBaseName(parent));
|
|
6869
7152
|
}
|
|
6870
7153
|
function createParentMove(parent, newDir) {
|
|
6871
7154
|
return {
|
|
6872
7155
|
from: parent,
|
|
6873
|
-
to:
|
|
7156
|
+
to: path38.join(newDir, `index${path38.extname(parent)}`),
|
|
6874
7157
|
reason: `Main module of new ${getBaseName(parent)}/ directory`
|
|
6875
7158
|
};
|
|
6876
7159
|
}
|
|
@@ -6894,7 +7177,7 @@ function planFileMoves(clusters) {
|
|
|
6894
7177
|
|
|
6895
7178
|
// src/commands/refactor/restructure/index.ts
|
|
6896
7179
|
function buildPlan(candidateFiles, tsConfigPath) {
|
|
6897
|
-
const candidates = new Set(candidateFiles.map((f) =>
|
|
7180
|
+
const candidates = new Set(candidateFiles.map((f) => path39.resolve(f)));
|
|
6898
7181
|
const graph = buildImportGraph(candidates, tsConfigPath);
|
|
6899
7182
|
const allProjectFiles = /* @__PURE__ */ new Set([
|
|
6900
7183
|
...graph.importedBy.keys(),
|
|
@@ -6914,22 +7197,22 @@ async function restructure(pattern2, options2 = {}) {
|
|
|
6914
7197
|
const targetPattern = pattern2 ?? "src";
|
|
6915
7198
|
const files = findSourceFiles2(targetPattern);
|
|
6916
7199
|
if (files.length === 0) {
|
|
6917
|
-
console.log(
|
|
7200
|
+
console.log(chalk80.yellow("No files found matching pattern"));
|
|
6918
7201
|
return;
|
|
6919
7202
|
}
|
|
6920
|
-
const tsConfigPath =
|
|
7203
|
+
const tsConfigPath = path39.resolve("tsconfig.json");
|
|
6921
7204
|
const plan = buildPlan(files, tsConfigPath);
|
|
6922
7205
|
if (plan.moves.length === 0) {
|
|
6923
|
-
console.log(
|
|
7206
|
+
console.log(chalk80.green("No restructuring needed"));
|
|
6924
7207
|
return;
|
|
6925
7208
|
}
|
|
6926
7209
|
displayPlan(plan);
|
|
6927
7210
|
if (options2.apply) {
|
|
6928
|
-
console.log(
|
|
7211
|
+
console.log(chalk80.bold("\nApplying changes..."));
|
|
6929
7212
|
executePlan(plan);
|
|
6930
|
-
console.log(
|
|
7213
|
+
console.log(chalk80.green("\nRestructuring complete"));
|
|
6931
7214
|
} else {
|
|
6932
|
-
console.log(
|
|
7215
|
+
console.log(chalk80.dim("\nDry run. Use --apply to execute."));
|
|
6933
7216
|
}
|
|
6934
7217
|
}
|
|
6935
7218
|
|
|
@@ -6957,8 +7240,8 @@ function registerRefactor(program2) {
|
|
|
6957
7240
|
}
|
|
6958
7241
|
|
|
6959
7242
|
// src/commands/transcript/shared.ts
|
|
6960
|
-
import { existsSync as
|
|
6961
|
-
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";
|
|
6962
7245
|
import * as readline2 from "readline";
|
|
6963
7246
|
var DATE_PREFIX_REGEX = /^\d{4}-\d{2}-\d{2}/;
|
|
6964
7247
|
function getDatePrefix(daysOffset = 0) {
|
|
@@ -6973,10 +7256,10 @@ function isValidDatePrefix(filename) {
|
|
|
6973
7256
|
return DATE_PREFIX_REGEX.test(filename);
|
|
6974
7257
|
}
|
|
6975
7258
|
function collectFiles(dir, extension) {
|
|
6976
|
-
if (!
|
|
7259
|
+
if (!existsSync27(dir)) return [];
|
|
6977
7260
|
const results = [];
|
|
6978
|
-
for (const entry of
|
|
6979
|
-
const fullPath =
|
|
7261
|
+
for (const entry of readdirSync5(dir)) {
|
|
7262
|
+
const fullPath = join24(dir, entry);
|
|
6980
7263
|
if (statSync2(fullPath).isDirectory()) {
|
|
6981
7264
|
results.push(...collectFiles(fullPath, extension));
|
|
6982
7265
|
} else if (entry.endsWith(extension)) {
|
|
@@ -7070,14 +7353,14 @@ async function configure() {
|
|
|
7070
7353
|
}
|
|
7071
7354
|
|
|
7072
7355
|
// src/commands/transcript/format/index.ts
|
|
7073
|
-
import { existsSync as
|
|
7356
|
+
import { existsSync as existsSync29 } from "fs";
|
|
7074
7357
|
|
|
7075
7358
|
// src/commands/transcript/format/fixInvalidDatePrefixes/index.ts
|
|
7076
|
-
import { dirname as
|
|
7359
|
+
import { dirname as dirname18, join as join26 } from "path";
|
|
7077
7360
|
|
|
7078
7361
|
// src/commands/transcript/format/fixInvalidDatePrefixes/promptForDateFix.ts
|
|
7079
7362
|
import { renameSync } from "fs";
|
|
7080
|
-
import { join as
|
|
7363
|
+
import { join as join25 } from "path";
|
|
7081
7364
|
async function resolveDate(rl, choice) {
|
|
7082
7365
|
if (choice === "1") return getDatePrefix(0);
|
|
7083
7366
|
if (choice === "2") return getDatePrefix(-1);
|
|
@@ -7092,7 +7375,7 @@ async function resolveDate(rl, choice) {
|
|
|
7092
7375
|
}
|
|
7093
7376
|
function renameWithPrefix(vttDir, vttFile, prefix2) {
|
|
7094
7377
|
const newFilename = `${prefix2}.${vttFile}`;
|
|
7095
|
-
renameSync(
|
|
7378
|
+
renameSync(join25(vttDir, vttFile), join25(vttDir, newFilename));
|
|
7096
7379
|
console.log(`Renamed to: ${newFilename}`);
|
|
7097
7380
|
return newFilename;
|
|
7098
7381
|
}
|
|
@@ -7123,15 +7406,15 @@ async function fixInvalidDatePrefixes(vttFiles) {
|
|
|
7123
7406
|
for (let i = 0; i < vttFiles.length; i++) {
|
|
7124
7407
|
const vttFile = vttFiles[i];
|
|
7125
7408
|
if (!isValidDatePrefix(vttFile.filename)) {
|
|
7126
|
-
const vttFileDir =
|
|
7409
|
+
const vttFileDir = dirname18(vttFile.absolutePath);
|
|
7127
7410
|
const newFilename = await promptForDateFix(vttFile.filename, vttFileDir);
|
|
7128
7411
|
if (newFilename) {
|
|
7129
|
-
const newRelativePath =
|
|
7130
|
-
|
|
7412
|
+
const newRelativePath = join26(
|
|
7413
|
+
dirname18(vttFile.relativePath),
|
|
7131
7414
|
newFilename
|
|
7132
7415
|
);
|
|
7133
7416
|
vttFiles[i] = {
|
|
7134
|
-
absolutePath:
|
|
7417
|
+
absolutePath: join26(vttFileDir, newFilename),
|
|
7135
7418
|
relativePath: newRelativePath,
|
|
7136
7419
|
filename: newFilename
|
|
7137
7420
|
};
|
|
@@ -7144,8 +7427,8 @@ async function fixInvalidDatePrefixes(vttFiles) {
|
|
|
7144
7427
|
}
|
|
7145
7428
|
|
|
7146
7429
|
// src/commands/transcript/format/processVttFile/index.ts
|
|
7147
|
-
import { existsSync as
|
|
7148
|
-
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";
|
|
7149
7432
|
|
|
7150
7433
|
// src/commands/transcript/cleanText.ts
|
|
7151
7434
|
function cleanText(text) {
|
|
@@ -7355,21 +7638,21 @@ function toMdFilename(vttFilename) {
|
|
|
7355
7638
|
return `${basename5(vttFilename, ".vtt").replace(/\s*Transcription\s*/g, " ").trim()}.md`;
|
|
7356
7639
|
}
|
|
7357
7640
|
function resolveOutputDir(relativeDir, transcriptsDir) {
|
|
7358
|
-
return relativeDir === "." ? transcriptsDir :
|
|
7641
|
+
return relativeDir === "." ? transcriptsDir : join27(transcriptsDir, relativeDir);
|
|
7359
7642
|
}
|
|
7360
7643
|
function buildOutputPaths(vttFile, transcriptsDir) {
|
|
7361
7644
|
const mdFile = toMdFilename(vttFile.filename);
|
|
7362
|
-
const relativeDir =
|
|
7645
|
+
const relativeDir = dirname19(vttFile.relativePath);
|
|
7363
7646
|
const outputDir = resolveOutputDir(relativeDir, transcriptsDir);
|
|
7364
|
-
const outputPath =
|
|
7647
|
+
const outputPath = join27(outputDir, mdFile);
|
|
7365
7648
|
return { outputDir, outputPath, mdFile, relativeDir };
|
|
7366
7649
|
}
|
|
7367
7650
|
function logSkipped(relativeDir, mdFile) {
|
|
7368
|
-
console.log(`Skipping (already exists): ${
|
|
7651
|
+
console.log(`Skipping (already exists): ${join27(relativeDir, mdFile)}`);
|
|
7369
7652
|
return "skipped";
|
|
7370
7653
|
}
|
|
7371
7654
|
function ensureDirectory(dir, label2) {
|
|
7372
|
-
if (!
|
|
7655
|
+
if (!existsSync28(dir)) {
|
|
7373
7656
|
mkdirSync7(dir, { recursive: true });
|
|
7374
7657
|
console.log(`Created ${label2}: ${dir}`);
|
|
7375
7658
|
}
|
|
@@ -7392,7 +7675,7 @@ function logReduction(cueCount, messageCount) {
|
|
|
7392
7675
|
}
|
|
7393
7676
|
function readAndParseCues(inputPath) {
|
|
7394
7677
|
console.log(`Reading: ${inputPath}`);
|
|
7395
|
-
return processCues(
|
|
7678
|
+
return processCues(readFileSync23(inputPath, "utf-8"));
|
|
7396
7679
|
}
|
|
7397
7680
|
function writeFormatted(outputPath, content) {
|
|
7398
7681
|
writeFileSync22(outputPath, content, "utf-8");
|
|
@@ -7405,7 +7688,7 @@ function convertVttToMarkdown(inputPath, outputPath) {
|
|
|
7405
7688
|
logReduction(cues.length, chatMessages.length);
|
|
7406
7689
|
}
|
|
7407
7690
|
function tryProcessVtt(vttFile, paths) {
|
|
7408
|
-
if (
|
|
7691
|
+
if (existsSync28(paths.outputPath))
|
|
7409
7692
|
return logSkipped(paths.relativeDir, paths.mdFile);
|
|
7410
7693
|
convertVttToMarkdown(vttFile.absolutePath, paths.outputPath);
|
|
7411
7694
|
return "processed";
|
|
@@ -7431,7 +7714,7 @@ function processAllFiles(vttFiles, transcriptsDir) {
|
|
|
7431
7714
|
logSummary(counts);
|
|
7432
7715
|
}
|
|
7433
7716
|
function requireVttDir(vttDir) {
|
|
7434
|
-
if (!
|
|
7717
|
+
if (!existsSync29(vttDir)) {
|
|
7435
7718
|
console.error(`VTT directory not found: ${vttDir}`);
|
|
7436
7719
|
process.exit(1);
|
|
7437
7720
|
}
|
|
@@ -7463,28 +7746,28 @@ async function format() {
|
|
|
7463
7746
|
}
|
|
7464
7747
|
|
|
7465
7748
|
// src/commands/transcript/summarise/index.ts
|
|
7466
|
-
import { existsSync as
|
|
7467
|
-
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";
|
|
7468
7751
|
|
|
7469
7752
|
// src/commands/transcript/summarise/processStagedFile/index.ts
|
|
7470
7753
|
import {
|
|
7471
|
-
existsSync as
|
|
7754
|
+
existsSync as existsSync30,
|
|
7472
7755
|
mkdirSync as mkdirSync8,
|
|
7473
|
-
readFileSync as
|
|
7756
|
+
readFileSync as readFileSync24,
|
|
7474
7757
|
renameSync as renameSync2,
|
|
7475
7758
|
rmSync
|
|
7476
7759
|
} from "fs";
|
|
7477
|
-
import { dirname as
|
|
7760
|
+
import { dirname as dirname20, join as join28 } from "path";
|
|
7478
7761
|
|
|
7479
7762
|
// src/commands/transcript/summarise/processStagedFile/validateStagedContent.ts
|
|
7480
|
-
import
|
|
7763
|
+
import chalk81 from "chalk";
|
|
7481
7764
|
var FULL_TRANSCRIPT_REGEX = /^\[Full Transcript\]\(([^)]+)\)/;
|
|
7482
7765
|
function validateStagedContent(filename, content) {
|
|
7483
7766
|
const firstLine = content.split("\n")[0];
|
|
7484
7767
|
const match = firstLine.match(FULL_TRANSCRIPT_REGEX);
|
|
7485
7768
|
if (!match) {
|
|
7486
7769
|
console.error(
|
|
7487
|
-
|
|
7770
|
+
chalk81.red(
|
|
7488
7771
|
`Staged file ${filename} missing [Full Transcript](<path>) link on first line.`
|
|
7489
7772
|
)
|
|
7490
7773
|
);
|
|
@@ -7493,7 +7776,7 @@ function validateStagedContent(filename, content) {
|
|
|
7493
7776
|
const contentAfterLink = content.slice(firstLine.length).trim();
|
|
7494
7777
|
if (!contentAfterLink) {
|
|
7495
7778
|
console.error(
|
|
7496
|
-
|
|
7779
|
+
chalk81.red(
|
|
7497
7780
|
`Staged file ${filename} has no summary content after the transcript link.`
|
|
7498
7781
|
)
|
|
7499
7782
|
);
|
|
@@ -7503,9 +7786,9 @@ function validateStagedContent(filename, content) {
|
|
|
7503
7786
|
}
|
|
7504
7787
|
|
|
7505
7788
|
// src/commands/transcript/summarise/processStagedFile/index.ts
|
|
7506
|
-
var STAGING_DIR =
|
|
7789
|
+
var STAGING_DIR = join28(process.cwd(), ".assist", "transcript");
|
|
7507
7790
|
function processStagedFile() {
|
|
7508
|
-
if (!
|
|
7791
|
+
if (!existsSync30(STAGING_DIR)) {
|
|
7509
7792
|
return false;
|
|
7510
7793
|
}
|
|
7511
7794
|
const stagedFiles = findMdFilesRecursive(STAGING_DIR);
|
|
@@ -7514,7 +7797,7 @@ function processStagedFile() {
|
|
|
7514
7797
|
}
|
|
7515
7798
|
const { transcriptsDir, summaryDir } = getTranscriptConfig();
|
|
7516
7799
|
const stagedFile = stagedFiles[0];
|
|
7517
|
-
const content =
|
|
7800
|
+
const content = readFileSync24(stagedFile.absolutePath, "utf-8");
|
|
7518
7801
|
validateStagedContent(stagedFile.filename, content);
|
|
7519
7802
|
const stagedBaseName = getTranscriptBaseName(stagedFile.filename);
|
|
7520
7803
|
const transcriptFiles = findMdFilesRecursive(transcriptsDir);
|
|
@@ -7527,9 +7810,9 @@ function processStagedFile() {
|
|
|
7527
7810
|
);
|
|
7528
7811
|
process.exit(1);
|
|
7529
7812
|
}
|
|
7530
|
-
const destPath =
|
|
7531
|
-
const destDir =
|
|
7532
|
-
if (!
|
|
7813
|
+
const destPath = join28(summaryDir, matchingTranscript.relativePath);
|
|
7814
|
+
const destDir = dirname20(destPath);
|
|
7815
|
+
if (!existsSync30(destDir)) {
|
|
7533
7816
|
mkdirSync8(destDir, { recursive: true });
|
|
7534
7817
|
}
|
|
7535
7818
|
renameSync2(stagedFile.absolutePath, destPath);
|
|
@@ -7542,8 +7825,8 @@ function processStagedFile() {
|
|
|
7542
7825
|
|
|
7543
7826
|
// src/commands/transcript/summarise/index.ts
|
|
7544
7827
|
function buildRelativeKey(relativePath, baseName) {
|
|
7545
|
-
const relDir =
|
|
7546
|
-
return relDir === "." ? baseName :
|
|
7828
|
+
const relDir = dirname21(relativePath);
|
|
7829
|
+
return relDir === "." ? baseName : join29(relDir, baseName);
|
|
7547
7830
|
}
|
|
7548
7831
|
function buildSummaryIndex(summaryDir) {
|
|
7549
7832
|
const summaryFiles = findMdFilesRecursive(summaryDir);
|
|
@@ -7556,7 +7839,7 @@ function buildSummaryIndex(summaryDir) {
|
|
|
7556
7839
|
function summarise2() {
|
|
7557
7840
|
processStagedFile();
|
|
7558
7841
|
const { transcriptsDir, summaryDir } = getTranscriptConfig();
|
|
7559
|
-
if (!
|
|
7842
|
+
if (!existsSync31(transcriptsDir)) {
|
|
7560
7843
|
console.log("No transcripts directory found.");
|
|
7561
7844
|
return;
|
|
7562
7845
|
}
|
|
@@ -7577,8 +7860,8 @@ function summarise2() {
|
|
|
7577
7860
|
}
|
|
7578
7861
|
const next2 = missing[0];
|
|
7579
7862
|
const outputFilename = `${getTranscriptBaseName(next2.filename)}.md`;
|
|
7580
|
-
const outputPath =
|
|
7581
|
-
const summaryFileDir =
|
|
7863
|
+
const outputPath = join29(STAGING_DIR, outputFilename);
|
|
7864
|
+
const summaryFileDir = join29(summaryDir, dirname21(next2.relativePath));
|
|
7582
7865
|
const relativeTranscriptPath = encodeURI(
|
|
7583
7866
|
relative2(summaryFileDir, next2.absolutePath).replace(/\\/g, "/")
|
|
7584
7867
|
);
|
|
@@ -7624,50 +7907,50 @@ function registerVerify(program2) {
|
|
|
7624
7907
|
|
|
7625
7908
|
// src/commands/voice/devices.ts
|
|
7626
7909
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
7627
|
-
import { join as
|
|
7910
|
+
import { join as join31 } from "path";
|
|
7628
7911
|
|
|
7629
7912
|
// src/commands/voice/shared.ts
|
|
7630
7913
|
import { homedir as homedir7 } from "os";
|
|
7631
|
-
import { dirname as
|
|
7914
|
+
import { dirname as dirname22, join as join30 } from "path";
|
|
7632
7915
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
7633
|
-
var __dirname6 =
|
|
7634
|
-
var VOICE_DIR =
|
|
7916
|
+
var __dirname6 = dirname22(fileURLToPath6(import.meta.url));
|
|
7917
|
+
var VOICE_DIR = join30(homedir7(), ".assist", "voice");
|
|
7635
7918
|
var voicePaths = {
|
|
7636
7919
|
dir: VOICE_DIR,
|
|
7637
|
-
pid:
|
|
7638
|
-
log:
|
|
7639
|
-
venv:
|
|
7640
|
-
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")
|
|
7641
7924
|
};
|
|
7642
7925
|
function getPythonDir() {
|
|
7643
|
-
return
|
|
7926
|
+
return join30(__dirname6, "commands", "voice", "python");
|
|
7644
7927
|
}
|
|
7645
7928
|
function getVenvPython() {
|
|
7646
|
-
return process.platform === "win32" ?
|
|
7929
|
+
return process.platform === "win32" ? join30(voicePaths.venv, "Scripts", "python.exe") : join30(voicePaths.venv, "bin", "python");
|
|
7647
7930
|
}
|
|
7648
7931
|
function getLockDir() {
|
|
7649
7932
|
const config = loadConfig();
|
|
7650
7933
|
return config.voice?.lockDir ?? VOICE_DIR;
|
|
7651
7934
|
}
|
|
7652
7935
|
function getLockFile() {
|
|
7653
|
-
return
|
|
7936
|
+
return join30(getLockDir(), "voice.lock");
|
|
7654
7937
|
}
|
|
7655
7938
|
|
|
7656
7939
|
// src/commands/voice/devices.ts
|
|
7657
7940
|
function devices() {
|
|
7658
|
-
const script =
|
|
7941
|
+
const script = join31(getPythonDir(), "list_devices.py");
|
|
7659
7942
|
spawnSync3(getVenvPython(), [script], { stdio: "inherit" });
|
|
7660
7943
|
}
|
|
7661
7944
|
|
|
7662
7945
|
// src/commands/voice/logs.ts
|
|
7663
|
-
import { existsSync as
|
|
7946
|
+
import { existsSync as existsSync32, readFileSync as readFileSync25 } from "fs";
|
|
7664
7947
|
function logs(options2) {
|
|
7665
|
-
if (!
|
|
7948
|
+
if (!existsSync32(voicePaths.log)) {
|
|
7666
7949
|
console.log("No voice log file found");
|
|
7667
7950
|
return;
|
|
7668
7951
|
}
|
|
7669
7952
|
const count = Number.parseInt(options2.lines ?? "150", 10);
|
|
7670
|
-
const content =
|
|
7953
|
+
const content = readFileSync25(voicePaths.log, "utf-8").trim();
|
|
7671
7954
|
if (!content) {
|
|
7672
7955
|
console.log("Voice log is empty");
|
|
7673
7956
|
return;
|
|
@@ -7690,12 +7973,12 @@ function logs(options2) {
|
|
|
7690
7973
|
// src/commands/voice/setup.ts
|
|
7691
7974
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
7692
7975
|
import { mkdirSync as mkdirSync10 } from "fs";
|
|
7693
|
-
import { join as
|
|
7976
|
+
import { join as join33 } from "path";
|
|
7694
7977
|
|
|
7695
7978
|
// src/commands/voice/checkLockFile.ts
|
|
7696
|
-
import { execSync as
|
|
7697
|
-
import { existsSync as
|
|
7698
|
-
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";
|
|
7699
7982
|
function isProcessAlive(pid) {
|
|
7700
7983
|
try {
|
|
7701
7984
|
process.kill(pid, 0);
|
|
@@ -7706,9 +7989,9 @@ function isProcessAlive(pid) {
|
|
|
7706
7989
|
}
|
|
7707
7990
|
function checkLockFile() {
|
|
7708
7991
|
const lockFile = getLockFile();
|
|
7709
|
-
if (!
|
|
7992
|
+
if (!existsSync33(lockFile)) return;
|
|
7710
7993
|
try {
|
|
7711
|
-
const lock = JSON.parse(
|
|
7994
|
+
const lock = JSON.parse(readFileSync26(lockFile, "utf-8"));
|
|
7712
7995
|
if (lock.pid && isProcessAlive(lock.pid)) {
|
|
7713
7996
|
console.error(
|
|
7714
7997
|
`Voice daemon already running (PID ${lock.pid}, env: ${lock.env}). Stop it first with: assist voice stop`
|
|
@@ -7719,10 +8002,10 @@ function checkLockFile() {
|
|
|
7719
8002
|
}
|
|
7720
8003
|
}
|
|
7721
8004
|
function bootstrapVenv() {
|
|
7722
|
-
if (
|
|
8005
|
+
if (existsSync33(getVenvPython())) return;
|
|
7723
8006
|
console.log("Setting up Python environment...");
|
|
7724
8007
|
const pythonDir = getPythonDir();
|
|
7725
|
-
|
|
8008
|
+
execSync34(
|
|
7726
8009
|
`uv sync --project "${pythonDir}" --extra runtime --no-install-project`,
|
|
7727
8010
|
{
|
|
7728
8011
|
stdio: "inherit",
|
|
@@ -7732,7 +8015,7 @@ function bootstrapVenv() {
|
|
|
7732
8015
|
}
|
|
7733
8016
|
function writeLockFile(pid) {
|
|
7734
8017
|
const lockFile = getLockFile();
|
|
7735
|
-
mkdirSync9(
|
|
8018
|
+
mkdirSync9(join32(lockFile, ".."), { recursive: true });
|
|
7736
8019
|
writeFileSync23(
|
|
7737
8020
|
lockFile,
|
|
7738
8021
|
JSON.stringify({
|
|
@@ -7748,7 +8031,7 @@ function setup() {
|
|
|
7748
8031
|
mkdirSync10(voicePaths.dir, { recursive: true });
|
|
7749
8032
|
bootstrapVenv();
|
|
7750
8033
|
console.log("\nDownloading models...\n");
|
|
7751
|
-
const script =
|
|
8034
|
+
const script = join33(getPythonDir(), "setup_models.py");
|
|
7752
8035
|
const result = spawnSync4(getVenvPython(), [script], {
|
|
7753
8036
|
stdio: "inherit",
|
|
7754
8037
|
env: { ...process.env, VOICE_LOG_FILE: voicePaths.log }
|
|
@@ -7762,7 +8045,7 @@ function setup() {
|
|
|
7762
8045
|
// src/commands/voice/start.ts
|
|
7763
8046
|
import { spawn as spawn4 } from "child_process";
|
|
7764
8047
|
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync24 } from "fs";
|
|
7765
|
-
import { join as
|
|
8048
|
+
import { join as join34 } from "path";
|
|
7766
8049
|
|
|
7767
8050
|
// src/commands/voice/buildDaemonEnv.ts
|
|
7768
8051
|
function buildDaemonEnv(options2) {
|
|
@@ -7800,7 +8083,7 @@ function start2(options2) {
|
|
|
7800
8083
|
bootstrapVenv();
|
|
7801
8084
|
const debug = options2.debug || options2.foreground || process.platform === "win32";
|
|
7802
8085
|
const env = buildDaemonEnv({ debug });
|
|
7803
|
-
const script =
|
|
8086
|
+
const script = join34(getPythonDir(), "voice_daemon.py");
|
|
7804
8087
|
const python = getVenvPython();
|
|
7805
8088
|
if (options2.foreground) {
|
|
7806
8089
|
spawnForeground(python, script, env);
|
|
@@ -7810,7 +8093,7 @@ function start2(options2) {
|
|
|
7810
8093
|
}
|
|
7811
8094
|
|
|
7812
8095
|
// src/commands/voice/status.ts
|
|
7813
|
-
import { existsSync as
|
|
8096
|
+
import { existsSync as existsSync34, readFileSync as readFileSync27 } from "fs";
|
|
7814
8097
|
function isProcessAlive2(pid) {
|
|
7815
8098
|
try {
|
|
7816
8099
|
process.kill(pid, 0);
|
|
@@ -7820,16 +8103,16 @@ function isProcessAlive2(pid) {
|
|
|
7820
8103
|
}
|
|
7821
8104
|
}
|
|
7822
8105
|
function readRecentLogs(count) {
|
|
7823
|
-
if (!
|
|
7824
|
-
const lines =
|
|
8106
|
+
if (!existsSync34(voicePaths.log)) return [];
|
|
8107
|
+
const lines = readFileSync27(voicePaths.log, "utf-8").trim().split("\n");
|
|
7825
8108
|
return lines.slice(-count);
|
|
7826
8109
|
}
|
|
7827
8110
|
function status() {
|
|
7828
|
-
if (!
|
|
8111
|
+
if (!existsSync34(voicePaths.pid)) {
|
|
7829
8112
|
console.log("Voice daemon: not running (no PID file)");
|
|
7830
8113
|
return;
|
|
7831
8114
|
}
|
|
7832
|
-
const pid = Number.parseInt(
|
|
8115
|
+
const pid = Number.parseInt(readFileSync27(voicePaths.pid, "utf-8").trim(), 10);
|
|
7833
8116
|
const alive = isProcessAlive2(pid);
|
|
7834
8117
|
console.log(`Voice daemon: ${alive ? "running" : "dead"} (PID ${pid})`);
|
|
7835
8118
|
const recent = readRecentLogs(5);
|
|
@@ -7848,13 +8131,13 @@ function status() {
|
|
|
7848
8131
|
}
|
|
7849
8132
|
|
|
7850
8133
|
// src/commands/voice/stop.ts
|
|
7851
|
-
import { existsSync as
|
|
8134
|
+
import { existsSync as existsSync35, readFileSync as readFileSync28, unlinkSync as unlinkSync8 } from "fs";
|
|
7852
8135
|
function stop() {
|
|
7853
|
-
if (!
|
|
8136
|
+
if (!existsSync35(voicePaths.pid)) {
|
|
7854
8137
|
console.log("Voice daemon is not running (no PID file)");
|
|
7855
8138
|
return;
|
|
7856
8139
|
}
|
|
7857
|
-
const pid = Number.parseInt(
|
|
8140
|
+
const pid = Number.parseInt(readFileSync28(voicePaths.pid, "utf-8").trim(), 10);
|
|
7858
8141
|
try {
|
|
7859
8142
|
process.kill(pid, "SIGTERM");
|
|
7860
8143
|
console.log(`Sent SIGTERM to voice daemon (PID ${pid})`);
|
|
@@ -7862,12 +8145,12 @@ function stop() {
|
|
|
7862
8145
|
console.log(`Voice daemon (PID ${pid}) is not running`);
|
|
7863
8146
|
}
|
|
7864
8147
|
try {
|
|
7865
|
-
|
|
8148
|
+
unlinkSync8(voicePaths.pid);
|
|
7866
8149
|
} catch {
|
|
7867
8150
|
}
|
|
7868
8151
|
try {
|
|
7869
8152
|
const lockFile = getLockFile();
|
|
7870
|
-
if (
|
|
8153
|
+
if (existsSync35(lockFile)) unlinkSync8(lockFile);
|
|
7871
8154
|
} catch {
|
|
7872
8155
|
}
|
|
7873
8156
|
console.log("Voice daemon stopped");
|
|
@@ -7886,14 +8169,14 @@ function registerVoice(program2) {
|
|
|
7886
8169
|
|
|
7887
8170
|
// src/commands/roam/auth.ts
|
|
7888
8171
|
import { randomBytes } from "crypto";
|
|
7889
|
-
import
|
|
8172
|
+
import chalk82 from "chalk";
|
|
7890
8173
|
|
|
7891
8174
|
// src/lib/openBrowser.ts
|
|
7892
|
-
import { execSync as
|
|
8175
|
+
import { execSync as execSync35 } from "child_process";
|
|
7893
8176
|
function tryExec(commands) {
|
|
7894
8177
|
for (const cmd of commands) {
|
|
7895
8178
|
try {
|
|
7896
|
-
|
|
8179
|
+
execSync35(cmd);
|
|
7897
8180
|
return true;
|
|
7898
8181
|
} catch {
|
|
7899
8182
|
}
|
|
@@ -8061,13 +8344,13 @@ async function auth() {
|
|
|
8061
8344
|
saveGlobalConfig(config);
|
|
8062
8345
|
const state = randomBytes(16).toString("hex");
|
|
8063
8346
|
console.log(
|
|
8064
|
-
|
|
8347
|
+
chalk82.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
|
|
8065
8348
|
);
|
|
8066
|
-
console.log(
|
|
8067
|
-
console.log(
|
|
8068
|
-
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..."));
|
|
8069
8352
|
const { code, redirectUri } = await authorizeInBrowser(clientId, state);
|
|
8070
|
-
console.log(
|
|
8353
|
+
console.log(chalk82.dim("Exchanging code for tokens..."));
|
|
8071
8354
|
const tokens = await exchangeToken({
|
|
8072
8355
|
code,
|
|
8073
8356
|
clientId,
|
|
@@ -8083,7 +8366,7 @@ async function auth() {
|
|
|
8083
8366
|
};
|
|
8084
8367
|
saveGlobalConfig(config);
|
|
8085
8368
|
console.log(
|
|
8086
|
-
|
|
8369
|
+
chalk82.green("Roam credentials and tokens saved to ~/.assist.yml")
|
|
8087
8370
|
);
|
|
8088
8371
|
}
|
|
8089
8372
|
|
|
@@ -8094,16 +8377,7 @@ function registerRoam(program2) {
|
|
|
8094
8377
|
}
|
|
8095
8378
|
|
|
8096
8379
|
// src/commands/run/index.ts
|
|
8097
|
-
import {
|
|
8098
|
-
|
|
8099
|
-
// src/shared/formatElapsed.ts
|
|
8100
|
-
function formatElapsed(ms) {
|
|
8101
|
-
const secs = ms / 1e3;
|
|
8102
|
-
if (secs < 60) return `${secs.toFixed(1)}s`;
|
|
8103
|
-
const mins = Math.floor(secs / 60);
|
|
8104
|
-
const remainSecs = secs - mins * 60;
|
|
8105
|
-
return `${mins}m ${remainSecs.toFixed(1)}s`;
|
|
8106
|
-
}
|
|
8380
|
+
import { execSync as execSync36 } from "child_process";
|
|
8107
8381
|
|
|
8108
8382
|
// src/commands/run/resolveParams.ts
|
|
8109
8383
|
function resolveParams(params, cliArgs) {
|
|
@@ -8129,9 +8403,30 @@ function resolveParams(params, cliArgs) {
|
|
|
8129
8403
|
return resolved;
|
|
8130
8404
|
}
|
|
8131
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
|
+
|
|
8132
8427
|
// src/commands/run/add.ts
|
|
8133
8428
|
import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync25 } from "fs";
|
|
8134
|
-
import { join as
|
|
8429
|
+
import { join as join35 } from "path";
|
|
8135
8430
|
function findAddIndex() {
|
|
8136
8431
|
const addIndex = process.argv.indexOf("add");
|
|
8137
8432
|
if (addIndex === -1 || addIndex + 2 >= process.argv.length) return -1;
|
|
@@ -8185,7 +8480,7 @@ function saveNewRunConfig(name, command, args) {
|
|
|
8185
8480
|
saveConfig(config);
|
|
8186
8481
|
}
|
|
8187
8482
|
function createCommandFile(name) {
|
|
8188
|
-
const dir =
|
|
8483
|
+
const dir = join35(".claude", "commands");
|
|
8189
8484
|
mkdirSync12(dir, { recursive: true });
|
|
8190
8485
|
const content = `---
|
|
8191
8486
|
description: Run ${name}
|
|
@@ -8193,7 +8488,7 @@ description: Run ${name}
|
|
|
8193
8488
|
|
|
8194
8489
|
Run \`assist run ${name} $ARGUMENTS 2>&1\`.
|
|
8195
8490
|
`;
|
|
8196
|
-
const filePath =
|
|
8491
|
+
const filePath = join35(dir, `${name}.md`);
|
|
8197
8492
|
writeFileSync25(filePath, content);
|
|
8198
8493
|
console.log(`Created command file: ${filePath}`);
|
|
8199
8494
|
}
|
|
@@ -8235,25 +8530,6 @@ function findRunConfig(name) {
|
|
|
8235
8530
|
const configs = requireRunConfigs();
|
|
8236
8531
|
return configs.find((r) => r.name === name) ?? exitWithConfigNotFound(name, configs);
|
|
8237
8532
|
}
|
|
8238
|
-
function onSpawnError(err) {
|
|
8239
|
-
console.error(`Failed to execute command: ${err.message}`);
|
|
8240
|
-
process.exit(1);
|
|
8241
|
-
}
|
|
8242
|
-
function spawnCommand2(fullCommand, env) {
|
|
8243
|
-
const start3 = Date.now();
|
|
8244
|
-
const child = spawn5(fullCommand, [], {
|
|
8245
|
-
stdio: "inherit",
|
|
8246
|
-
shell: true,
|
|
8247
|
-
env: env ? { ...process.env, ...expandEnv(env) } : void 0
|
|
8248
|
-
});
|
|
8249
|
-
child.on("close", (code) => {
|
|
8250
|
-
const elapsed = formatElapsed(Date.now() - start3);
|
|
8251
|
-
console.log(`
|
|
8252
|
-
Done in ${elapsed}`);
|
|
8253
|
-
process.exit(code ?? 0);
|
|
8254
|
-
});
|
|
8255
|
-
child.on("error", onSpawnError);
|
|
8256
|
-
}
|
|
8257
8533
|
function listRunConfigs() {
|
|
8258
8534
|
const configs = requireRunConfigs();
|
|
8259
8535
|
for (const config of configs) {
|
|
@@ -8261,17 +8537,28 @@ function listRunConfigs() {
|
|
|
8261
8537
|
console.log(`${config.name}: ${config.command}${args}`);
|
|
8262
8538
|
}
|
|
8263
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
|
+
}
|
|
8264
8550
|
function run2(name, args) {
|
|
8265
8551
|
const runConfig = findRunConfig(name);
|
|
8552
|
+
if (runConfig.pre) runPreCommands(runConfig.pre);
|
|
8266
8553
|
const resolved = resolveParams(runConfig.params, args);
|
|
8267
|
-
|
|
8554
|
+
spawnRunCommand(
|
|
8268
8555
|
buildCommand(runConfig.command, runConfig.args ?? [], resolved),
|
|
8269
8556
|
runConfig.env
|
|
8270
8557
|
);
|
|
8271
8558
|
}
|
|
8272
8559
|
|
|
8273
8560
|
// src/commands/statusLine.ts
|
|
8274
|
-
import
|
|
8561
|
+
import chalk83 from "chalk";
|
|
8275
8562
|
function formatNumber(num) {
|
|
8276
8563
|
return num.toLocaleString("en-US");
|
|
8277
8564
|
}
|
|
@@ -8286,8 +8573,8 @@ function formatTimeLeft(resetsAt) {
|
|
|
8286
8573
|
}
|
|
8287
8574
|
function colorizePercent(pct) {
|
|
8288
8575
|
const label2 = `${Math.round(pct)}%`;
|
|
8289
|
-
if (pct > 80) return
|
|
8290
|
-
if (pct > 40) return
|
|
8576
|
+
if (pct > 80) return chalk83.red(label2);
|
|
8577
|
+
if (pct > 40) return chalk83.yellow(label2);
|
|
8291
8578
|
return label2;
|
|
8292
8579
|
}
|
|
8293
8580
|
function formatLimit(limit, fallbackLabel) {
|
|
@@ -8322,27 +8609,27 @@ async function statusLine() {
|
|
|
8322
8609
|
// src/commands/sync.ts
|
|
8323
8610
|
import * as fs24 from "fs";
|
|
8324
8611
|
import * as os from "os";
|
|
8325
|
-
import * as
|
|
8612
|
+
import * as path42 from "path";
|
|
8326
8613
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
8327
8614
|
|
|
8328
8615
|
// src/commands/sync/syncClaudeMd.ts
|
|
8329
8616
|
import * as fs22 from "fs";
|
|
8330
|
-
import * as
|
|
8331
|
-
import
|
|
8617
|
+
import * as path40 from "path";
|
|
8618
|
+
import chalk84 from "chalk";
|
|
8332
8619
|
async function syncClaudeMd(claudeDir, targetBase) {
|
|
8333
|
-
const source =
|
|
8334
|
-
const target =
|
|
8620
|
+
const source = path40.join(claudeDir, "CLAUDE.md");
|
|
8621
|
+
const target = path40.join(targetBase, "CLAUDE.md");
|
|
8335
8622
|
const sourceContent = fs22.readFileSync(source, "utf-8");
|
|
8336
8623
|
if (fs22.existsSync(target)) {
|
|
8337
8624
|
const targetContent = fs22.readFileSync(target, "utf-8");
|
|
8338
8625
|
if (sourceContent !== targetContent) {
|
|
8339
8626
|
console.log(
|
|
8340
|
-
|
|
8627
|
+
chalk84.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
|
|
8341
8628
|
);
|
|
8342
8629
|
console.log();
|
|
8343
8630
|
printDiff(targetContent, sourceContent);
|
|
8344
8631
|
const confirm = await promptConfirm(
|
|
8345
|
-
|
|
8632
|
+
chalk84.red("Overwrite existing CLAUDE.md?"),
|
|
8346
8633
|
false
|
|
8347
8634
|
);
|
|
8348
8635
|
if (!confirm) {
|
|
@@ -8357,11 +8644,11 @@ async function syncClaudeMd(claudeDir, targetBase) {
|
|
|
8357
8644
|
|
|
8358
8645
|
// src/commands/sync/syncSettings.ts
|
|
8359
8646
|
import * as fs23 from "fs";
|
|
8360
|
-
import * as
|
|
8361
|
-
import
|
|
8647
|
+
import * as path41 from "path";
|
|
8648
|
+
import chalk85 from "chalk";
|
|
8362
8649
|
async function syncSettings(claudeDir, targetBase, options2) {
|
|
8363
|
-
const source =
|
|
8364
|
-
const target =
|
|
8650
|
+
const source = path41.join(claudeDir, "settings.json");
|
|
8651
|
+
const target = path41.join(targetBase, "settings.json");
|
|
8365
8652
|
const sourceContent = fs23.readFileSync(source, "utf-8");
|
|
8366
8653
|
const mergedContent = JSON.stringify(JSON.parse(sourceContent), null, " ");
|
|
8367
8654
|
if (fs23.existsSync(target)) {
|
|
@@ -8374,14 +8661,14 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
8374
8661
|
if (mergedContent !== normalizedTarget) {
|
|
8375
8662
|
if (!options2?.yes) {
|
|
8376
8663
|
console.log(
|
|
8377
|
-
|
|
8664
|
+
chalk85.yellow(
|
|
8378
8665
|
"\n\u26A0\uFE0F Warning: settings.json differs from existing file"
|
|
8379
8666
|
)
|
|
8380
8667
|
);
|
|
8381
8668
|
console.log();
|
|
8382
8669
|
printDiff(targetContent, mergedContent);
|
|
8383
8670
|
const confirm = await promptConfirm(
|
|
8384
|
-
|
|
8671
|
+
chalk85.red("Overwrite existing settings.json?"),
|
|
8385
8672
|
false
|
|
8386
8673
|
);
|
|
8387
8674
|
if (!confirm) {
|
|
@@ -8397,37 +8684,37 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
8397
8684
|
|
|
8398
8685
|
// src/commands/sync.ts
|
|
8399
8686
|
var __filename4 = fileURLToPath7(import.meta.url);
|
|
8400
|
-
var __dirname7 =
|
|
8687
|
+
var __dirname7 = path42.dirname(__filename4);
|
|
8401
8688
|
async function sync(options2) {
|
|
8402
|
-
const claudeDir =
|
|
8403
|
-
const targetBase =
|
|
8689
|
+
const claudeDir = path42.join(__dirname7, "..", "claude");
|
|
8690
|
+
const targetBase = path42.join(os.homedir(), ".claude");
|
|
8404
8691
|
syncCommands(claudeDir, targetBase);
|
|
8405
8692
|
await syncSettings(claudeDir, targetBase, { yes: options2?.yes });
|
|
8406
8693
|
await syncClaudeMd(claudeDir, targetBase);
|
|
8407
8694
|
}
|
|
8408
8695
|
function syncCommands(claudeDir, targetBase) {
|
|
8409
|
-
const sourceDir =
|
|
8410
|
-
const targetDir =
|
|
8696
|
+
const sourceDir = path42.join(claudeDir, "commands");
|
|
8697
|
+
const targetDir = path42.join(targetBase, "commands");
|
|
8411
8698
|
fs24.mkdirSync(targetDir, { recursive: true });
|
|
8412
8699
|
const files = fs24.readdirSync(sourceDir);
|
|
8413
8700
|
for (const file of files) {
|
|
8414
|
-
fs24.copyFileSync(
|
|
8701
|
+
fs24.copyFileSync(path42.join(sourceDir, file), path42.join(targetDir, file));
|
|
8415
8702
|
console.log(`Copied ${file} to ${targetDir}`);
|
|
8416
8703
|
}
|
|
8417
8704
|
console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
|
|
8418
8705
|
}
|
|
8419
8706
|
|
|
8420
8707
|
// src/commands/update.ts
|
|
8421
|
-
import { execSync as
|
|
8422
|
-
import * as
|
|
8708
|
+
import { execSync as execSync37 } from "child_process";
|
|
8709
|
+
import * as path43 from "path";
|
|
8423
8710
|
function isGlobalNpmInstall(dir) {
|
|
8424
8711
|
try {
|
|
8425
|
-
const resolved =
|
|
8426
|
-
if (resolved.split(
|
|
8712
|
+
const resolved = path43.resolve(dir);
|
|
8713
|
+
if (resolved.split(path43.sep).includes("node_modules")) {
|
|
8427
8714
|
return true;
|
|
8428
8715
|
}
|
|
8429
|
-
const globalPrefix =
|
|
8430
|
-
return resolved.toLowerCase().startsWith(
|
|
8716
|
+
const globalPrefix = execSync37("npm prefix -g", { stdio: "pipe" }).toString().trim();
|
|
8717
|
+
return resolved.toLowerCase().startsWith(path43.resolve(globalPrefix).toLowerCase());
|
|
8431
8718
|
} catch {
|
|
8432
8719
|
return false;
|
|
8433
8720
|
}
|
|
@@ -8437,18 +8724,18 @@ async function update() {
|
|
|
8437
8724
|
console.log(`Assist is installed at: ${installDir}`);
|
|
8438
8725
|
if (isGitRepo(installDir)) {
|
|
8439
8726
|
console.log("Detected git repo installation, pulling latest...");
|
|
8440
|
-
|
|
8727
|
+
execSync37("git pull", { cwd: installDir, stdio: "inherit" });
|
|
8441
8728
|
console.log("Installing dependencies...");
|
|
8442
|
-
|
|
8729
|
+
execSync37("npm i", { cwd: installDir, stdio: "inherit" });
|
|
8443
8730
|
console.log("Building...");
|
|
8444
|
-
|
|
8731
|
+
execSync37("npm run build", { cwd: installDir, stdio: "inherit" });
|
|
8445
8732
|
console.log("Syncing commands...");
|
|
8446
|
-
|
|
8733
|
+
execSync37("assist sync", { stdio: "inherit" });
|
|
8447
8734
|
} else if (isGlobalNpmInstall(installDir)) {
|
|
8448
8735
|
console.log("Detected global npm installation, updating...");
|
|
8449
|
-
|
|
8736
|
+
execSync37("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
|
|
8450
8737
|
console.log("Syncing commands...");
|
|
8451
|
-
|
|
8738
|
+
execSync37("assist sync", { stdio: "inherit" });
|
|
8452
8739
|
} else {
|
|
8453
8740
|
console.error(
|
|
8454
8741
|
"Could not determine installation method. Expected a git repo or global npm install."
|
|
@@ -8495,7 +8782,7 @@ registerRefactor(program);
|
|
|
8495
8782
|
registerDevlog(program);
|
|
8496
8783
|
registerDeploy(program);
|
|
8497
8784
|
registerComplexity(program);
|
|
8498
|
-
|
|
8785
|
+
registerDotnet(program);
|
|
8499
8786
|
registerNews(program);
|
|
8500
8787
|
registerRavendb(program);
|
|
8501
8788
|
registerTranscript(program);
|