@staff0rd/assist 0.120.1 → 0.122.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 +952 -660
- 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.122.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(),
|
|
@@ -162,6 +163,11 @@ var assistConfigSchema = z.strictObject({
|
|
|
162
163
|
news: z.strictObject({
|
|
163
164
|
feeds: z.array(z.string()).default([])
|
|
164
165
|
}).default({ feeds: [] }),
|
|
166
|
+
dotnet: z.strictObject({
|
|
167
|
+
inspect: z.strictObject({
|
|
168
|
+
suppress: z.array(z.string()).default([])
|
|
169
|
+
}).default({ suppress: [] })
|
|
170
|
+
}).optional(),
|
|
165
171
|
ravendb: z.strictObject({
|
|
166
172
|
connections: z.array(
|
|
167
173
|
z.strictObject({
|
|
@@ -351,9 +357,9 @@ function isTraversable(value) {
|
|
|
351
357
|
function stepInto(current, key) {
|
|
352
358
|
return isTraversable(current) ? current[key] : void 0;
|
|
353
359
|
}
|
|
354
|
-
function getNestedValue(obj,
|
|
360
|
+
function getNestedValue(obj, path44) {
|
|
355
361
|
let current = obj;
|
|
356
|
-
for (const key of
|
|
362
|
+
for (const key of path44.split(".")) current = stepInto(current, key);
|
|
357
363
|
return current;
|
|
358
364
|
}
|
|
359
365
|
|
|
@@ -394,8 +400,8 @@ function stepIntoNested(container, key, nextKey) {
|
|
|
394
400
|
}
|
|
395
401
|
return ensureObject(container, resolved);
|
|
396
402
|
}
|
|
397
|
-
function setNestedValue(obj,
|
|
398
|
-
const keys =
|
|
403
|
+
function setNestedValue(obj, path44, value) {
|
|
404
|
+
const keys = path44.split(".");
|
|
399
405
|
const result = { ...obj };
|
|
400
406
|
let current = result;
|
|
401
407
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
@@ -2841,12 +2847,12 @@ function getCliReadsPath() {
|
|
|
2841
2847
|
var cachedLines;
|
|
2842
2848
|
function getCliReadsLines() {
|
|
2843
2849
|
if (cachedLines) return cachedLines;
|
|
2844
|
-
const
|
|
2845
|
-
if (!existsSync16(
|
|
2850
|
+
const path44 = getCliReadsPath();
|
|
2851
|
+
if (!existsSync16(path44)) {
|
|
2846
2852
|
cachedLines = [];
|
|
2847
2853
|
return cachedLines;
|
|
2848
2854
|
}
|
|
2849
|
-
cachedLines = readFileSync13(
|
|
2855
|
+
cachedLines = readFileSync13(path44, "utf-8").split("\n").filter((line) => line.trim() !== "");
|
|
2850
2856
|
return cachedLines;
|
|
2851
2857
|
}
|
|
2852
2858
|
function loadCliReads() {
|
|
@@ -3198,14 +3204,14 @@ function showProgress(p, label2) {
|
|
|
3198
3204
|
const pct = Math.round(p.done / p.total * 100);
|
|
3199
3205
|
process.stderr.write(`\r\x1B[K[${pct}%] Scanning ${label2}...`);
|
|
3200
3206
|
}
|
|
3201
|
-
async function resolveCommand(cli,
|
|
3202
|
-
showProgress(p,
|
|
3203
|
-
const subHelp = await runHelp([cli, ...
|
|
3207
|
+
async function resolveCommand(cli, path44, description, depth, p) {
|
|
3208
|
+
showProgress(p, path44.join(" "));
|
|
3209
|
+
const subHelp = await runHelp([cli, ...path44]);
|
|
3204
3210
|
if (!subHelp || !hasSubcommands(subHelp)) {
|
|
3205
|
-
return [{ path:
|
|
3211
|
+
return [{ path: path44, description }];
|
|
3206
3212
|
}
|
|
3207
|
-
const children = await discoverAt(cli,
|
|
3208
|
-
return children.length > 0 ? children : [{ path:
|
|
3213
|
+
const children = await discoverAt(cli, path44, depth + 1, p);
|
|
3214
|
+
return children.length > 0 ? children : [{ path: path44, description }];
|
|
3209
3215
|
}
|
|
3210
3216
|
async function discoverAt(cli, parentPath, depth, p) {
|
|
3211
3217
|
if (depth > SAFETY_DEPTH) return [];
|
|
@@ -3353,9 +3359,9 @@ function logPath(cli) {
|
|
|
3353
3359
|
return join12(homedir4(), ".assist", `cli-discover-${safeName}.log`);
|
|
3354
3360
|
}
|
|
3355
3361
|
function readCache(cli) {
|
|
3356
|
-
const
|
|
3357
|
-
if (!existsSync18(
|
|
3358
|
-
return readFileSync15(
|
|
3362
|
+
const path44 = logPath(cli);
|
|
3363
|
+
if (!existsSync18(path44)) return void 0;
|
|
3364
|
+
return readFileSync15(path44, "utf-8");
|
|
3359
3365
|
}
|
|
3360
3366
|
function writeCache(cli, output) {
|
|
3361
3367
|
const dir = join12(homedir4(), ".assist");
|
|
@@ -4437,234 +4443,88 @@ function registerDevlog(program2) {
|
|
|
4437
4443
|
).option("--all", "Show all non-archived repos regardless of push date").action(repos);
|
|
4438
4444
|
}
|
|
4439
4445
|
|
|
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";
|
|
4446
|
+
// src/commands/dotnet/checkBuildLocks.ts
|
|
4447
|
+
import { closeSync, openSync, readdirSync as readdirSync2 } from "fs";
|
|
4448
|
+
import { join as join16 } from "path";
|
|
4502
4449
|
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
|
-
}
|
|
4450
|
+
|
|
4451
|
+
// src/shared/findRepoRoot.ts
|
|
4452
|
+
import { existsSync as existsSync20 } from "fs";
|
|
4453
|
+
import path21 from "path";
|
|
4454
|
+
function findRepoRoot(dir) {
|
|
4455
|
+
let current = dir;
|
|
4456
|
+
while (current !== path21.dirname(current)) {
|
|
4457
|
+
if (existsSync20(path21.join(current, ".git"))) {
|
|
4458
|
+
return current;
|
|
4522
4459
|
}
|
|
4523
|
-
|
|
4524
|
-
process.exit(1);
|
|
4460
|
+
current = path21.dirname(current);
|
|
4525
4461
|
}
|
|
4526
|
-
return
|
|
4462
|
+
return null;
|
|
4527
4463
|
}
|
|
4528
4464
|
|
|
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;
|
|
4465
|
+
// src/commands/dotnet/checkBuildLocks.ts
|
|
4466
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "packages"]);
|
|
4467
|
+
function isLockedDll(debugDir) {
|
|
4468
|
+
let files;
|
|
4469
|
+
try {
|
|
4470
|
+
files = readdirSync2(debugDir, { recursive: true });
|
|
4471
|
+
} catch {
|
|
4472
|
+
return null;
|
|
4547
4473
|
}
|
|
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)) {
|
|
4474
|
+
for (const file of files) {
|
|
4475
|
+
if (!file.toLowerCase().endsWith(".dll")) continue;
|
|
4476
|
+
const dllPath = join16(debugDir, file);
|
|
4568
4477
|
try {
|
|
4569
|
-
|
|
4478
|
+
const fd = openSync(dllPath, "r+");
|
|
4479
|
+
closeSync(fd);
|
|
4570
4480
|
} catch {
|
|
4571
|
-
return
|
|
4481
|
+
return dllPath;
|
|
4572
4482
|
}
|
|
4573
4483
|
}
|
|
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 };
|
|
4484
|
+
return null;
|
|
4603
4485
|
}
|
|
4604
|
-
|
|
4605
|
-
|
|
4486
|
+
function findFirstLockedDll(dir) {
|
|
4487
|
+
let entries;
|
|
4606
4488
|
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);
|
|
4489
|
+
entries = readdirSync2(dir);
|
|
4490
|
+
} catch {
|
|
4491
|
+
return null;
|
|
4624
4492
|
}
|
|
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));
|
|
4493
|
+
if (entries.includes("bin")) {
|
|
4494
|
+
const locked = isLockedDll(join16(dir, "bin", "Debug"));
|
|
4495
|
+
if (locked) return locked;
|
|
4636
4496
|
}
|
|
4637
|
-
|
|
4638
|
-
if (
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
console.log(adfToText(description));
|
|
4643
|
-
} else {
|
|
4644
|
-
console.log(JSON.stringify(description, null, 2));
|
|
4645
|
-
}
|
|
4497
|
+
for (const entry of entries) {
|
|
4498
|
+
if (SKIP_DIRS.has(entry) || entry === "bin" || entry.startsWith("."))
|
|
4499
|
+
continue;
|
|
4500
|
+
const found = findFirstLockedDll(join16(dir, entry));
|
|
4501
|
+
if (found) return found;
|
|
4646
4502
|
}
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4503
|
+
return null;
|
|
4504
|
+
}
|
|
4505
|
+
function getSearchRoot() {
|
|
4506
|
+
return findRepoRoot(process.cwd()) ?? process.cwd();
|
|
4507
|
+
}
|
|
4508
|
+
function checkBuildLocks(startDir) {
|
|
4509
|
+
const locked = findFirstLockedDll(startDir ?? getSearchRoot());
|
|
4510
|
+
if (locked) {
|
|
4511
|
+
console.error(
|
|
4512
|
+
chalk48.red("Build output locked (is VS debugging?): ") + locked
|
|
4650
4513
|
);
|
|
4514
|
+
process.exit(1);
|
|
4651
4515
|
}
|
|
4652
4516
|
}
|
|
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));
|
|
4517
|
+
async function checkBuildLocksCommand() {
|
|
4518
|
+
checkBuildLocks();
|
|
4519
|
+
console.log(chalk48.green("No build locks detected"));
|
|
4660
4520
|
}
|
|
4661
4521
|
|
|
4662
|
-
// src/commands/
|
|
4663
|
-
import { readFileSync as
|
|
4664
|
-
import
|
|
4522
|
+
// src/commands/dotnet/buildTree.ts
|
|
4523
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
4524
|
+
import path22 from "path";
|
|
4665
4525
|
var PROJECT_REF_RE = /<ProjectReference\s+Include="([^"]+)"/g;
|
|
4666
4526
|
function getProjectRefs(csprojPath) {
|
|
4667
|
-
const content =
|
|
4527
|
+
const content = readFileSync18(csprojPath, "utf-8");
|
|
4668
4528
|
const refs = [];
|
|
4669
4529
|
for (const match of content.matchAll(PROJECT_REF_RE)) {
|
|
4670
4530
|
refs.push(match[1].replace(/\\/g, "/"));
|
|
@@ -4672,16 +4532,16 @@ function getProjectRefs(csprojPath) {
|
|
|
4672
4532
|
return refs;
|
|
4673
4533
|
}
|
|
4674
4534
|
function buildTree(csprojPath, repoRoot, visited = /* @__PURE__ */ new Set()) {
|
|
4675
|
-
const abs =
|
|
4676
|
-
const rel =
|
|
4535
|
+
const abs = path22.resolve(csprojPath);
|
|
4536
|
+
const rel = path22.relative(repoRoot, abs);
|
|
4677
4537
|
const node = { path: abs, relativePath: rel, children: [] };
|
|
4678
4538
|
if (visited.has(abs)) return node;
|
|
4679
4539
|
visited.add(abs);
|
|
4680
|
-
const dir =
|
|
4540
|
+
const dir = path22.dirname(abs);
|
|
4681
4541
|
for (const ref of getProjectRefs(abs)) {
|
|
4682
|
-
const childAbs =
|
|
4542
|
+
const childAbs = path22.resolve(dir, ref);
|
|
4683
4543
|
try {
|
|
4684
|
-
|
|
4544
|
+
readFileSync18(childAbs);
|
|
4685
4545
|
node.children.push(buildTree(childAbs, repoRoot, visited));
|
|
4686
4546
|
} catch {
|
|
4687
4547
|
node.children.push({
|
|
@@ -4705,22 +4565,22 @@ function collectAllDeps(node) {
|
|
|
4705
4565
|
return result;
|
|
4706
4566
|
}
|
|
4707
4567
|
|
|
4708
|
-
// src/commands/
|
|
4709
|
-
import { readdirSync as
|
|
4710
|
-
import
|
|
4568
|
+
// src/commands/dotnet/findContainingSolutions.ts
|
|
4569
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync19, statSync } from "fs";
|
|
4570
|
+
import path23 from "path";
|
|
4711
4571
|
function findSlnFiles(dir, maxDepth, depth = 0) {
|
|
4712
4572
|
if (depth > maxDepth) return [];
|
|
4713
4573
|
const results = [];
|
|
4714
4574
|
let entries;
|
|
4715
4575
|
try {
|
|
4716
|
-
entries =
|
|
4576
|
+
entries = readdirSync3(dir);
|
|
4717
4577
|
} catch {
|
|
4718
4578
|
return results;
|
|
4719
4579
|
}
|
|
4720
4580
|
for (const entry of entries) {
|
|
4721
4581
|
if (entry.startsWith(".") || entry === "node_modules" || entry === "packages")
|
|
4722
4582
|
continue;
|
|
4723
|
-
const full =
|
|
4583
|
+
const full = path23.join(dir, entry);
|
|
4724
4584
|
try {
|
|
4725
4585
|
const stat = statSync(full);
|
|
4726
4586
|
if (stat.isFile() && entry.endsWith(".sln")) {
|
|
@@ -4734,16 +4594,16 @@ function findSlnFiles(dir, maxDepth, depth = 0) {
|
|
|
4734
4594
|
return results;
|
|
4735
4595
|
}
|
|
4736
4596
|
function findContainingSolutions(csprojPath, repoRoot) {
|
|
4737
|
-
const csprojAbs =
|
|
4738
|
-
const csprojBasename =
|
|
4597
|
+
const csprojAbs = path23.resolve(csprojPath);
|
|
4598
|
+
const csprojBasename = path23.basename(csprojAbs);
|
|
4739
4599
|
const slnFiles = findSlnFiles(repoRoot, 3);
|
|
4740
4600
|
const matches = [];
|
|
4741
4601
|
const pattern2 = new RegExp(`[\\\\"/]${escapeRegex(csprojBasename)}"`);
|
|
4742
4602
|
for (const sln of slnFiles) {
|
|
4743
4603
|
try {
|
|
4744
|
-
const content =
|
|
4604
|
+
const content = readFileSync19(sln, "utf-8");
|
|
4745
4605
|
if (pattern2.test(content)) {
|
|
4746
|
-
matches.push(
|
|
4606
|
+
matches.push(path23.relative(repoRoot, sln));
|
|
4747
4607
|
}
|
|
4748
4608
|
} catch {
|
|
4749
4609
|
}
|
|
@@ -4754,31 +4614,31 @@ function escapeRegex(s) {
|
|
|
4754
4614
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4755
4615
|
}
|
|
4756
4616
|
|
|
4757
|
-
// src/commands/
|
|
4758
|
-
import
|
|
4617
|
+
// src/commands/dotnet/printTree.ts
|
|
4618
|
+
import chalk49 from "chalk";
|
|
4759
4619
|
function printNodes(nodes, prefix2) {
|
|
4760
4620
|
for (let i = 0; i < nodes.length; i++) {
|
|
4761
4621
|
const isLast = i === nodes.length - 1;
|
|
4762
4622
|
const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
4763
4623
|
const childPrefix = isLast ? " " : "\u2502 ";
|
|
4764
4624
|
const isMissing = nodes[i].relativePath.startsWith("[MISSING]");
|
|
4765
|
-
const label2 = isMissing ?
|
|
4625
|
+
const label2 = isMissing ? chalk49.red(nodes[i].relativePath) : nodes[i].relativePath;
|
|
4766
4626
|
console.log(`${prefix2}${connector}${label2}`);
|
|
4767
4627
|
printNodes(nodes[i].children, prefix2 + childPrefix);
|
|
4768
4628
|
}
|
|
4769
4629
|
}
|
|
4770
4630
|
function printTree(tree, totalCount, solutions) {
|
|
4771
|
-
console.log(
|
|
4772
|
-
console.log(
|
|
4631
|
+
console.log(chalk49.bold("\nProject Dependency Tree"));
|
|
4632
|
+
console.log(chalk49.cyan(tree.relativePath));
|
|
4773
4633
|
printNodes(tree.children, "");
|
|
4774
|
-
console.log(
|
|
4634
|
+
console.log(chalk49.dim(`
|
|
4775
4635
|
${totalCount} projects total (including root)`));
|
|
4776
|
-
console.log(
|
|
4636
|
+
console.log(chalk49.bold("\nSolution Membership"));
|
|
4777
4637
|
if (solutions.length === 0) {
|
|
4778
|
-
console.log(
|
|
4638
|
+
console.log(chalk49.yellow(" Not found in any .sln"));
|
|
4779
4639
|
} else {
|
|
4780
4640
|
for (const sln of solutions) {
|
|
4781
|
-
console.log(` ${
|
|
4641
|
+
console.log(` ${chalk49.green(sln)}`);
|
|
4782
4642
|
}
|
|
4783
4643
|
}
|
|
4784
4644
|
console.log();
|
|
@@ -4804,41 +4664,25 @@ function printJson(tree, totalCount, solutions) {
|
|
|
4804
4664
|
);
|
|
4805
4665
|
}
|
|
4806
4666
|
|
|
4807
|
-
// src/commands/
|
|
4808
|
-
import { existsSync as existsSync22 } from "fs";
|
|
4809
|
-
import path24 from "path";
|
|
4810
|
-
import chalk52 from "chalk";
|
|
4811
|
-
|
|
4812
|
-
// src/shared/findRepoRoot.ts
|
|
4667
|
+
// src/commands/dotnet/resolveCsproj.ts
|
|
4813
4668
|
import { existsSync as existsSync21 } from "fs";
|
|
4814
|
-
import
|
|
4815
|
-
|
|
4816
|
-
let current = dir;
|
|
4817
|
-
while (current !== path23.dirname(current)) {
|
|
4818
|
-
if (existsSync21(path23.join(current, ".git"))) {
|
|
4819
|
-
return current;
|
|
4820
|
-
}
|
|
4821
|
-
current = path23.dirname(current);
|
|
4822
|
-
}
|
|
4823
|
-
return null;
|
|
4824
|
-
}
|
|
4825
|
-
|
|
4826
|
-
// src/commands/netframework/resolveCsproj.ts
|
|
4669
|
+
import path24 from "path";
|
|
4670
|
+
import chalk50 from "chalk";
|
|
4827
4671
|
function resolveCsproj(csprojPath) {
|
|
4828
4672
|
const resolved = path24.resolve(csprojPath);
|
|
4829
|
-
if (!
|
|
4830
|
-
console.error(
|
|
4673
|
+
if (!existsSync21(resolved)) {
|
|
4674
|
+
console.error(chalk50.red(`File not found: ${resolved}`));
|
|
4831
4675
|
process.exit(1);
|
|
4832
4676
|
}
|
|
4833
4677
|
const repoRoot = findRepoRoot(path24.dirname(resolved));
|
|
4834
4678
|
if (!repoRoot) {
|
|
4835
|
-
console.error(
|
|
4679
|
+
console.error(chalk50.red("Could not find git repository root"));
|
|
4836
4680
|
process.exit(1);
|
|
4837
4681
|
}
|
|
4838
4682
|
return { resolved, repoRoot };
|
|
4839
4683
|
}
|
|
4840
4684
|
|
|
4841
|
-
// src/commands/
|
|
4685
|
+
// src/commands/dotnet/deps.ts
|
|
4842
4686
|
async function deps(csprojPath, options2) {
|
|
4843
4687
|
const { resolved, repoRoot } = resolveCsproj(csprojPath);
|
|
4844
4688
|
const tree = buildTree(resolved, repoRoot);
|
|
@@ -4851,13 +4695,13 @@ async function deps(csprojPath, options2) {
|
|
|
4851
4695
|
}
|
|
4852
4696
|
}
|
|
4853
4697
|
|
|
4854
|
-
// src/commands/
|
|
4855
|
-
import
|
|
4698
|
+
// src/commands/dotnet/inSln.ts
|
|
4699
|
+
import chalk51 from "chalk";
|
|
4856
4700
|
async function inSln(csprojPath) {
|
|
4857
4701
|
const { resolved, repoRoot } = resolveCsproj(csprojPath);
|
|
4858
4702
|
const solutions = findContainingSolutions(resolved, repoRoot);
|
|
4859
4703
|
if (solutions.length === 0) {
|
|
4860
|
-
console.log(
|
|
4704
|
+
console.log(chalk51.yellow("Not found in any .sln file"));
|
|
4861
4705
|
process.exit(1);
|
|
4862
4706
|
}
|
|
4863
4707
|
for (const sln of solutions) {
|
|
@@ -4865,21 +4709,465 @@ async function inSln(csprojPath) {
|
|
|
4865
4709
|
}
|
|
4866
4710
|
}
|
|
4867
4711
|
|
|
4868
|
-
// src/commands/
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4712
|
+
// src/commands/dotnet/inspect.ts
|
|
4713
|
+
import { existsSync as existsSync23 } from "fs";
|
|
4714
|
+
import path26 from "path";
|
|
4715
|
+
import chalk55 from "chalk";
|
|
4716
|
+
|
|
4717
|
+
// src/shared/formatElapsed.ts
|
|
4718
|
+
function formatElapsed(ms) {
|
|
4719
|
+
const secs = ms / 1e3;
|
|
4720
|
+
if (secs < 60) return `${secs.toFixed(1)}s`;
|
|
4721
|
+
const mins = Math.floor(secs / 60);
|
|
4722
|
+
const remainSecs = secs - mins * 60;
|
|
4723
|
+
return `${mins}m ${remainSecs.toFixed(1)}s`;
|
|
4873
4724
|
}
|
|
4874
4725
|
|
|
4875
|
-
// src/commands/
|
|
4876
|
-
import
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4726
|
+
// src/commands/dotnet/displayIssues.ts
|
|
4727
|
+
import chalk52 from "chalk";
|
|
4728
|
+
var SEVERITY_COLOR = {
|
|
4729
|
+
ERROR: chalk52.red,
|
|
4730
|
+
WARNING: chalk52.yellow,
|
|
4731
|
+
SUGGESTION: chalk52.cyan,
|
|
4732
|
+
HINT: chalk52.dim
|
|
4733
|
+
};
|
|
4734
|
+
function groupByFile(issues) {
|
|
4735
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
4736
|
+
for (const issue of issues) {
|
|
4737
|
+
const existing = byFile.get(issue.file);
|
|
4738
|
+
if (existing) {
|
|
4739
|
+
existing.push(issue);
|
|
4740
|
+
} else {
|
|
4741
|
+
byFile.set(issue.file, [issue]);
|
|
4742
|
+
}
|
|
4743
|
+
}
|
|
4744
|
+
return byFile;
|
|
4745
|
+
}
|
|
4746
|
+
function displayIssues(issues) {
|
|
4747
|
+
for (const [file, fileIssues] of groupByFile(issues)) {
|
|
4748
|
+
console.log(chalk52.bold(file));
|
|
4749
|
+
for (const issue of fileIssues.sort((a, b) => a.line - b.line)) {
|
|
4750
|
+
const color = SEVERITY_COLOR[issue.severity] ?? chalk52.white;
|
|
4751
|
+
console.log(
|
|
4752
|
+
` ${chalk52.dim(`${issue.line}:`)} ${color(issue.severity)} [${issue.typeId}] ${issue.message}`
|
|
4753
|
+
);
|
|
4754
|
+
}
|
|
4755
|
+
}
|
|
4756
|
+
console.log(chalk52.dim(`
|
|
4757
|
+
${issues.length} issue(s) found`));
|
|
4758
|
+
}
|
|
4759
|
+
|
|
4760
|
+
// src/commands/dotnet/deadCodeRules.ts
|
|
4761
|
+
var deadCodeRules = /* @__PURE__ */ new Set([
|
|
4762
|
+
"UnusedMember.Local",
|
|
4763
|
+
"UnusedType.Local",
|
|
4764
|
+
"UnusedParameter.Global",
|
|
4765
|
+
"UnusedParameter.Local",
|
|
4766
|
+
"NotAccessedField.Global",
|
|
4767
|
+
"NotAccessedField.Local",
|
|
4768
|
+
"NotAccessedVariable.Local",
|
|
4769
|
+
"UnusedAutoPropertyAccessor.Global",
|
|
4770
|
+
"UnusedAutoPropertyAccessor.Local",
|
|
4771
|
+
"ClassNeverInstantiated.Global",
|
|
4772
|
+
"ClassNeverInstantiated.Local",
|
|
4773
|
+
"UnusedMethodReturnValue.Global",
|
|
4774
|
+
"UnusedMethodReturnValue.Local",
|
|
4775
|
+
"UnusedVariable.Compiler",
|
|
4776
|
+
"RedundantUsingDirective"
|
|
4777
|
+
]);
|
|
4778
|
+
|
|
4779
|
+
// src/commands/dotnet/filterIssues.ts
|
|
4780
|
+
function filterIssues(issues, all) {
|
|
4781
|
+
const suppress = new Set(loadConfig().dotnet?.inspect.suppress ?? []);
|
|
4782
|
+
return issues.filter(
|
|
4783
|
+
(i) => (all || deadCodeRules.has(i.typeId)) && !suppress.has(i.typeId)
|
|
4784
|
+
);
|
|
4785
|
+
}
|
|
4786
|
+
|
|
4787
|
+
// src/commands/dotnet/findSolution.ts
|
|
4788
|
+
import { readdirSync as readdirSync4 } from "fs";
|
|
4789
|
+
import { dirname as dirname16, join as join17 } from "path";
|
|
4790
|
+
import chalk53 from "chalk";
|
|
4791
|
+
function findSlnInDir(dir) {
|
|
4792
|
+
try {
|
|
4793
|
+
return readdirSync4(dir).filter((f) => f.endsWith(".sln")).map((f) => join17(dir, f));
|
|
4794
|
+
} catch {
|
|
4795
|
+
return [];
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4798
|
+
function findSolution() {
|
|
4799
|
+
const repoRoot = findRepoRoot(process.cwd());
|
|
4800
|
+
const ceiling = repoRoot ?? process.cwd();
|
|
4801
|
+
let current = process.cwd();
|
|
4802
|
+
while (true) {
|
|
4803
|
+
const slnFiles = findSlnInDir(current);
|
|
4804
|
+
if (slnFiles.length === 1) return slnFiles[0];
|
|
4805
|
+
if (slnFiles.length > 1) {
|
|
4806
|
+
console.error(chalk53.red(`Multiple .sln files found in ${current}:`));
|
|
4807
|
+
for (const f of slnFiles) console.error(` ${f}`);
|
|
4808
|
+
console.error(
|
|
4809
|
+
chalk53.yellow("Specify which one: assist dotnet inspect <sln>")
|
|
4810
|
+
);
|
|
4811
|
+
process.exit(1);
|
|
4812
|
+
}
|
|
4813
|
+
if (current === ceiling) break;
|
|
4814
|
+
current = dirname16(current);
|
|
4815
|
+
}
|
|
4816
|
+
console.error(chalk53.red("No .sln file found between cwd and repo root"));
|
|
4817
|
+
process.exit(1);
|
|
4818
|
+
}
|
|
4819
|
+
|
|
4820
|
+
// src/commands/dotnet/getChangedCsFiles.ts
|
|
4821
|
+
import { execSync as execSync20 } from "child_process";
|
|
4822
|
+
function getChangedCsFiles(ref) {
|
|
4823
|
+
const cmd = ref ? `git diff --name-only ${ref}~1 ${ref}` : "git diff --name-only HEAD";
|
|
4824
|
+
const output = execSync20(cmd, { encoding: "utf-8" }).trim();
|
|
4825
|
+
if (output === "") return [];
|
|
4826
|
+
return output.split("\n").filter((f) => f.toLowerCase().endsWith(".cs"));
|
|
4827
|
+
}
|
|
4828
|
+
|
|
4829
|
+
// src/commands/dotnet/parseInspectReport.ts
|
|
4830
|
+
var LEVEL_TO_SEVERITY = {
|
|
4831
|
+
error: "ERROR",
|
|
4832
|
+
warning: "WARNING",
|
|
4833
|
+
note: "SUGGESTION"
|
|
4834
|
+
};
|
|
4835
|
+
function parseInspectReport(json) {
|
|
4836
|
+
const sarif = JSON.parse(json);
|
|
4837
|
+
const results = sarif.runs?.[0]?.results;
|
|
4838
|
+
if (!Array.isArray(results)) return [];
|
|
4839
|
+
return results.map((r) => ({
|
|
4840
|
+
typeId: r.ruleId,
|
|
4841
|
+
file: r.locations?.[0]?.physicalLocation?.artifactLocation?.uri ?? "",
|
|
4842
|
+
line: r.locations?.[0]?.physicalLocation?.region?.startLine ?? 0,
|
|
4843
|
+
message: r.message?.text ?? "",
|
|
4844
|
+
severity: LEVEL_TO_SEVERITY[r.level] ?? "WARNING"
|
|
4845
|
+
}));
|
|
4846
|
+
}
|
|
4847
|
+
|
|
4848
|
+
// src/commands/dotnet/runInspectCode.ts
|
|
4849
|
+
import { execSync as execSync21 } from "child_process";
|
|
4850
|
+
import { existsSync as existsSync22, readFileSync as readFileSync20, unlinkSync as unlinkSync3 } from "fs";
|
|
4851
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
4852
|
+
import path25 from "path";
|
|
4853
|
+
import chalk54 from "chalk";
|
|
4854
|
+
function assertJbInstalled() {
|
|
4855
|
+
try {
|
|
4856
|
+
execSync21("jb inspectcode --version", { stdio: "pipe" });
|
|
4857
|
+
} catch {
|
|
4858
|
+
console.error(chalk54.red("jb is not installed. Install with:"));
|
|
4859
|
+
console.error(
|
|
4860
|
+
chalk54.yellow(" dotnet tool install -g JetBrains.ReSharper.GlobalTools")
|
|
4861
|
+
);
|
|
4862
|
+
process.exit(1);
|
|
4863
|
+
}
|
|
4864
|
+
}
|
|
4865
|
+
function runInspectCode(slnPath, include, swea) {
|
|
4866
|
+
const reportPath = path25.join(tmpdir2(), `inspect-${Date.now()}.xml`);
|
|
4867
|
+
const sweaFlag = swea ? " --swea" : "";
|
|
4868
|
+
try {
|
|
4869
|
+
execSync21(
|
|
4870
|
+
`jb inspectcode "${slnPath}" -o="${reportPath}" --include="${include}"${sweaFlag} --verbosity=OFF`,
|
|
4871
|
+
{ stdio: "pipe" }
|
|
4872
|
+
);
|
|
4873
|
+
} catch (err) {
|
|
4874
|
+
if (err && typeof err === "object" && "stderr" in err) {
|
|
4875
|
+
process.stderr.write(err.stderr);
|
|
4876
|
+
}
|
|
4877
|
+
console.error(chalk54.red("jb inspectcode failed"));
|
|
4878
|
+
process.exit(1);
|
|
4879
|
+
}
|
|
4880
|
+
if (!existsSync22(reportPath)) {
|
|
4881
|
+
console.error(chalk54.red("Report file not generated"));
|
|
4882
|
+
process.exit(1);
|
|
4883
|
+
}
|
|
4884
|
+
const xml = readFileSync20(reportPath, "utf-8");
|
|
4885
|
+
unlinkSync3(reportPath);
|
|
4886
|
+
return xml;
|
|
4887
|
+
}
|
|
4888
|
+
|
|
4889
|
+
// src/commands/dotnet/inspect.ts
|
|
4890
|
+
function resolveSolution(sln) {
|
|
4891
|
+
if (sln) {
|
|
4892
|
+
const resolved = path26.resolve(sln);
|
|
4893
|
+
if (!existsSync23(resolved)) {
|
|
4894
|
+
console.error(chalk55.red(`Solution file not found: ${resolved}`));
|
|
4895
|
+
process.exit(1);
|
|
4896
|
+
}
|
|
4897
|
+
return resolved;
|
|
4898
|
+
}
|
|
4899
|
+
return findSolution();
|
|
4900
|
+
}
|
|
4901
|
+
function reportResults(issues, elapsed) {
|
|
4902
|
+
if (issues.length > 0) displayIssues(issues);
|
|
4903
|
+
else console.log(chalk55.green("No issues found"));
|
|
4904
|
+
console.log(chalk55.dim(`Completed in ${formatElapsed(elapsed)}`));
|
|
4905
|
+
if (issues.length > 0) process.exit(1);
|
|
4906
|
+
}
|
|
4907
|
+
async function inspect(sln, options2) {
|
|
4908
|
+
const resolved = resolveSolution(sln);
|
|
4909
|
+
checkBuildLocks();
|
|
4910
|
+
assertJbInstalled();
|
|
4911
|
+
const changedFiles = getChangedCsFiles(options2.ref);
|
|
4912
|
+
if (changedFiles.length === 0) {
|
|
4913
|
+
console.log(chalk55.green("No changed .cs files found"));
|
|
4914
|
+
return;
|
|
4915
|
+
}
|
|
4916
|
+
console.log(
|
|
4917
|
+
chalk55.dim(`Inspecting ${changedFiles.length} changed file(s)...`)
|
|
4918
|
+
);
|
|
4919
|
+
const start3 = Date.now();
|
|
4920
|
+
const report = runInspectCode(
|
|
4921
|
+
resolved,
|
|
4922
|
+
changedFiles.join(";"),
|
|
4923
|
+
!!options2.swea
|
|
4924
|
+
);
|
|
4925
|
+
const elapsed = Date.now() - start3;
|
|
4926
|
+
const issues = filterIssues(parseInspectReport(report), !!options2.all);
|
|
4927
|
+
reportResults(issues, elapsed);
|
|
4928
|
+
}
|
|
4929
|
+
|
|
4930
|
+
// src/commands/registerDotnet.ts
|
|
4931
|
+
function registerDotnet(program2) {
|
|
4932
|
+
const cmd = program2.command("dotnet").description(".NET project utilities");
|
|
4933
|
+
cmd.command("inspect").description(
|
|
4934
|
+
"Run JetBrains inspections on changed .cs files to find dead code"
|
|
4935
|
+
).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);
|
|
4936
|
+
cmd.command("check-locks").description("Check if build output files are locked by a debugger").action(checkBuildLocksCommand);
|
|
4937
|
+
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);
|
|
4938
|
+
cmd.command("in-sln").description("Check whether a .csproj is referenced by any .sln file").argument("<csproj>", "Path to a .csproj file").action(inSln);
|
|
4939
|
+
}
|
|
4940
|
+
|
|
4941
|
+
// src/commands/jira/acceptanceCriteria.ts
|
|
4942
|
+
import chalk57 from "chalk";
|
|
4943
|
+
|
|
4944
|
+
// src/commands/jira/adfToText.ts
|
|
4945
|
+
function renderInline(node) {
|
|
4946
|
+
const text = node.text ?? "";
|
|
4947
|
+
if (node.marks?.some((m) => m.type === "code")) return `\`${text}\``;
|
|
4948
|
+
return text;
|
|
4949
|
+
}
|
|
4950
|
+
function renderChildren(node, indent) {
|
|
4951
|
+
return renderNodes(node.content ?? [], indent);
|
|
4952
|
+
}
|
|
4953
|
+
function renderOrderedList(node, indent) {
|
|
4954
|
+
let counter = 0;
|
|
4955
|
+
return (node.content ?? []).map((item) => {
|
|
4956
|
+
counter++;
|
|
4957
|
+
return renderListItem(item, indent, `${counter}.`);
|
|
4958
|
+
}).join("\n");
|
|
4959
|
+
}
|
|
4960
|
+
function renderBulletList(node, indent) {
|
|
4961
|
+
return (node.content ?? []).map((item) => renderListItem(item, indent, "-")).join("\n");
|
|
4962
|
+
}
|
|
4963
|
+
function renderHeading(node, indent) {
|
|
4964
|
+
const level = node.attrs?.level ?? 1;
|
|
4965
|
+
return `${"#".repeat(level)} ${renderChildren(node, indent)}`;
|
|
4966
|
+
}
|
|
4967
|
+
var renderers = {
|
|
4968
|
+
text: (node) => renderInline(node),
|
|
4969
|
+
paragraph: renderChildren,
|
|
4970
|
+
orderedList: renderOrderedList,
|
|
4971
|
+
bulletList: renderBulletList,
|
|
4972
|
+
listItem: (node, indent) => renderListItem(node, indent, "-"),
|
|
4973
|
+
heading: renderHeading,
|
|
4974
|
+
doc: renderChildren
|
|
4975
|
+
};
|
|
4976
|
+
function renderNode(node, indent) {
|
|
4977
|
+
const renderer = renderers[node.type];
|
|
4978
|
+
if (renderer) return renderer(node, indent);
|
|
4979
|
+
return node.content ? renderChildren(node, indent) : "";
|
|
4980
|
+
}
|
|
4981
|
+
function renderNodes(nodes, indent) {
|
|
4982
|
+
return nodes.map((node) => renderNode(node, indent)).join("");
|
|
4983
|
+
}
|
|
4984
|
+
function isListNode(node) {
|
|
4985
|
+
return node.type === "orderedList" || node.type === "bulletList";
|
|
4986
|
+
}
|
|
4987
|
+
function renderListChild(child, indent, pad, marker, isFirst) {
|
|
4988
|
+
if (isListNode(child)) return renderNodes([child], indent + 1);
|
|
4989
|
+
if (child.type !== "paragraph") return renderNode(child, indent);
|
|
4990
|
+
const text = renderChildren(child, indent);
|
|
4991
|
+
return isFirst ? `${pad}${marker} ${text}` : `${pad} ${text}`;
|
|
4992
|
+
}
|
|
4993
|
+
function renderListItem(node, indent, marker) {
|
|
4994
|
+
const pad = " ".repeat(indent);
|
|
4995
|
+
return (node.content ?? []).map((child, i) => renderListChild(child, indent, pad, marker, i === 0)).join("\n");
|
|
4996
|
+
}
|
|
4997
|
+
function adfToText(doc) {
|
|
4998
|
+
return renderNodes([doc], 0);
|
|
4999
|
+
}
|
|
5000
|
+
|
|
5001
|
+
// src/commands/jira/fetchIssue.ts
|
|
5002
|
+
import { execSync as execSync22 } from "child_process";
|
|
5003
|
+
import chalk56 from "chalk";
|
|
5004
|
+
function fetchIssue(issueKey, fields) {
|
|
5005
|
+
let result;
|
|
5006
|
+
try {
|
|
5007
|
+
result = execSync22(
|
|
5008
|
+
`acli jira workitem view ${issueKey} -f ${fields} --json`,
|
|
5009
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
5010
|
+
);
|
|
5011
|
+
} catch (error) {
|
|
5012
|
+
if (error instanceof Error && "stderr" in error) {
|
|
5013
|
+
const stderr = error.stderr;
|
|
5014
|
+
if (stderr.includes("unauthorized")) {
|
|
5015
|
+
console.error(
|
|
5016
|
+
chalk56.red("Jira authentication expired."),
|
|
5017
|
+
"Run",
|
|
5018
|
+
chalk56.cyan("assist jira auth"),
|
|
5019
|
+
"to re-authenticate."
|
|
5020
|
+
);
|
|
5021
|
+
process.exit(1);
|
|
5022
|
+
}
|
|
5023
|
+
}
|
|
5024
|
+
console.error(chalk56.red(`Failed to fetch ${issueKey}.`));
|
|
5025
|
+
process.exit(1);
|
|
5026
|
+
}
|
|
5027
|
+
return JSON.parse(result);
|
|
5028
|
+
}
|
|
5029
|
+
|
|
5030
|
+
// src/commands/jira/acceptanceCriteria.ts
|
|
5031
|
+
var DEFAULT_AC_FIELD = "customfield_11937";
|
|
5032
|
+
function acceptanceCriteria(issueKey) {
|
|
5033
|
+
const config = loadConfig();
|
|
5034
|
+
const field = config.jira?.acField ?? DEFAULT_AC_FIELD;
|
|
5035
|
+
const parsed = fetchIssue(issueKey, field);
|
|
5036
|
+
const acValue = parsed?.fields?.[field];
|
|
5037
|
+
if (!acValue) {
|
|
5038
|
+
console.log(chalk57.yellow(`No acceptance criteria found on ${issueKey}.`));
|
|
5039
|
+
return;
|
|
5040
|
+
}
|
|
5041
|
+
if (typeof acValue === "string") {
|
|
5042
|
+
console.log(acValue);
|
|
5043
|
+
return;
|
|
5044
|
+
}
|
|
5045
|
+
if (acValue.type === "doc") {
|
|
5046
|
+
console.log(adfToText(acValue));
|
|
5047
|
+
return;
|
|
5048
|
+
}
|
|
5049
|
+
console.log(JSON.stringify(acValue, null, 2));
|
|
5050
|
+
}
|
|
5051
|
+
|
|
5052
|
+
// src/commands/jira/jiraAuth.ts
|
|
5053
|
+
import { execSync as execSync23 } from "child_process";
|
|
5054
|
+
import Enquirer from "enquirer";
|
|
5055
|
+
|
|
5056
|
+
// src/shared/loadJson.ts
|
|
5057
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync5, readFileSync as readFileSync21, writeFileSync as writeFileSync17 } from "fs";
|
|
5058
|
+
import { homedir as homedir6 } from "os";
|
|
5059
|
+
import { join as join18 } from "path";
|
|
5060
|
+
function getStoreDir() {
|
|
5061
|
+
return join18(homedir6(), ".assist");
|
|
5062
|
+
}
|
|
5063
|
+
function getStorePath(filename) {
|
|
5064
|
+
return join18(getStoreDir(), filename);
|
|
5065
|
+
}
|
|
5066
|
+
function loadJson(filename) {
|
|
5067
|
+
const path44 = getStorePath(filename);
|
|
5068
|
+
if (existsSync24(path44)) {
|
|
5069
|
+
try {
|
|
5070
|
+
return JSON.parse(readFileSync21(path44, "utf-8"));
|
|
5071
|
+
} catch {
|
|
5072
|
+
return {};
|
|
5073
|
+
}
|
|
5074
|
+
}
|
|
5075
|
+
return {};
|
|
5076
|
+
}
|
|
5077
|
+
function saveJson(filename, data) {
|
|
5078
|
+
const dir = getStoreDir();
|
|
5079
|
+
if (!existsSync24(dir)) {
|
|
5080
|
+
mkdirSync5(dir, { recursive: true });
|
|
5081
|
+
}
|
|
5082
|
+
writeFileSync17(getStorePath(filename), JSON.stringify(data, null, 2));
|
|
5083
|
+
}
|
|
5084
|
+
|
|
5085
|
+
// src/commands/jira/jiraAuth.ts
|
|
5086
|
+
var CONFIG_FILE = "jira.json";
|
|
5087
|
+
async function promptCredentials(config) {
|
|
5088
|
+
const { Input: Input2, Password } = Enquirer;
|
|
5089
|
+
const site = await new Input2({
|
|
5090
|
+
name: "site",
|
|
5091
|
+
message: "Jira site (e.g., mycompany.atlassian.net):",
|
|
5092
|
+
initial: config.site
|
|
5093
|
+
}).run();
|
|
5094
|
+
const email = await new Input2({
|
|
5095
|
+
name: "email",
|
|
5096
|
+
message: "Email:",
|
|
5097
|
+
initial: config.email
|
|
5098
|
+
}).run();
|
|
5099
|
+
const token = await new Password({
|
|
5100
|
+
name: "token",
|
|
5101
|
+
message: "API token (https://id.atlassian.com/manage-profile/security/api-tokens):"
|
|
5102
|
+
}).run();
|
|
5103
|
+
return { site, email, token };
|
|
5104
|
+
}
|
|
5105
|
+
async function jiraAuth() {
|
|
5106
|
+
const config = loadJson(CONFIG_FILE);
|
|
5107
|
+
try {
|
|
5108
|
+
const { site, email, token } = await promptCredentials(config);
|
|
5109
|
+
if (!site || !email || !token) {
|
|
5110
|
+
console.error("All fields are required.");
|
|
5111
|
+
process.exit(1);
|
|
5112
|
+
}
|
|
5113
|
+
execSync23(`acli jira auth login --site ${site} --email "${email}" --token`, {
|
|
5114
|
+
encoding: "utf-8",
|
|
5115
|
+
input: token,
|
|
5116
|
+
stdio: ["pipe", "inherit", "inherit"]
|
|
5117
|
+
});
|
|
5118
|
+
saveJson(CONFIG_FILE, { site, email });
|
|
5119
|
+
console.log("Successfully authenticated with Jira.");
|
|
5120
|
+
} catch (error) {
|
|
5121
|
+
if (error instanceof Error) {
|
|
5122
|
+
console.error("Error authenticating with Jira:", error.message);
|
|
5123
|
+
}
|
|
5124
|
+
process.exit(1);
|
|
5125
|
+
}
|
|
5126
|
+
}
|
|
5127
|
+
|
|
5128
|
+
// src/commands/jira/viewIssue.ts
|
|
5129
|
+
import chalk58 from "chalk";
|
|
5130
|
+
function viewIssue(issueKey) {
|
|
5131
|
+
const parsed = fetchIssue(issueKey, "summary,description");
|
|
5132
|
+
const fields = parsed?.fields;
|
|
5133
|
+
const summary = fields?.summary;
|
|
5134
|
+
const description = fields?.description;
|
|
5135
|
+
if (summary) {
|
|
5136
|
+
console.log(chalk58.bold(summary));
|
|
5137
|
+
}
|
|
5138
|
+
if (description) {
|
|
5139
|
+
if (summary) console.log();
|
|
5140
|
+
if (typeof description === "string") {
|
|
5141
|
+
console.log(description);
|
|
5142
|
+
} else if (description.type === "doc") {
|
|
5143
|
+
console.log(adfToText(description));
|
|
5144
|
+
} else {
|
|
5145
|
+
console.log(JSON.stringify(description, null, 2));
|
|
5146
|
+
}
|
|
5147
|
+
}
|
|
5148
|
+
if (!summary && !description) {
|
|
5149
|
+
console.log(
|
|
5150
|
+
chalk58.yellow(`No summary or description found on ${issueKey}.`)
|
|
5151
|
+
);
|
|
5152
|
+
}
|
|
5153
|
+
}
|
|
5154
|
+
|
|
5155
|
+
// src/commands/registerJira.ts
|
|
5156
|
+
function registerJira(program2) {
|
|
5157
|
+
const jiraCommand = program2.command("jira").description("Jira utilities");
|
|
5158
|
+
jiraCommand.command("auth").description("Authenticate with Jira via API token").action(() => jiraAuth());
|
|
5159
|
+
jiraCommand.command("ac <issue-key>").description("Print acceptance criteria for a Jira issue").action((issueKey) => acceptanceCriteria(issueKey));
|
|
5160
|
+
jiraCommand.command("view <issue-key>").description("Print the title and description of a Jira issue").action((issueKey) => viewIssue(issueKey));
|
|
5161
|
+
}
|
|
5162
|
+
|
|
5163
|
+
// src/commands/news/add/index.ts
|
|
5164
|
+
import chalk59 from "chalk";
|
|
5165
|
+
import enquirer5 from "enquirer";
|
|
5166
|
+
async function add2(url) {
|
|
5167
|
+
if (!url) {
|
|
5168
|
+
const response = await enquirer5.prompt({
|
|
5169
|
+
type: "input",
|
|
5170
|
+
name: "url",
|
|
4883
5171
|
message: "RSS feed URL:",
|
|
4884
5172
|
validate: (value) => {
|
|
4885
5173
|
try {
|
|
@@ -4896,17 +5184,17 @@ async function add2(url) {
|
|
|
4896
5184
|
const news = config.news ?? {};
|
|
4897
5185
|
const feeds = news.feeds ?? [];
|
|
4898
5186
|
if (feeds.includes(url)) {
|
|
4899
|
-
console.log(
|
|
5187
|
+
console.log(chalk59.yellow("Feed already exists in config"));
|
|
4900
5188
|
return;
|
|
4901
5189
|
}
|
|
4902
5190
|
feeds.push(url);
|
|
4903
5191
|
config.news = { ...news, feeds };
|
|
4904
5192
|
saveGlobalConfig(config);
|
|
4905
|
-
console.log(
|
|
5193
|
+
console.log(chalk59.green(`Added feed: ${url}`));
|
|
4906
5194
|
}
|
|
4907
5195
|
|
|
4908
5196
|
// src/commands/news/web/handleRequest.ts
|
|
4909
|
-
import
|
|
5197
|
+
import chalk60 from "chalk";
|
|
4910
5198
|
|
|
4911
5199
|
// src/commands/news/web/shared.ts
|
|
4912
5200
|
import { decodeHTML } from "entities";
|
|
@@ -5042,17 +5330,17 @@ function prefetch() {
|
|
|
5042
5330
|
const config = loadConfig();
|
|
5043
5331
|
const total = config.news.feeds.length;
|
|
5044
5332
|
if (total === 0) return;
|
|
5045
|
-
process.stdout.write(
|
|
5333
|
+
process.stdout.write(chalk60.dim(`Fetching ${total} feed(s)\u2026 `));
|
|
5046
5334
|
prefetchPromise = fetchFeeds(config.news.feeds, (done2, t) => {
|
|
5047
5335
|
const width = 20;
|
|
5048
5336
|
const filled = Math.round(done2 / t * width);
|
|
5049
5337
|
const bar = `${"\u2588".repeat(filled)}${"\u2591".repeat(width - filled)}`;
|
|
5050
5338
|
process.stdout.write(
|
|
5051
|
-
`\r${
|
|
5339
|
+
`\r${chalk60.dim(`Fetching feeds ${bar} ${done2}/${t}`)}`
|
|
5052
5340
|
);
|
|
5053
5341
|
}).then((items) => {
|
|
5054
5342
|
process.stdout.write(
|
|
5055
|
-
`\r${
|
|
5343
|
+
`\r${chalk60.green(`Fetched ${items.length} items from ${total} feed(s)`)}
|
|
5056
5344
|
`
|
|
5057
5345
|
);
|
|
5058
5346
|
cachedItems = items;
|
|
@@ -5098,12 +5386,12 @@ function registerNews(program2) {
|
|
|
5098
5386
|
|
|
5099
5387
|
// src/commands/prs/comment.ts
|
|
5100
5388
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
5101
|
-
import { unlinkSync as
|
|
5102
|
-
import { tmpdir as
|
|
5103
|
-
import { join as
|
|
5389
|
+
import { unlinkSync as unlinkSync4, writeFileSync as writeFileSync18 } from "fs";
|
|
5390
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
5391
|
+
import { join as join19 } from "path";
|
|
5104
5392
|
|
|
5105
5393
|
// src/commands/prs/shared.ts
|
|
5106
|
-
import { execSync as
|
|
5394
|
+
import { execSync as execSync24 } from "child_process";
|
|
5107
5395
|
function isGhNotInstalled(error) {
|
|
5108
5396
|
if (error instanceof Error) {
|
|
5109
5397
|
const msg = error.message.toLowerCase();
|
|
@@ -5119,14 +5407,14 @@ function isNotFound(error) {
|
|
|
5119
5407
|
}
|
|
5120
5408
|
function getRepoInfo() {
|
|
5121
5409
|
const repoInfo = JSON.parse(
|
|
5122
|
-
|
|
5410
|
+
execSync24("gh repo view --json owner,name", { encoding: "utf-8" })
|
|
5123
5411
|
);
|
|
5124
5412
|
return { org: repoInfo.owner.login, repo: repoInfo.name };
|
|
5125
5413
|
}
|
|
5126
5414
|
function getCurrentPrNumber() {
|
|
5127
5415
|
try {
|
|
5128
5416
|
const prInfo = JSON.parse(
|
|
5129
|
-
|
|
5417
|
+
execSync24("gh pr view --json number", { encoding: "utf-8" })
|
|
5130
5418
|
);
|
|
5131
5419
|
return prInfo.number;
|
|
5132
5420
|
} catch (error) {
|
|
@@ -5140,7 +5428,7 @@ function getCurrentPrNumber() {
|
|
|
5140
5428
|
function getCurrentPrNodeId() {
|
|
5141
5429
|
try {
|
|
5142
5430
|
const prInfo = JSON.parse(
|
|
5143
|
-
|
|
5431
|
+
execSync24("gh pr view --json id", { encoding: "utf-8" })
|
|
5144
5432
|
);
|
|
5145
5433
|
return prInfo.id;
|
|
5146
5434
|
} catch (error) {
|
|
@@ -5167,12 +5455,12 @@ function validateLine(line) {
|
|
|
5167
5455
|
process.exit(1);
|
|
5168
5456
|
}
|
|
5169
5457
|
}
|
|
5170
|
-
function comment(
|
|
5458
|
+
function comment(path44, line, body) {
|
|
5171
5459
|
validateBody(body);
|
|
5172
5460
|
validateLine(line);
|
|
5173
5461
|
try {
|
|
5174
5462
|
const prId = getCurrentPrNodeId();
|
|
5175
|
-
const queryFile =
|
|
5463
|
+
const queryFile = join19(tmpdir3(), `gh-query-${Date.now()}.graphql`);
|
|
5176
5464
|
writeFileSync18(queryFile, MUTATION);
|
|
5177
5465
|
try {
|
|
5178
5466
|
const result = spawnSync2(
|
|
@@ -5187,7 +5475,7 @@ function comment(path42, line, body) {
|
|
|
5187
5475
|
"-f",
|
|
5188
5476
|
`body=${body}`,
|
|
5189
5477
|
"-f",
|
|
5190
|
-
`path=${
|
|
5478
|
+
`path=${path44}`,
|
|
5191
5479
|
"-F",
|
|
5192
5480
|
`line=${line}`
|
|
5193
5481
|
],
|
|
@@ -5196,9 +5484,9 @@ function comment(path42, line, body) {
|
|
|
5196
5484
|
if (result.status !== 0) {
|
|
5197
5485
|
throw new Error(result.stderr || result.stdout);
|
|
5198
5486
|
}
|
|
5199
|
-
console.log(`Added review comment on ${
|
|
5487
|
+
console.log(`Added review comment on ${path44}:${line}`);
|
|
5200
5488
|
} finally {
|
|
5201
|
-
|
|
5489
|
+
unlinkSync4(queryFile);
|
|
5202
5490
|
}
|
|
5203
5491
|
} catch (error) {
|
|
5204
5492
|
if (isGhNotInstalled(error)) {
|
|
@@ -5211,55 +5499,55 @@ function comment(path42, line, body) {
|
|
|
5211
5499
|
}
|
|
5212
5500
|
|
|
5213
5501
|
// src/commands/prs/fixed.ts
|
|
5214
|
-
import { execSync as
|
|
5502
|
+
import { execSync as execSync26 } from "child_process";
|
|
5215
5503
|
|
|
5216
5504
|
// src/commands/prs/resolveCommentWithReply.ts
|
|
5217
|
-
import { execSync as
|
|
5218
|
-
import { unlinkSync as
|
|
5219
|
-
import { tmpdir as
|
|
5220
|
-
import { join as
|
|
5505
|
+
import { execSync as execSync25 } from "child_process";
|
|
5506
|
+
import { unlinkSync as unlinkSync6, writeFileSync as writeFileSync19 } from "fs";
|
|
5507
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
5508
|
+
import { join as join21 } from "path";
|
|
5221
5509
|
|
|
5222
5510
|
// src/commands/prs/loadCommentsCache.ts
|
|
5223
|
-
import { existsSync as
|
|
5224
|
-
import { join as
|
|
5511
|
+
import { existsSync as existsSync25, readFileSync as readFileSync22, unlinkSync as unlinkSync5 } from "fs";
|
|
5512
|
+
import { join as join20 } from "path";
|
|
5225
5513
|
import { parse as parse2 } from "yaml";
|
|
5226
5514
|
function getCachePath(prNumber) {
|
|
5227
|
-
return
|
|
5515
|
+
return join20(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`);
|
|
5228
5516
|
}
|
|
5229
5517
|
function loadCommentsCache(prNumber) {
|
|
5230
5518
|
const cachePath = getCachePath(prNumber);
|
|
5231
|
-
if (!
|
|
5519
|
+
if (!existsSync25(cachePath)) {
|
|
5232
5520
|
return null;
|
|
5233
5521
|
}
|
|
5234
|
-
const content =
|
|
5522
|
+
const content = readFileSync22(cachePath, "utf-8");
|
|
5235
5523
|
return parse2(content);
|
|
5236
5524
|
}
|
|
5237
5525
|
function deleteCommentsCache(prNumber) {
|
|
5238
5526
|
const cachePath = getCachePath(prNumber);
|
|
5239
|
-
if (
|
|
5240
|
-
|
|
5527
|
+
if (existsSync25(cachePath)) {
|
|
5528
|
+
unlinkSync5(cachePath);
|
|
5241
5529
|
console.log("No more unresolved line comments. Cache dropped.");
|
|
5242
5530
|
}
|
|
5243
5531
|
}
|
|
5244
5532
|
|
|
5245
5533
|
// src/commands/prs/resolveCommentWithReply.ts
|
|
5246
5534
|
function replyToComment(org, repo, prNumber, commentId, message) {
|
|
5247
|
-
|
|
5535
|
+
execSync25(
|
|
5248
5536
|
`gh api repos/${org}/${repo}/pulls/${prNumber}/comments -f body="${message.replace(/"/g, '\\"')}" -F in_reply_to=${commentId}`,
|
|
5249
5537
|
{ stdio: ["inherit", "pipe", "inherit"] }
|
|
5250
5538
|
);
|
|
5251
5539
|
}
|
|
5252
5540
|
function resolveThread(threadId) {
|
|
5253
5541
|
const mutation = `mutation($threadId: ID!) { resolveReviewThread(input: {threadId: $threadId}) { thread { isResolved } } }`;
|
|
5254
|
-
const queryFile =
|
|
5542
|
+
const queryFile = join21(tmpdir4(), `gh-mutation-${Date.now()}.graphql`);
|
|
5255
5543
|
writeFileSync19(queryFile, mutation);
|
|
5256
5544
|
try {
|
|
5257
|
-
|
|
5545
|
+
execSync25(
|
|
5258
5546
|
`gh api graphql -F query=@${queryFile} -f threadId="${threadId}"`,
|
|
5259
5547
|
{ stdio: ["inherit", "pipe", "inherit"] }
|
|
5260
5548
|
);
|
|
5261
5549
|
} finally {
|
|
5262
|
-
|
|
5550
|
+
unlinkSync6(queryFile);
|
|
5263
5551
|
}
|
|
5264
5552
|
}
|
|
5265
5553
|
function requireCache(prNumber) {
|
|
@@ -5306,7 +5594,7 @@ function resolveCommentWithReply(commentId, message) {
|
|
|
5306
5594
|
// src/commands/prs/fixed.ts
|
|
5307
5595
|
function verifySha(sha) {
|
|
5308
5596
|
try {
|
|
5309
|
-
return
|
|
5597
|
+
return execSync26(`git rev-parse --verify ${sha}`, {
|
|
5310
5598
|
encoding: "utf-8"
|
|
5311
5599
|
}).trim();
|
|
5312
5600
|
} catch {
|
|
@@ -5320,7 +5608,7 @@ function fixed(commentId, sha) {
|
|
|
5320
5608
|
const { org, repo } = getRepoInfo();
|
|
5321
5609
|
const repoUrl = `https://github.com/${org}/${repo}`;
|
|
5322
5610
|
const message = `Fixed in [${fullSha}](${repoUrl}/commit/${fullSha})`;
|
|
5323
|
-
|
|
5611
|
+
execSync26("git push", { stdio: "inherit" });
|
|
5324
5612
|
resolveCommentWithReply(commentId, message);
|
|
5325
5613
|
} catch (error) {
|
|
5326
5614
|
if (isGhNotInstalled(error)) {
|
|
@@ -5333,21 +5621,21 @@ function fixed(commentId, sha) {
|
|
|
5333
5621
|
}
|
|
5334
5622
|
|
|
5335
5623
|
// src/commands/prs/listComments/index.ts
|
|
5336
|
-
import { existsSync as
|
|
5337
|
-
import { join as
|
|
5624
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync6, writeFileSync as writeFileSync21 } from "fs";
|
|
5625
|
+
import { join as join23 } from "path";
|
|
5338
5626
|
import { stringify } from "yaml";
|
|
5339
5627
|
|
|
5340
5628
|
// src/commands/prs/fetchThreadIds.ts
|
|
5341
|
-
import { execSync as
|
|
5342
|
-
import { unlinkSync as
|
|
5343
|
-
import { tmpdir as
|
|
5344
|
-
import { join as
|
|
5629
|
+
import { execSync as execSync27 } from "child_process";
|
|
5630
|
+
import { unlinkSync as unlinkSync7, writeFileSync as writeFileSync20 } from "fs";
|
|
5631
|
+
import { tmpdir as tmpdir5 } from "os";
|
|
5632
|
+
import { join as join22 } from "path";
|
|
5345
5633
|
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
5634
|
function fetchThreadIds(org, repo, prNumber) {
|
|
5347
|
-
const queryFile =
|
|
5635
|
+
const queryFile = join22(tmpdir5(), `gh-query-${Date.now()}.graphql`);
|
|
5348
5636
|
writeFileSync20(queryFile, THREAD_QUERY);
|
|
5349
5637
|
try {
|
|
5350
|
-
const result =
|
|
5638
|
+
const result = execSync27(
|
|
5351
5639
|
`gh api graphql -F query=@${queryFile} -F owner="${org}" -F repo="${repo}" -F prNumber=${prNumber}`,
|
|
5352
5640
|
{ encoding: "utf-8" }
|
|
5353
5641
|
);
|
|
@@ -5364,14 +5652,14 @@ function fetchThreadIds(org, repo, prNumber) {
|
|
|
5364
5652
|
}
|
|
5365
5653
|
return { threadMap, resolvedThreadIds };
|
|
5366
5654
|
} finally {
|
|
5367
|
-
|
|
5655
|
+
unlinkSync7(queryFile);
|
|
5368
5656
|
}
|
|
5369
5657
|
}
|
|
5370
5658
|
|
|
5371
5659
|
// src/commands/prs/listComments/fetchReviewComments.ts
|
|
5372
|
-
import { execSync as
|
|
5660
|
+
import { execSync as execSync28 } from "child_process";
|
|
5373
5661
|
function fetchJson(endpoint) {
|
|
5374
|
-
const result =
|
|
5662
|
+
const result = execSync28(`gh api --paginate ${endpoint}`, {
|
|
5375
5663
|
encoding: "utf-8"
|
|
5376
5664
|
});
|
|
5377
5665
|
if (!result.trim()) return [];
|
|
@@ -5413,20 +5701,20 @@ function fetchLineComments(org, repo, prNumber, threadInfo) {
|
|
|
5413
5701
|
}
|
|
5414
5702
|
|
|
5415
5703
|
// src/commands/prs/listComments/printComments.ts
|
|
5416
|
-
import
|
|
5704
|
+
import chalk61 from "chalk";
|
|
5417
5705
|
function formatForHuman(comment2) {
|
|
5418
5706
|
if (comment2.type === "review") {
|
|
5419
|
-
const stateColor = comment2.state === "APPROVED" ?
|
|
5707
|
+
const stateColor = comment2.state === "APPROVED" ? chalk61.green : comment2.state === "CHANGES_REQUESTED" ? chalk61.red : chalk61.yellow;
|
|
5420
5708
|
return [
|
|
5421
|
-
`${
|
|
5709
|
+
`${chalk61.cyan("Review")} by ${chalk61.bold(comment2.user)} ${stateColor(`[${comment2.state}]`)}`,
|
|
5422
5710
|
comment2.body,
|
|
5423
5711
|
""
|
|
5424
5712
|
].join("\n");
|
|
5425
5713
|
}
|
|
5426
5714
|
const location = comment2.line ? `:${comment2.line}` : "";
|
|
5427
5715
|
return [
|
|
5428
|
-
`${
|
|
5429
|
-
|
|
5716
|
+
`${chalk61.cyan("Line comment")} by ${chalk61.bold(comment2.user)} on ${chalk61.dim(`${comment2.path}${location}`)}`,
|
|
5717
|
+
chalk61.dim(comment2.diff_hunk.split("\n").slice(-3).join("\n")),
|
|
5430
5718
|
comment2.body,
|
|
5431
5719
|
""
|
|
5432
5720
|
].join("\n");
|
|
@@ -5458,8 +5746,8 @@ function printComments(result) {
|
|
|
5458
5746
|
|
|
5459
5747
|
// src/commands/prs/listComments/index.ts
|
|
5460
5748
|
function writeCommentsCache(prNumber, comments) {
|
|
5461
|
-
const assistDir =
|
|
5462
|
-
if (!
|
|
5749
|
+
const assistDir = join23(process.cwd(), ".assist");
|
|
5750
|
+
if (!existsSync26(assistDir)) {
|
|
5463
5751
|
mkdirSync6(assistDir, { recursive: true });
|
|
5464
5752
|
}
|
|
5465
5753
|
const cacheData = {
|
|
@@ -5467,7 +5755,7 @@ function writeCommentsCache(prNumber, comments) {
|
|
|
5467
5755
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5468
5756
|
comments
|
|
5469
5757
|
};
|
|
5470
|
-
const cachePath =
|
|
5758
|
+
const cachePath = join23(assistDir, `pr-${prNumber}-comments.yaml`);
|
|
5471
5759
|
writeFileSync21(cachePath, stringify(cacheData));
|
|
5472
5760
|
}
|
|
5473
5761
|
function handleKnownErrors(error) {
|
|
@@ -5500,7 +5788,7 @@ async function listComments() {
|
|
|
5500
5788
|
];
|
|
5501
5789
|
updateCache(prNumber, allComments);
|
|
5502
5790
|
const hasLineComments = allComments.some((c) => c.type === "line");
|
|
5503
|
-
const cachePath = hasLineComments ?
|
|
5791
|
+
const cachePath = hasLineComments ? join23(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`) : null;
|
|
5504
5792
|
return { comments: allComments, cachePath };
|
|
5505
5793
|
} catch (error) {
|
|
5506
5794
|
const handled = handleKnownErrors(error);
|
|
@@ -5510,19 +5798,19 @@ async function listComments() {
|
|
|
5510
5798
|
}
|
|
5511
5799
|
|
|
5512
5800
|
// src/commands/prs/prs/index.ts
|
|
5513
|
-
import { execSync as
|
|
5801
|
+
import { execSync as execSync29 } from "child_process";
|
|
5514
5802
|
|
|
5515
5803
|
// src/commands/prs/prs/displayPaginated/index.ts
|
|
5516
5804
|
import enquirer6 from "enquirer";
|
|
5517
5805
|
|
|
5518
5806
|
// src/commands/prs/prs/displayPaginated/printPr.ts
|
|
5519
|
-
import
|
|
5807
|
+
import chalk62 from "chalk";
|
|
5520
5808
|
var STATUS_MAP = {
|
|
5521
|
-
MERGED: (pr) => pr.mergedAt ? { label:
|
|
5522
|
-
CLOSED: (pr) => pr.closedAt ? { label:
|
|
5809
|
+
MERGED: (pr) => pr.mergedAt ? { label: chalk62.magenta("merged"), date: pr.mergedAt } : null,
|
|
5810
|
+
CLOSED: (pr) => pr.closedAt ? { label: chalk62.red("closed"), date: pr.closedAt } : null
|
|
5523
5811
|
};
|
|
5524
5812
|
function defaultStatus(pr) {
|
|
5525
|
-
return { label:
|
|
5813
|
+
return { label: chalk62.green("opened"), date: pr.createdAt };
|
|
5526
5814
|
}
|
|
5527
5815
|
function getStatus2(pr) {
|
|
5528
5816
|
return STATUS_MAP[pr.state]?.(pr) ?? defaultStatus(pr);
|
|
@@ -5531,11 +5819,11 @@ function formatDate(dateStr) {
|
|
|
5531
5819
|
return new Date(dateStr).toISOString().split("T")[0];
|
|
5532
5820
|
}
|
|
5533
5821
|
function formatPrHeader(pr, status2) {
|
|
5534
|
-
return `${
|
|
5822
|
+
return `${chalk62.cyan(`#${pr.number}`)} ${pr.title} ${chalk62.dim(`(${pr.author.login},`)} ${status2.label} ${chalk62.dim(`${formatDate(status2.date)})`)}`;
|
|
5535
5823
|
}
|
|
5536
5824
|
function logPrDetails(pr) {
|
|
5537
5825
|
console.log(
|
|
5538
|
-
|
|
5826
|
+
chalk62.dim(` ${pr.changedFiles.toLocaleString()} files | ${pr.url}`)
|
|
5539
5827
|
);
|
|
5540
5828
|
console.log();
|
|
5541
5829
|
}
|
|
@@ -5616,7 +5904,7 @@ async function displayPaginated(pullRequests) {
|
|
|
5616
5904
|
async function prs(options2) {
|
|
5617
5905
|
const state = options2.open ? "open" : options2.closed ? "closed" : "all";
|
|
5618
5906
|
try {
|
|
5619
|
-
const result =
|
|
5907
|
+
const result = execSync29(
|
|
5620
5908
|
`gh pr list --state ${state} --json number,title,url,author,createdAt,mergedAt,closedAt,state,changedFiles --limit 100`,
|
|
5621
5909
|
{ encoding: "utf-8" }
|
|
5622
5910
|
);
|
|
@@ -5639,7 +5927,7 @@ async function prs(options2) {
|
|
|
5639
5927
|
}
|
|
5640
5928
|
|
|
5641
5929
|
// src/commands/prs/wontfix.ts
|
|
5642
|
-
import { execSync as
|
|
5930
|
+
import { execSync as execSync30 } from "child_process";
|
|
5643
5931
|
function validateReason(reason) {
|
|
5644
5932
|
const lowerReason = reason.toLowerCase();
|
|
5645
5933
|
if (lowerReason.includes("claude") || lowerReason.includes("opus")) {
|
|
@@ -5656,7 +5944,7 @@ function validateShaReferences(reason) {
|
|
|
5656
5944
|
const invalidShas = [];
|
|
5657
5945
|
for (const sha of shas) {
|
|
5658
5946
|
try {
|
|
5659
|
-
|
|
5947
|
+
execSync30(`git cat-file -t ${sha}`, { stdio: "pipe" });
|
|
5660
5948
|
} catch {
|
|
5661
5949
|
invalidShas.push(sha);
|
|
5662
5950
|
}
|
|
@@ -5695,13 +5983,13 @@ function registerPrs(program2) {
|
|
|
5695
5983
|
prsCommand.command("wontfix <comment-id> <reason>").description("Reply with reason and resolve thread").action((commentId, reason) => {
|
|
5696
5984
|
wontfix(Number.parseInt(commentId, 10), reason);
|
|
5697
5985
|
});
|
|
5698
|
-
prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((
|
|
5699
|
-
comment(
|
|
5986
|
+
prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((path44, line, body) => {
|
|
5987
|
+
comment(path44, Number.parseInt(line, 10), body);
|
|
5700
5988
|
});
|
|
5701
5989
|
}
|
|
5702
5990
|
|
|
5703
5991
|
// src/commands/ravendb/ravendbAuth.ts
|
|
5704
|
-
import
|
|
5992
|
+
import chalk67 from "chalk";
|
|
5705
5993
|
|
|
5706
5994
|
// src/commands/ravendb/loadConnections.ts
|
|
5707
5995
|
function loadConnections() {
|
|
@@ -5718,18 +6006,18 @@ function saveConnections(connections) {
|
|
|
5718
6006
|
}
|
|
5719
6007
|
|
|
5720
6008
|
// src/commands/ravendb/promptConnection.ts
|
|
5721
|
-
import
|
|
6009
|
+
import chalk65 from "chalk";
|
|
5722
6010
|
import Enquirer3 from "enquirer";
|
|
5723
6011
|
|
|
5724
6012
|
// src/commands/ravendb/selectOpSecret.ts
|
|
5725
|
-
import
|
|
6013
|
+
import chalk64 from "chalk";
|
|
5726
6014
|
import Enquirer2 from "enquirer";
|
|
5727
6015
|
|
|
5728
6016
|
// src/commands/ravendb/searchItems.ts
|
|
5729
|
-
import { execSync as
|
|
5730
|
-
import
|
|
6017
|
+
import { execSync as execSync31 } from "child_process";
|
|
6018
|
+
import chalk63 from "chalk";
|
|
5731
6019
|
function opExec(args) {
|
|
5732
|
-
return
|
|
6020
|
+
return execSync31(`op ${args}`, {
|
|
5733
6021
|
encoding: "utf-8",
|
|
5734
6022
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5735
6023
|
}).trim();
|
|
@@ -5740,7 +6028,7 @@ function searchItems(search) {
|
|
|
5740
6028
|
items = JSON.parse(opExec("item list --format=json"));
|
|
5741
6029
|
} catch {
|
|
5742
6030
|
console.error(
|
|
5743
|
-
|
|
6031
|
+
chalk63.red(
|
|
5744
6032
|
"Failed to search 1Password. Ensure the CLI is installed and you are signed in."
|
|
5745
6033
|
)
|
|
5746
6034
|
);
|
|
@@ -5754,7 +6042,7 @@ function getItemFields(itemId) {
|
|
|
5754
6042
|
const item = JSON.parse(opExec(`item get "${itemId}" --format=json`));
|
|
5755
6043
|
return item.fields.filter((f) => f.reference && f.label);
|
|
5756
6044
|
} catch {
|
|
5757
|
-
console.error(
|
|
6045
|
+
console.error(chalk63.red("Failed to get item details from 1Password."));
|
|
5758
6046
|
process.exit(1);
|
|
5759
6047
|
}
|
|
5760
6048
|
}
|
|
@@ -5773,7 +6061,7 @@ async function selectOpSecret(searchTerm) {
|
|
|
5773
6061
|
}).run();
|
|
5774
6062
|
const items = searchItems(search);
|
|
5775
6063
|
if (items.length === 0) {
|
|
5776
|
-
console.error(
|
|
6064
|
+
console.error(chalk64.red(`No items found matching "${search}".`));
|
|
5777
6065
|
process.exit(1);
|
|
5778
6066
|
}
|
|
5779
6067
|
const itemId = await selectOne(
|
|
@@ -5782,7 +6070,7 @@ async function selectOpSecret(searchTerm) {
|
|
|
5782
6070
|
);
|
|
5783
6071
|
const fields = getItemFields(itemId);
|
|
5784
6072
|
if (fields.length === 0) {
|
|
5785
|
-
console.error(
|
|
6073
|
+
console.error(chalk64.red("No fields with references found on this item."));
|
|
5786
6074
|
process.exit(1);
|
|
5787
6075
|
}
|
|
5788
6076
|
const ref = await selectOne(
|
|
@@ -5800,7 +6088,7 @@ async function promptConnection(existingNames) {
|
|
|
5800
6088
|
message: "Connection name:"
|
|
5801
6089
|
}).run();
|
|
5802
6090
|
if (existingNames.includes(name)) {
|
|
5803
|
-
console.error(
|
|
6091
|
+
console.error(chalk65.red(`Connection "${name}" already exists.`));
|
|
5804
6092
|
process.exit(1);
|
|
5805
6093
|
}
|
|
5806
6094
|
const url = await new Input2({
|
|
@@ -5812,22 +6100,22 @@ async function promptConnection(existingNames) {
|
|
|
5812
6100
|
message: "Database name:"
|
|
5813
6101
|
}).run();
|
|
5814
6102
|
if (!name || !url || !database) {
|
|
5815
|
-
console.error(
|
|
6103
|
+
console.error(chalk65.red("All fields are required."));
|
|
5816
6104
|
process.exit(1);
|
|
5817
6105
|
}
|
|
5818
6106
|
const apiKeyRef = await selectOpSecret();
|
|
5819
|
-
console.log(
|
|
6107
|
+
console.log(chalk65.dim(`Using: ${apiKeyRef}`));
|
|
5820
6108
|
return { name, url, database, apiKeyRef };
|
|
5821
6109
|
}
|
|
5822
6110
|
|
|
5823
6111
|
// src/commands/ravendb/ravendbSetConnection.ts
|
|
5824
|
-
import
|
|
6112
|
+
import chalk66 from "chalk";
|
|
5825
6113
|
function ravendbSetConnection(name) {
|
|
5826
6114
|
const raw = loadGlobalConfigRaw();
|
|
5827
6115
|
const ravendb = raw.ravendb ?? {};
|
|
5828
6116
|
const connections = ravendb.connections ?? [];
|
|
5829
6117
|
if (!connections.some((c) => c.name === name)) {
|
|
5830
|
-
console.error(
|
|
6118
|
+
console.error(chalk66.red(`Connection "${name}" not found.`));
|
|
5831
6119
|
console.error(
|
|
5832
6120
|
`Available: ${connections.map((c) => c.name).join(", ") || "(none)"}`
|
|
5833
6121
|
);
|
|
@@ -5849,7 +6137,7 @@ async function ravendbAuth(options2) {
|
|
|
5849
6137
|
}
|
|
5850
6138
|
for (const c of connections) {
|
|
5851
6139
|
console.log(
|
|
5852
|
-
`${
|
|
6140
|
+
`${chalk67.bold(c.name)} ${c.url} db=${c.database} key=${c.apiKeyRef}`
|
|
5853
6141
|
);
|
|
5854
6142
|
}
|
|
5855
6143
|
return;
|
|
@@ -5857,7 +6145,7 @@ async function ravendbAuth(options2) {
|
|
|
5857
6145
|
if (options2.remove) {
|
|
5858
6146
|
const filtered = connections.filter((c) => c.name !== options2.remove);
|
|
5859
6147
|
if (filtered.length === connections.length) {
|
|
5860
|
-
console.error(
|
|
6148
|
+
console.error(chalk67.red(`Connection "${options2.remove}" not found.`));
|
|
5861
6149
|
process.exit(1);
|
|
5862
6150
|
}
|
|
5863
6151
|
saveConnections(filtered);
|
|
@@ -5875,10 +6163,10 @@ async function ravendbAuth(options2) {
|
|
|
5875
6163
|
}
|
|
5876
6164
|
|
|
5877
6165
|
// src/commands/ravendb/ravendbCollections.ts
|
|
5878
|
-
import
|
|
6166
|
+
import chalk71 from "chalk";
|
|
5879
6167
|
|
|
5880
6168
|
// src/commands/ravendb/ravenFetch.ts
|
|
5881
|
-
import
|
|
6169
|
+
import chalk69 from "chalk";
|
|
5882
6170
|
|
|
5883
6171
|
// src/commands/ravendb/getAccessToken.ts
|
|
5884
6172
|
var OAUTH_URL = "https://amazon-useast-1-oauth.ravenhq.com/ApiKeys/OAuth/AccessToken";
|
|
@@ -5914,21 +6202,21 @@ ${errorText}`
|
|
|
5914
6202
|
}
|
|
5915
6203
|
|
|
5916
6204
|
// src/commands/ravendb/resolveOpSecret.ts
|
|
5917
|
-
import { execSync as
|
|
5918
|
-
import
|
|
6205
|
+
import { execSync as execSync32 } from "child_process";
|
|
6206
|
+
import chalk68 from "chalk";
|
|
5919
6207
|
function resolveOpSecret(reference) {
|
|
5920
6208
|
if (!reference.startsWith("op://")) {
|
|
5921
|
-
console.error(
|
|
6209
|
+
console.error(chalk68.red(`Invalid secret reference: must start with op://`));
|
|
5922
6210
|
process.exit(1);
|
|
5923
6211
|
}
|
|
5924
6212
|
try {
|
|
5925
|
-
return
|
|
6213
|
+
return execSync32(`op read "${reference}"`, {
|
|
5926
6214
|
encoding: "utf-8",
|
|
5927
6215
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5928
6216
|
}).trim();
|
|
5929
6217
|
} catch {
|
|
5930
6218
|
console.error(
|
|
5931
|
-
|
|
6219
|
+
chalk68.red(
|
|
5932
6220
|
"Failed to resolve secret reference. Ensure 1Password CLI is installed and you are signed in."
|
|
5933
6221
|
)
|
|
5934
6222
|
);
|
|
@@ -5937,10 +6225,10 @@ function resolveOpSecret(reference) {
|
|
|
5937
6225
|
}
|
|
5938
6226
|
|
|
5939
6227
|
// src/commands/ravendb/ravenFetch.ts
|
|
5940
|
-
async function ravenFetch(connection,
|
|
6228
|
+
async function ravenFetch(connection, path44) {
|
|
5941
6229
|
const apiKey = resolveOpSecret(connection.apiKeyRef);
|
|
5942
6230
|
let accessToken = await getAccessToken(apiKey);
|
|
5943
|
-
const url = `${connection.url}${
|
|
6231
|
+
const url = `${connection.url}${path44}`;
|
|
5944
6232
|
const headers = {
|
|
5945
6233
|
Authorization: `Bearer ${accessToken}`,
|
|
5946
6234
|
"Content-Type": "application/json"
|
|
@@ -5955,7 +6243,7 @@ async function ravenFetch(connection, path42) {
|
|
|
5955
6243
|
if (!response.ok) {
|
|
5956
6244
|
const body = await response.text();
|
|
5957
6245
|
console.error(
|
|
5958
|
-
|
|
6246
|
+
chalk69.red(`RavenDB error: ${response.status} ${response.statusText}`)
|
|
5959
6247
|
);
|
|
5960
6248
|
console.error(body.substring(0, 500));
|
|
5961
6249
|
process.exit(1);
|
|
@@ -5964,7 +6252,7 @@ async function ravenFetch(connection, path42) {
|
|
|
5964
6252
|
}
|
|
5965
6253
|
|
|
5966
6254
|
// src/commands/ravendb/resolveConnection.ts
|
|
5967
|
-
import
|
|
6255
|
+
import chalk70 from "chalk";
|
|
5968
6256
|
function loadRavendb() {
|
|
5969
6257
|
const raw = loadGlobalConfigRaw();
|
|
5970
6258
|
const ravendb = raw.ravendb;
|
|
@@ -5978,7 +6266,7 @@ function resolveConnection(name) {
|
|
|
5978
6266
|
const connectionName = name ?? defaultConnection;
|
|
5979
6267
|
if (!connectionName) {
|
|
5980
6268
|
console.error(
|
|
5981
|
-
|
|
6269
|
+
chalk70.red(
|
|
5982
6270
|
"No connection specified and no default set. Use assist ravendb set-connection <name> or pass a connection name."
|
|
5983
6271
|
)
|
|
5984
6272
|
);
|
|
@@ -5986,7 +6274,7 @@ function resolveConnection(name) {
|
|
|
5986
6274
|
}
|
|
5987
6275
|
const connection = connections.find((c) => c.name === connectionName);
|
|
5988
6276
|
if (!connection) {
|
|
5989
|
-
console.error(
|
|
6277
|
+
console.error(chalk70.red(`Connection "${connectionName}" not found.`));
|
|
5990
6278
|
console.error(
|
|
5991
6279
|
`Available: ${connections.map((c) => c.name).join(", ") || "(none)"}`
|
|
5992
6280
|
);
|
|
@@ -6017,29 +6305,29 @@ async function ravendbCollections(connectionName) {
|
|
|
6017
6305
|
return;
|
|
6018
6306
|
}
|
|
6019
6307
|
for (const c of collections) {
|
|
6020
|
-
console.log(`${
|
|
6308
|
+
console.log(`${chalk71.bold(c.Name)} ${c.CountOfDocuments} docs`);
|
|
6021
6309
|
}
|
|
6022
6310
|
}
|
|
6023
6311
|
|
|
6024
6312
|
// src/commands/ravendb/ravendbQuery.ts
|
|
6025
|
-
import
|
|
6313
|
+
import chalk73 from "chalk";
|
|
6026
6314
|
|
|
6027
6315
|
// src/commands/ravendb/fetchAllPages.ts
|
|
6028
|
-
import
|
|
6316
|
+
import chalk72 from "chalk";
|
|
6029
6317
|
|
|
6030
6318
|
// src/commands/ravendb/buildQueryPath.ts
|
|
6031
6319
|
function buildQueryPath(opts) {
|
|
6032
6320
|
const db = encodeURIComponent(opts.db);
|
|
6033
|
-
let
|
|
6321
|
+
let path44;
|
|
6034
6322
|
if (opts.collection) {
|
|
6035
|
-
|
|
6323
|
+
path44 = `/databases/${db}/indexes/dynamic/${encodeURIComponent(opts.collection)}?start=${opts.start}&pageSize=${opts.pageSize}&sort=${encodeURIComponent(opts.sort)}`;
|
|
6036
6324
|
} else {
|
|
6037
|
-
|
|
6325
|
+
path44 = `/databases/${db}/queries?start=${opts.start}&pageSize=${opts.pageSize}`;
|
|
6038
6326
|
}
|
|
6039
6327
|
if (opts.query) {
|
|
6040
|
-
|
|
6328
|
+
path44 += `&query=${encodeURIComponent(opts.query)}`;
|
|
6041
6329
|
}
|
|
6042
|
-
return
|
|
6330
|
+
return path44;
|
|
6043
6331
|
}
|
|
6044
6332
|
|
|
6045
6333
|
// src/commands/ravendb/fetchAllPages.ts
|
|
@@ -6048,7 +6336,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
6048
6336
|
let start3 = 0;
|
|
6049
6337
|
while (true) {
|
|
6050
6338
|
const effectivePageSize = opts.limit !== void 0 ? Math.min(opts.pageSize, opts.limit - allResults.length) : opts.pageSize;
|
|
6051
|
-
const
|
|
6339
|
+
const path44 = buildQueryPath({
|
|
6052
6340
|
db: connection.database,
|
|
6053
6341
|
collection: opts.collection,
|
|
6054
6342
|
start: start3,
|
|
@@ -6056,14 +6344,14 @@ async function fetchAllPages(connection, opts) {
|
|
|
6056
6344
|
sort: opts.sort,
|
|
6057
6345
|
query: opts.query
|
|
6058
6346
|
});
|
|
6059
|
-
const data = await ravenFetch(connection,
|
|
6347
|
+
const data = await ravenFetch(connection, path44);
|
|
6060
6348
|
const results = data.Results ?? [];
|
|
6061
6349
|
const totalResults = data.TotalResults ?? 0;
|
|
6062
6350
|
if (results.length === 0) break;
|
|
6063
6351
|
allResults.push(...results);
|
|
6064
6352
|
start3 += results.length;
|
|
6065
6353
|
process.stderr.write(
|
|
6066
|
-
`\r${
|
|
6354
|
+
`\r${chalk72.dim(`Fetched ${allResults.length}/${totalResults}`)}`
|
|
6067
6355
|
);
|
|
6068
6356
|
if (start3 >= totalResults) break;
|
|
6069
6357
|
if (opts.limit !== void 0 && allResults.length >= opts.limit) break;
|
|
@@ -6078,7 +6366,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
6078
6366
|
async function ravendbQuery(connectionName, collection, options2) {
|
|
6079
6367
|
const resolved = resolveArgs(connectionName, collection);
|
|
6080
6368
|
if (!resolved.collection && !options2.query) {
|
|
6081
|
-
console.error(
|
|
6369
|
+
console.error(chalk73.red("Provide a collection name or --query filter."));
|
|
6082
6370
|
process.exit(1);
|
|
6083
6371
|
}
|
|
6084
6372
|
const { collection: col } = resolved;
|
|
@@ -6110,10 +6398,10 @@ function registerRavendb(program2) {
|
|
|
6110
6398
|
|
|
6111
6399
|
// src/commands/refactor/check/index.ts
|
|
6112
6400
|
import { spawn as spawn3 } from "child_process";
|
|
6113
|
-
import * as
|
|
6401
|
+
import * as path27 from "path";
|
|
6114
6402
|
|
|
6115
6403
|
// src/commands/refactor/logViolations.ts
|
|
6116
|
-
import
|
|
6404
|
+
import chalk74 from "chalk";
|
|
6117
6405
|
var DEFAULT_MAX_LINES = 100;
|
|
6118
6406
|
function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
|
|
6119
6407
|
if (violations.length === 0) {
|
|
@@ -6122,43 +6410,43 @@ function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
|
|
|
6122
6410
|
}
|
|
6123
6411
|
return;
|
|
6124
6412
|
}
|
|
6125
|
-
console.error(
|
|
6413
|
+
console.error(chalk74.red(`
|
|
6126
6414
|
Refactor check failed:
|
|
6127
6415
|
`));
|
|
6128
|
-
console.error(
|
|
6416
|
+
console.error(chalk74.red(` The following files exceed ${maxLines} lines:
|
|
6129
6417
|
`));
|
|
6130
6418
|
for (const violation of violations) {
|
|
6131
|
-
console.error(
|
|
6419
|
+
console.error(chalk74.red(` ${violation.file} (${violation.lines} lines)`));
|
|
6132
6420
|
}
|
|
6133
6421
|
console.error(
|
|
6134
|
-
|
|
6422
|
+
chalk74.yellow(
|
|
6135
6423
|
`
|
|
6136
6424
|
Each file needs to be sensibly refactored, or if there is no sensible
|
|
6137
6425
|
way to refactor it, ignore it with:
|
|
6138
6426
|
`
|
|
6139
6427
|
)
|
|
6140
6428
|
);
|
|
6141
|
-
console.error(
|
|
6429
|
+
console.error(chalk74.gray(` assist refactor ignore <file>
|
|
6142
6430
|
`));
|
|
6143
6431
|
if (process.env.CLAUDECODE) {
|
|
6144
|
-
console.error(
|
|
6432
|
+
console.error(chalk74.cyan(`
|
|
6145
6433
|
## Extracting Code to New Files
|
|
6146
6434
|
`));
|
|
6147
6435
|
console.error(
|
|
6148
|
-
|
|
6436
|
+
chalk74.cyan(
|
|
6149
6437
|
` When extracting logic from one file to another, consider where the extracted code belongs:
|
|
6150
6438
|
`
|
|
6151
6439
|
)
|
|
6152
6440
|
);
|
|
6153
6441
|
console.error(
|
|
6154
|
-
|
|
6442
|
+
chalk74.cyan(
|
|
6155
6443
|
` 1. Keep related logic together: If the extracted code is tightly coupled to the
|
|
6156
6444
|
original file's domain, create a new folder containing both the original and extracted files.
|
|
6157
6445
|
`
|
|
6158
6446
|
)
|
|
6159
6447
|
);
|
|
6160
6448
|
console.error(
|
|
6161
|
-
|
|
6449
|
+
chalk74.cyan(
|
|
6162
6450
|
` 2. Share common utilities: If the extracted code can be reused across multiple
|
|
6163
6451
|
domains, move it to a common/shared folder.
|
|
6164
6452
|
`
|
|
@@ -6168,7 +6456,7 @@ Refactor check failed:
|
|
|
6168
6456
|
}
|
|
6169
6457
|
|
|
6170
6458
|
// src/commands/refactor/check/getViolations/index.ts
|
|
6171
|
-
import { execSync as
|
|
6459
|
+
import { execSync as execSync33 } from "child_process";
|
|
6172
6460
|
import fs16 from "fs";
|
|
6173
6461
|
import { minimatch as minimatch4 } from "minimatch";
|
|
6174
6462
|
|
|
@@ -6218,7 +6506,7 @@ function getGitFiles(options2) {
|
|
|
6218
6506
|
}
|
|
6219
6507
|
const files = /* @__PURE__ */ new Set();
|
|
6220
6508
|
if (options2.staged || options2.modified) {
|
|
6221
|
-
const staged =
|
|
6509
|
+
const staged = execSync33("git diff --cached --name-only", {
|
|
6222
6510
|
encoding: "utf-8"
|
|
6223
6511
|
});
|
|
6224
6512
|
for (const file of staged.trim().split("\n").filter(Boolean)) {
|
|
@@ -6226,7 +6514,7 @@ function getGitFiles(options2) {
|
|
|
6226
6514
|
}
|
|
6227
6515
|
}
|
|
6228
6516
|
if (options2.unstaged || options2.modified) {
|
|
6229
|
-
const unstaged =
|
|
6517
|
+
const unstaged = execSync33("git diff --name-only", { encoding: "utf-8" });
|
|
6230
6518
|
for (const file of unstaged.trim().split("\n").filter(Boolean)) {
|
|
6231
6519
|
files.add(file);
|
|
6232
6520
|
}
|
|
@@ -6287,7 +6575,7 @@ ${failed.length} verify script(s) failed:`);
|
|
|
6287
6575
|
async function runVerifyQuietly() {
|
|
6288
6576
|
const result = findPackageJsonWithVerifyScripts(process.cwd());
|
|
6289
6577
|
if (!result) return true;
|
|
6290
|
-
const packageDir =
|
|
6578
|
+
const packageDir = path27.dirname(result.packageJsonPath);
|
|
6291
6579
|
const results = await Promise.all(
|
|
6292
6580
|
result.verifyScripts.map((script) => runScript(script, packageDir))
|
|
6293
6581
|
);
|
|
@@ -6314,11 +6602,11 @@ async function check(pattern2, options2) {
|
|
|
6314
6602
|
|
|
6315
6603
|
// src/commands/refactor/ignore.ts
|
|
6316
6604
|
import fs17 from "fs";
|
|
6317
|
-
import
|
|
6605
|
+
import chalk75 from "chalk";
|
|
6318
6606
|
var REFACTOR_YML_PATH2 = "refactor.yml";
|
|
6319
6607
|
function ignore(file) {
|
|
6320
6608
|
if (!fs17.existsSync(file)) {
|
|
6321
|
-
console.error(
|
|
6609
|
+
console.error(chalk75.red(`Error: File does not exist: ${file}`));
|
|
6322
6610
|
process.exit(1);
|
|
6323
6611
|
}
|
|
6324
6612
|
const content = fs17.readFileSync(file, "utf-8");
|
|
@@ -6334,43 +6622,43 @@ function ignore(file) {
|
|
|
6334
6622
|
fs17.writeFileSync(REFACTOR_YML_PATH2, entry);
|
|
6335
6623
|
}
|
|
6336
6624
|
console.log(
|
|
6337
|
-
|
|
6625
|
+
chalk75.green(
|
|
6338
6626
|
`Added ${file} to refactor ignore list (max ${maxLines} lines)`
|
|
6339
6627
|
)
|
|
6340
6628
|
);
|
|
6341
6629
|
}
|
|
6342
6630
|
|
|
6343
6631
|
// src/commands/refactor/rename/index.ts
|
|
6344
|
-
import
|
|
6345
|
-
import
|
|
6632
|
+
import path28 from "path";
|
|
6633
|
+
import chalk76 from "chalk";
|
|
6346
6634
|
import { Project as Project2 } from "ts-morph";
|
|
6347
6635
|
async function rename(source, destination, options2 = {}) {
|
|
6348
|
-
const sourcePath =
|
|
6349
|
-
const destPath =
|
|
6636
|
+
const sourcePath = path28.resolve(source);
|
|
6637
|
+
const destPath = path28.resolve(destination);
|
|
6350
6638
|
const cwd = process.cwd();
|
|
6351
|
-
const relSource =
|
|
6352
|
-
const relDest =
|
|
6639
|
+
const relSource = path28.relative(cwd, sourcePath);
|
|
6640
|
+
const relDest = path28.relative(cwd, destPath);
|
|
6353
6641
|
const project = new Project2({
|
|
6354
|
-
tsConfigFilePath:
|
|
6642
|
+
tsConfigFilePath: path28.resolve("tsconfig.json")
|
|
6355
6643
|
});
|
|
6356
6644
|
const sourceFile = project.getSourceFile(sourcePath);
|
|
6357
6645
|
if (!sourceFile) {
|
|
6358
|
-
console.log(
|
|
6646
|
+
console.log(chalk76.red(`File not found in project: ${source}`));
|
|
6359
6647
|
process.exit(1);
|
|
6360
6648
|
}
|
|
6361
|
-
console.log(
|
|
6649
|
+
console.log(chalk76.bold(`Rename: ${relSource} \u2192 ${relDest}`));
|
|
6362
6650
|
if (options2.apply) {
|
|
6363
6651
|
sourceFile.move(destPath);
|
|
6364
6652
|
await project.save();
|
|
6365
|
-
console.log(
|
|
6653
|
+
console.log(chalk76.green("Done"));
|
|
6366
6654
|
} else {
|
|
6367
|
-
console.log(
|
|
6655
|
+
console.log(chalk76.dim("Dry run. Use --apply to execute."));
|
|
6368
6656
|
}
|
|
6369
6657
|
}
|
|
6370
6658
|
|
|
6371
6659
|
// src/commands/refactor/renameSymbol/index.ts
|
|
6372
|
-
import
|
|
6373
|
-
import
|
|
6660
|
+
import path30 from "path";
|
|
6661
|
+
import chalk77 from "chalk";
|
|
6374
6662
|
import { Project as Project3 } from "ts-morph";
|
|
6375
6663
|
|
|
6376
6664
|
// src/commands/refactor/renameSymbol/findSymbol.ts
|
|
@@ -6398,12 +6686,12 @@ function findSymbol(sourceFile, symbolName) {
|
|
|
6398
6686
|
}
|
|
6399
6687
|
|
|
6400
6688
|
// src/commands/refactor/renameSymbol/groupReferences.ts
|
|
6401
|
-
import
|
|
6689
|
+
import path29 from "path";
|
|
6402
6690
|
function groupReferences(symbol, cwd) {
|
|
6403
6691
|
const refs = symbol.findReferencesAsNodes();
|
|
6404
6692
|
const grouped = /* @__PURE__ */ new Map();
|
|
6405
6693
|
for (const ref of refs) {
|
|
6406
|
-
const refFile =
|
|
6694
|
+
const refFile = path29.relative(cwd, ref.getSourceFile().getFilePath());
|
|
6407
6695
|
const lines = grouped.get(refFile) ?? [];
|
|
6408
6696
|
if (!grouped.has(refFile)) grouped.set(refFile, lines);
|
|
6409
6697
|
lines.push(ref.getStartLineNumber());
|
|
@@ -6413,47 +6701,47 @@ function groupReferences(symbol, cwd) {
|
|
|
6413
6701
|
|
|
6414
6702
|
// src/commands/refactor/renameSymbol/index.ts
|
|
6415
6703
|
async function renameSymbol(file, oldName, newName, options2 = {}) {
|
|
6416
|
-
const filePath =
|
|
6417
|
-
const tsConfigPath =
|
|
6704
|
+
const filePath = path30.resolve(file);
|
|
6705
|
+
const tsConfigPath = path30.resolve("tsconfig.json");
|
|
6418
6706
|
const cwd = process.cwd();
|
|
6419
6707
|
const project = new Project3({ tsConfigFilePath: tsConfigPath });
|
|
6420
6708
|
const sourceFile = project.getSourceFile(filePath);
|
|
6421
6709
|
if (!sourceFile) {
|
|
6422
|
-
console.log(
|
|
6710
|
+
console.log(chalk77.red(`File not found in project: ${file}`));
|
|
6423
6711
|
process.exit(1);
|
|
6424
6712
|
}
|
|
6425
6713
|
const symbol = findSymbol(sourceFile, oldName);
|
|
6426
6714
|
if (!symbol) {
|
|
6427
|
-
console.log(
|
|
6715
|
+
console.log(chalk77.red(`Symbol "${oldName}" not found in ${file}`));
|
|
6428
6716
|
process.exit(1);
|
|
6429
6717
|
}
|
|
6430
6718
|
const grouped = groupReferences(symbol, cwd);
|
|
6431
6719
|
const totalRefs = [...grouped.values()].reduce((s, l) => s + l.length, 0);
|
|
6432
6720
|
console.log(
|
|
6433
|
-
|
|
6721
|
+
chalk77.bold(`Rename: ${oldName} \u2192 ${newName} (${totalRefs} references)
|
|
6434
6722
|
`)
|
|
6435
6723
|
);
|
|
6436
6724
|
for (const [refFile, lines] of grouped) {
|
|
6437
6725
|
console.log(
|
|
6438
|
-
` ${
|
|
6726
|
+
` ${chalk77.dim(refFile)}: lines ${chalk77.cyan(lines.join(", "))}`
|
|
6439
6727
|
);
|
|
6440
6728
|
}
|
|
6441
6729
|
if (options2.apply) {
|
|
6442
6730
|
symbol.rename(newName);
|
|
6443
6731
|
await project.save();
|
|
6444
|
-
console.log(
|
|
6732
|
+
console.log(chalk77.green(`
|
|
6445
6733
|
Renamed ${oldName} \u2192 ${newName}`));
|
|
6446
6734
|
} else {
|
|
6447
|
-
console.log(
|
|
6735
|
+
console.log(chalk77.dim("\nDry run. Use --apply to execute."));
|
|
6448
6736
|
}
|
|
6449
6737
|
}
|
|
6450
6738
|
|
|
6451
6739
|
// src/commands/refactor/restructure/index.ts
|
|
6452
|
-
import
|
|
6453
|
-
import
|
|
6740
|
+
import path39 from "path";
|
|
6741
|
+
import chalk80 from "chalk";
|
|
6454
6742
|
|
|
6455
6743
|
// src/commands/refactor/restructure/buildImportGraph/index.ts
|
|
6456
|
-
import
|
|
6744
|
+
import path31 from "path";
|
|
6457
6745
|
import ts7 from "typescript";
|
|
6458
6746
|
|
|
6459
6747
|
// src/commands/refactor/restructure/buildImportGraph/getImportSpecifiers.ts
|
|
@@ -6480,7 +6768,7 @@ function loadParsedConfig(tsConfigPath) {
|
|
|
6480
6768
|
return ts7.parseJsonConfigFileContent(
|
|
6481
6769
|
configFile.config,
|
|
6482
6770
|
ts7.sys,
|
|
6483
|
-
|
|
6771
|
+
path31.dirname(tsConfigPath)
|
|
6484
6772
|
);
|
|
6485
6773
|
}
|
|
6486
6774
|
function addToSetMap(map, key, value) {
|
|
@@ -6496,7 +6784,7 @@ function resolveImport(specifier, filePath, options2) {
|
|
|
6496
6784
|
const resolved = ts7.resolveModuleName(specifier, filePath, options2, ts7.sys);
|
|
6497
6785
|
const resolvedPath = resolved.resolvedModule?.resolvedFileName;
|
|
6498
6786
|
if (!resolvedPath || resolvedPath.includes("node_modules")) return null;
|
|
6499
|
-
return
|
|
6787
|
+
return path31.resolve(resolvedPath);
|
|
6500
6788
|
}
|
|
6501
6789
|
function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
6502
6790
|
const parsed = loadParsedConfig(tsConfigPath);
|
|
@@ -6505,7 +6793,7 @@ function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
|
6505
6793
|
const importedBy = /* @__PURE__ */ new Map();
|
|
6506
6794
|
const imports = /* @__PURE__ */ new Map();
|
|
6507
6795
|
for (const sourceFile of program2.getSourceFiles()) {
|
|
6508
|
-
const filePath =
|
|
6796
|
+
const filePath = path31.resolve(sourceFile.fileName);
|
|
6509
6797
|
if (filePath.includes("node_modules")) continue;
|
|
6510
6798
|
for (const specifier of getImportSpecifiers(sourceFile)) {
|
|
6511
6799
|
const absTarget = resolveImport(specifier, filePath, parsed.options);
|
|
@@ -6519,12 +6807,12 @@ function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
|
6519
6807
|
}
|
|
6520
6808
|
|
|
6521
6809
|
// src/commands/refactor/restructure/clusterDirectories.ts
|
|
6522
|
-
import
|
|
6810
|
+
import path32 from "path";
|
|
6523
6811
|
function clusterDirectories(graph) {
|
|
6524
6812
|
const dirImportedBy = /* @__PURE__ */ new Map();
|
|
6525
6813
|
for (const edge of graph.edges) {
|
|
6526
|
-
const sourceDir =
|
|
6527
|
-
const targetDir =
|
|
6814
|
+
const sourceDir = path32.dirname(edge.source);
|
|
6815
|
+
const targetDir = path32.dirname(edge.target);
|
|
6528
6816
|
if (sourceDir === targetDir) continue;
|
|
6529
6817
|
if (!graph.files.has(edge.target)) continue;
|
|
6530
6818
|
const existing = dirImportedBy.get(targetDir) ?? /* @__PURE__ */ new Set();
|
|
@@ -6552,20 +6840,20 @@ function clusterDirectories(graph) {
|
|
|
6552
6840
|
return clusters;
|
|
6553
6841
|
}
|
|
6554
6842
|
function isAncestor(ancestor, descendant) {
|
|
6555
|
-
const rel =
|
|
6843
|
+
const rel = path32.relative(ancestor, descendant);
|
|
6556
6844
|
return !rel.startsWith("..") && rel !== "";
|
|
6557
6845
|
}
|
|
6558
6846
|
|
|
6559
6847
|
// src/commands/refactor/restructure/clusterFiles.ts
|
|
6560
|
-
import
|
|
6848
|
+
import path33 from "path";
|
|
6561
6849
|
function findRootParent(file, importedBy, visited) {
|
|
6562
6850
|
const importers = importedBy.get(file);
|
|
6563
6851
|
if (!importers || importers.size !== 1) return file;
|
|
6564
6852
|
const parent = [...importers][0];
|
|
6565
|
-
const parentDir =
|
|
6566
|
-
const fileDir =
|
|
6853
|
+
const parentDir = path33.dirname(parent);
|
|
6854
|
+
const fileDir = path33.dirname(file);
|
|
6567
6855
|
if (parentDir !== fileDir) return file;
|
|
6568
|
-
if (
|
|
6856
|
+
if (path33.basename(parent, path33.extname(parent)) === "index") return file;
|
|
6569
6857
|
if (visited.has(parent)) return file;
|
|
6570
6858
|
visited.add(parent);
|
|
6571
6859
|
return findRootParent(parent, importedBy, visited);
|
|
@@ -6573,16 +6861,16 @@ function findRootParent(file, importedBy, visited) {
|
|
|
6573
6861
|
function clusterFiles(graph) {
|
|
6574
6862
|
const clusters = /* @__PURE__ */ new Map();
|
|
6575
6863
|
for (const file of graph.files) {
|
|
6576
|
-
const basename7 =
|
|
6864
|
+
const basename7 = path33.basename(file, path33.extname(file));
|
|
6577
6865
|
if (basename7 === "index") continue;
|
|
6578
6866
|
const importers = graph.importedBy.get(file);
|
|
6579
6867
|
if (!importers || importers.size !== 1) continue;
|
|
6580
6868
|
const parent = [...importers][0];
|
|
6581
6869
|
if (!graph.files.has(parent)) continue;
|
|
6582
|
-
const parentDir =
|
|
6583
|
-
const fileDir =
|
|
6870
|
+
const parentDir = path33.dirname(parent);
|
|
6871
|
+
const fileDir = path33.dirname(file);
|
|
6584
6872
|
if (parentDir !== fileDir) continue;
|
|
6585
|
-
const parentBasename =
|
|
6873
|
+
const parentBasename = path33.basename(parent, path33.extname(parent));
|
|
6586
6874
|
if (parentBasename === "index") continue;
|
|
6587
6875
|
const root = findRootParent(parent, graph.importedBy, /* @__PURE__ */ new Set([file]));
|
|
6588
6876
|
if (!root || root === file) continue;
|
|
@@ -6594,7 +6882,7 @@ function clusterFiles(graph) {
|
|
|
6594
6882
|
}
|
|
6595
6883
|
|
|
6596
6884
|
// src/commands/refactor/restructure/computeRewrites/index.ts
|
|
6597
|
-
import
|
|
6885
|
+
import path34 from "path";
|
|
6598
6886
|
|
|
6599
6887
|
// src/commands/refactor/restructure/computeRewrites/applyRewrites.ts
|
|
6600
6888
|
import fs18 from "fs";
|
|
@@ -6603,7 +6891,7 @@ function getOrCreateList(map, key) {
|
|
|
6603
6891
|
if (!map.has(key)) map.set(key, list4);
|
|
6604
6892
|
return list4;
|
|
6605
6893
|
}
|
|
6606
|
-
function
|
|
6894
|
+
function groupByFile2(rewrites) {
|
|
6607
6895
|
const grouped = /* @__PURE__ */ new Map();
|
|
6608
6896
|
for (const rewrite of rewrites) {
|
|
6609
6897
|
getOrCreateList(grouped, rewrite.file).push(rewrite);
|
|
@@ -6624,7 +6912,7 @@ function applyFileRewrites(file, fileRewrites) {
|
|
|
6624
6912
|
}
|
|
6625
6913
|
function applyRewrites(rewrites) {
|
|
6626
6914
|
const updatedContents = /* @__PURE__ */ new Map();
|
|
6627
|
-
for (const [file, fileRewrites] of
|
|
6915
|
+
for (const [file, fileRewrites] of groupByFile2(rewrites)) {
|
|
6628
6916
|
updatedContents.set(file, applyFileRewrites(file, fileRewrites));
|
|
6629
6917
|
}
|
|
6630
6918
|
return updatedContents;
|
|
@@ -6648,7 +6936,7 @@ function normalizeSpecifier(rel) {
|
|
|
6648
6936
|
);
|
|
6649
6937
|
}
|
|
6650
6938
|
function computeSpecifier(fromFile, toFile) {
|
|
6651
|
-
return normalizeSpecifier(
|
|
6939
|
+
return normalizeSpecifier(path34.relative(path34.dirname(fromFile), toFile));
|
|
6652
6940
|
}
|
|
6653
6941
|
function isAffected(edge, moveMap) {
|
|
6654
6942
|
return moveMap.has(edge.target) || moveMap.has(edge.source);
|
|
@@ -6692,51 +6980,51 @@ function computeRewrites(moves, edges, allProjectFiles) {
|
|
|
6692
6980
|
}
|
|
6693
6981
|
|
|
6694
6982
|
// src/commands/refactor/restructure/displayPlan.ts
|
|
6695
|
-
import
|
|
6696
|
-
import
|
|
6983
|
+
import path35 from "path";
|
|
6984
|
+
import chalk78 from "chalk";
|
|
6697
6985
|
function relPath(filePath) {
|
|
6698
|
-
return
|
|
6986
|
+
return path35.relative(process.cwd(), filePath);
|
|
6699
6987
|
}
|
|
6700
6988
|
function displayMoves(plan) {
|
|
6701
6989
|
if (plan.moves.length === 0) return;
|
|
6702
|
-
console.log(
|
|
6990
|
+
console.log(chalk78.bold("\nFile moves:"));
|
|
6703
6991
|
for (const move of plan.moves) {
|
|
6704
6992
|
console.log(
|
|
6705
|
-
` ${
|
|
6993
|
+
` ${chalk78.red(relPath(move.from))} \u2192 ${chalk78.green(relPath(move.to))}`
|
|
6706
6994
|
);
|
|
6707
|
-
console.log(
|
|
6995
|
+
console.log(chalk78.dim(` ${move.reason}`));
|
|
6708
6996
|
}
|
|
6709
6997
|
}
|
|
6710
6998
|
function displayRewrites(rewrites) {
|
|
6711
6999
|
if (rewrites.length === 0) return;
|
|
6712
7000
|
const affectedFiles = new Set(rewrites.map((r) => r.file));
|
|
6713
|
-
console.log(
|
|
7001
|
+
console.log(chalk78.bold(`
|
|
6714
7002
|
Import rewrites (${affectedFiles.size} files):`));
|
|
6715
7003
|
for (const file of affectedFiles) {
|
|
6716
|
-
console.log(` ${
|
|
7004
|
+
console.log(` ${chalk78.cyan(relPath(file))}:`);
|
|
6717
7005
|
for (const { oldSpecifier, newSpecifier } of rewrites.filter(
|
|
6718
7006
|
(r) => r.file === file
|
|
6719
7007
|
)) {
|
|
6720
7008
|
console.log(
|
|
6721
|
-
` ${
|
|
7009
|
+
` ${chalk78.red(`"${oldSpecifier}"`)} \u2192 ${chalk78.green(`"${newSpecifier}"`)}`
|
|
6722
7010
|
);
|
|
6723
7011
|
}
|
|
6724
7012
|
}
|
|
6725
7013
|
}
|
|
6726
7014
|
function displayPlan(plan) {
|
|
6727
7015
|
if (plan.warnings.length > 0) {
|
|
6728
|
-
console.log(
|
|
6729
|
-
for (const w of plan.warnings) console.log(
|
|
7016
|
+
console.log(chalk78.yellow("\nWarnings:"));
|
|
7017
|
+
for (const w of plan.warnings) console.log(chalk78.yellow(` ${w}`));
|
|
6730
7018
|
}
|
|
6731
7019
|
if (plan.newDirectories.length > 0) {
|
|
6732
|
-
console.log(
|
|
7020
|
+
console.log(chalk78.bold("\nNew directories:"));
|
|
6733
7021
|
for (const dir of plan.newDirectories)
|
|
6734
|
-
console.log(
|
|
7022
|
+
console.log(chalk78.green(` ${dir}/`));
|
|
6735
7023
|
}
|
|
6736
7024
|
displayMoves(plan);
|
|
6737
7025
|
displayRewrites(plan.rewrites);
|
|
6738
7026
|
console.log(
|
|
6739
|
-
|
|
7027
|
+
chalk78.dim(
|
|
6740
7028
|
`
|
|
6741
7029
|
Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rewritten`
|
|
6742
7030
|
)
|
|
@@ -6745,33 +7033,33 @@ Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rew
|
|
|
6745
7033
|
|
|
6746
7034
|
// src/commands/refactor/restructure/executePlan.ts
|
|
6747
7035
|
import fs19 from "fs";
|
|
6748
|
-
import
|
|
6749
|
-
import
|
|
7036
|
+
import path36 from "path";
|
|
7037
|
+
import chalk79 from "chalk";
|
|
6750
7038
|
function executePlan(plan) {
|
|
6751
7039
|
const updatedContents = applyRewrites(plan.rewrites);
|
|
6752
7040
|
for (const [file, content] of updatedContents) {
|
|
6753
7041
|
fs19.writeFileSync(file, content, "utf-8");
|
|
6754
7042
|
console.log(
|
|
6755
|
-
|
|
7043
|
+
chalk79.cyan(` Rewrote imports in ${path36.relative(process.cwd(), file)}`)
|
|
6756
7044
|
);
|
|
6757
7045
|
}
|
|
6758
7046
|
for (const dir of plan.newDirectories) {
|
|
6759
7047
|
fs19.mkdirSync(dir, { recursive: true });
|
|
6760
|
-
console.log(
|
|
7048
|
+
console.log(chalk79.green(` Created ${path36.relative(process.cwd(), dir)}/`));
|
|
6761
7049
|
}
|
|
6762
7050
|
for (const move of plan.moves) {
|
|
6763
|
-
const targetDir =
|
|
7051
|
+
const targetDir = path36.dirname(move.to);
|
|
6764
7052
|
if (!fs19.existsSync(targetDir)) {
|
|
6765
7053
|
fs19.mkdirSync(targetDir, { recursive: true });
|
|
6766
7054
|
}
|
|
6767
7055
|
fs19.renameSync(move.from, move.to);
|
|
6768
7056
|
console.log(
|
|
6769
|
-
|
|
6770
|
-
` Moved ${
|
|
7057
|
+
chalk79.white(
|
|
7058
|
+
` Moved ${path36.relative(process.cwd(), move.from)} \u2192 ${path36.relative(process.cwd(), move.to)}`
|
|
6771
7059
|
)
|
|
6772
7060
|
);
|
|
6773
7061
|
}
|
|
6774
|
-
removeEmptyDirectories(plan.moves.map((m) =>
|
|
7062
|
+
removeEmptyDirectories(plan.moves.map((m) => path36.dirname(m.from)));
|
|
6775
7063
|
}
|
|
6776
7064
|
function removeEmptyDirectories(dirs) {
|
|
6777
7065
|
const unique = [...new Set(dirs)];
|
|
@@ -6781,8 +7069,8 @@ function removeEmptyDirectories(dirs) {
|
|
|
6781
7069
|
if (entries.length === 0) {
|
|
6782
7070
|
fs19.rmdirSync(dir);
|
|
6783
7071
|
console.log(
|
|
6784
|
-
|
|
6785
|
-
` Removed empty directory ${
|
|
7072
|
+
chalk79.dim(
|
|
7073
|
+
` Removed empty directory ${path36.relative(process.cwd(), dir)}`
|
|
6786
7074
|
)
|
|
6787
7075
|
);
|
|
6788
7076
|
}
|
|
@@ -6790,7 +7078,7 @@ function removeEmptyDirectories(dirs) {
|
|
|
6790
7078
|
}
|
|
6791
7079
|
|
|
6792
7080
|
// src/commands/refactor/restructure/planFileMoves/index.ts
|
|
6793
|
-
import
|
|
7081
|
+
import path38 from "path";
|
|
6794
7082
|
|
|
6795
7083
|
// src/commands/refactor/restructure/planFileMoves/shared.ts
|
|
6796
7084
|
import fs20 from "fs";
|
|
@@ -6805,9 +7093,9 @@ function checkDirConflict(result, label2, dir) {
|
|
|
6805
7093
|
|
|
6806
7094
|
// src/commands/refactor/restructure/planFileMoves/planDirectoryMoves.ts
|
|
6807
7095
|
import fs21 from "fs";
|
|
6808
|
-
import
|
|
7096
|
+
import path37 from "path";
|
|
6809
7097
|
function collectEntry(results, dir, entry) {
|
|
6810
|
-
const full =
|
|
7098
|
+
const full = path37.join(dir, entry.name);
|
|
6811
7099
|
const items = entry.isDirectory() ? listFilesRecursive(full) : [full];
|
|
6812
7100
|
results.push(...items);
|
|
6813
7101
|
}
|
|
@@ -6821,15 +7109,15 @@ function listFilesRecursive(dir) {
|
|
|
6821
7109
|
}
|
|
6822
7110
|
function addDirectoryFileMoves(moves, childDir, newLocation, reason) {
|
|
6823
7111
|
for (const file of listFilesRecursive(childDir)) {
|
|
6824
|
-
const rel =
|
|
6825
|
-
moves.push({ from: file, to:
|
|
7112
|
+
const rel = path37.relative(childDir, file);
|
|
7113
|
+
moves.push({ from: file, to: path37.join(newLocation, rel), reason });
|
|
6826
7114
|
}
|
|
6827
7115
|
}
|
|
6828
7116
|
function resolveChildDest(parentDir, childDir) {
|
|
6829
|
-
return
|
|
7117
|
+
return path37.join(parentDir, path37.basename(childDir));
|
|
6830
7118
|
}
|
|
6831
7119
|
function childMoveReason(parentDir) {
|
|
6832
|
-
return `Directory only imported from ${
|
|
7120
|
+
return `Directory only imported from ${path37.basename(parentDir)}/`;
|
|
6833
7121
|
}
|
|
6834
7122
|
function registerDirectoryMove(result, childDir, dest, parentDir) {
|
|
6835
7123
|
result.directories.push(dest);
|
|
@@ -6854,7 +7142,7 @@ function planDirectoryMoves(clusters) {
|
|
|
6854
7142
|
|
|
6855
7143
|
// src/commands/refactor/restructure/planFileMoves/index.ts
|
|
6856
7144
|
function childMoveData(child, newDir, parentBase) {
|
|
6857
|
-
const to =
|
|
7145
|
+
const to = path38.join(newDir, path38.basename(child));
|
|
6858
7146
|
return { from: child, to, reason: `Only imported by ${parentBase}` };
|
|
6859
7147
|
}
|
|
6860
7148
|
function addChildMoves(moves, children, newDir, parentBase) {
|
|
@@ -6862,15 +7150,15 @@ function addChildMoves(moves, children, newDir, parentBase) {
|
|
|
6862
7150
|
moves.push(childMoveData(child, newDir, parentBase));
|
|
6863
7151
|
}
|
|
6864
7152
|
function getBaseName(filePath) {
|
|
6865
|
-
return
|
|
7153
|
+
return path38.basename(filePath, path38.extname(filePath));
|
|
6866
7154
|
}
|
|
6867
7155
|
function resolveClusterDir(parent) {
|
|
6868
|
-
return
|
|
7156
|
+
return path38.join(path38.dirname(parent), getBaseName(parent));
|
|
6869
7157
|
}
|
|
6870
7158
|
function createParentMove(parent, newDir) {
|
|
6871
7159
|
return {
|
|
6872
7160
|
from: parent,
|
|
6873
|
-
to:
|
|
7161
|
+
to: path38.join(newDir, `index${path38.extname(parent)}`),
|
|
6874
7162
|
reason: `Main module of new ${getBaseName(parent)}/ directory`
|
|
6875
7163
|
};
|
|
6876
7164
|
}
|
|
@@ -6894,7 +7182,7 @@ function planFileMoves(clusters) {
|
|
|
6894
7182
|
|
|
6895
7183
|
// src/commands/refactor/restructure/index.ts
|
|
6896
7184
|
function buildPlan(candidateFiles, tsConfigPath) {
|
|
6897
|
-
const candidates = new Set(candidateFiles.map((f) =>
|
|
7185
|
+
const candidates = new Set(candidateFiles.map((f) => path39.resolve(f)));
|
|
6898
7186
|
const graph = buildImportGraph(candidates, tsConfigPath);
|
|
6899
7187
|
const allProjectFiles = /* @__PURE__ */ new Set([
|
|
6900
7188
|
...graph.importedBy.keys(),
|
|
@@ -6914,22 +7202,22 @@ async function restructure(pattern2, options2 = {}) {
|
|
|
6914
7202
|
const targetPattern = pattern2 ?? "src";
|
|
6915
7203
|
const files = findSourceFiles2(targetPattern);
|
|
6916
7204
|
if (files.length === 0) {
|
|
6917
|
-
console.log(
|
|
7205
|
+
console.log(chalk80.yellow("No files found matching pattern"));
|
|
6918
7206
|
return;
|
|
6919
7207
|
}
|
|
6920
|
-
const tsConfigPath =
|
|
7208
|
+
const tsConfigPath = path39.resolve("tsconfig.json");
|
|
6921
7209
|
const plan = buildPlan(files, tsConfigPath);
|
|
6922
7210
|
if (plan.moves.length === 0) {
|
|
6923
|
-
console.log(
|
|
7211
|
+
console.log(chalk80.green("No restructuring needed"));
|
|
6924
7212
|
return;
|
|
6925
7213
|
}
|
|
6926
7214
|
displayPlan(plan);
|
|
6927
7215
|
if (options2.apply) {
|
|
6928
|
-
console.log(
|
|
7216
|
+
console.log(chalk80.bold("\nApplying changes..."));
|
|
6929
7217
|
executePlan(plan);
|
|
6930
|
-
console.log(
|
|
7218
|
+
console.log(chalk80.green("\nRestructuring complete"));
|
|
6931
7219
|
} else {
|
|
6932
|
-
console.log(
|
|
7220
|
+
console.log(chalk80.dim("\nDry run. Use --apply to execute."));
|
|
6933
7221
|
}
|
|
6934
7222
|
}
|
|
6935
7223
|
|
|
@@ -6957,8 +7245,8 @@ function registerRefactor(program2) {
|
|
|
6957
7245
|
}
|
|
6958
7246
|
|
|
6959
7247
|
// src/commands/transcript/shared.ts
|
|
6960
|
-
import { existsSync as
|
|
6961
|
-
import { basename as basename4, join as
|
|
7248
|
+
import { existsSync as existsSync27, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
|
|
7249
|
+
import { basename as basename4, join as join24, relative } from "path";
|
|
6962
7250
|
import * as readline2 from "readline";
|
|
6963
7251
|
var DATE_PREFIX_REGEX = /^\d{4}-\d{2}-\d{2}/;
|
|
6964
7252
|
function getDatePrefix(daysOffset = 0) {
|
|
@@ -6973,10 +7261,10 @@ function isValidDatePrefix(filename) {
|
|
|
6973
7261
|
return DATE_PREFIX_REGEX.test(filename);
|
|
6974
7262
|
}
|
|
6975
7263
|
function collectFiles(dir, extension) {
|
|
6976
|
-
if (!
|
|
7264
|
+
if (!existsSync27(dir)) return [];
|
|
6977
7265
|
const results = [];
|
|
6978
|
-
for (const entry of
|
|
6979
|
-
const fullPath =
|
|
7266
|
+
for (const entry of readdirSync5(dir)) {
|
|
7267
|
+
const fullPath = join24(dir, entry);
|
|
6980
7268
|
if (statSync2(fullPath).isDirectory()) {
|
|
6981
7269
|
results.push(...collectFiles(fullPath, extension));
|
|
6982
7270
|
} else if (entry.endsWith(extension)) {
|
|
@@ -7070,14 +7358,14 @@ async function configure() {
|
|
|
7070
7358
|
}
|
|
7071
7359
|
|
|
7072
7360
|
// src/commands/transcript/format/index.ts
|
|
7073
|
-
import { existsSync as
|
|
7361
|
+
import { existsSync as existsSync29 } from "fs";
|
|
7074
7362
|
|
|
7075
7363
|
// src/commands/transcript/format/fixInvalidDatePrefixes/index.ts
|
|
7076
|
-
import { dirname as
|
|
7364
|
+
import { dirname as dirname18, join as join26 } from "path";
|
|
7077
7365
|
|
|
7078
7366
|
// src/commands/transcript/format/fixInvalidDatePrefixes/promptForDateFix.ts
|
|
7079
7367
|
import { renameSync } from "fs";
|
|
7080
|
-
import { join as
|
|
7368
|
+
import { join as join25 } from "path";
|
|
7081
7369
|
async function resolveDate(rl, choice) {
|
|
7082
7370
|
if (choice === "1") return getDatePrefix(0);
|
|
7083
7371
|
if (choice === "2") return getDatePrefix(-1);
|
|
@@ -7092,7 +7380,7 @@ async function resolveDate(rl, choice) {
|
|
|
7092
7380
|
}
|
|
7093
7381
|
function renameWithPrefix(vttDir, vttFile, prefix2) {
|
|
7094
7382
|
const newFilename = `${prefix2}.${vttFile}`;
|
|
7095
|
-
renameSync(
|
|
7383
|
+
renameSync(join25(vttDir, vttFile), join25(vttDir, newFilename));
|
|
7096
7384
|
console.log(`Renamed to: ${newFilename}`);
|
|
7097
7385
|
return newFilename;
|
|
7098
7386
|
}
|
|
@@ -7123,15 +7411,15 @@ async function fixInvalidDatePrefixes(vttFiles) {
|
|
|
7123
7411
|
for (let i = 0; i < vttFiles.length; i++) {
|
|
7124
7412
|
const vttFile = vttFiles[i];
|
|
7125
7413
|
if (!isValidDatePrefix(vttFile.filename)) {
|
|
7126
|
-
const vttFileDir =
|
|
7414
|
+
const vttFileDir = dirname18(vttFile.absolutePath);
|
|
7127
7415
|
const newFilename = await promptForDateFix(vttFile.filename, vttFileDir);
|
|
7128
7416
|
if (newFilename) {
|
|
7129
|
-
const newRelativePath =
|
|
7130
|
-
|
|
7417
|
+
const newRelativePath = join26(
|
|
7418
|
+
dirname18(vttFile.relativePath),
|
|
7131
7419
|
newFilename
|
|
7132
7420
|
);
|
|
7133
7421
|
vttFiles[i] = {
|
|
7134
|
-
absolutePath:
|
|
7422
|
+
absolutePath: join26(vttFileDir, newFilename),
|
|
7135
7423
|
relativePath: newRelativePath,
|
|
7136
7424
|
filename: newFilename
|
|
7137
7425
|
};
|
|
@@ -7144,8 +7432,8 @@ async function fixInvalidDatePrefixes(vttFiles) {
|
|
|
7144
7432
|
}
|
|
7145
7433
|
|
|
7146
7434
|
// src/commands/transcript/format/processVttFile/index.ts
|
|
7147
|
-
import { existsSync as
|
|
7148
|
-
import { basename as basename5, dirname as
|
|
7435
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync7, readFileSync as readFileSync23, writeFileSync as writeFileSync22 } from "fs";
|
|
7436
|
+
import { basename as basename5, dirname as dirname19, join as join27 } from "path";
|
|
7149
7437
|
|
|
7150
7438
|
// src/commands/transcript/cleanText.ts
|
|
7151
7439
|
function cleanText(text) {
|
|
@@ -7355,21 +7643,21 @@ function toMdFilename(vttFilename) {
|
|
|
7355
7643
|
return `${basename5(vttFilename, ".vtt").replace(/\s*Transcription\s*/g, " ").trim()}.md`;
|
|
7356
7644
|
}
|
|
7357
7645
|
function resolveOutputDir(relativeDir, transcriptsDir) {
|
|
7358
|
-
return relativeDir === "." ? transcriptsDir :
|
|
7646
|
+
return relativeDir === "." ? transcriptsDir : join27(transcriptsDir, relativeDir);
|
|
7359
7647
|
}
|
|
7360
7648
|
function buildOutputPaths(vttFile, transcriptsDir) {
|
|
7361
7649
|
const mdFile = toMdFilename(vttFile.filename);
|
|
7362
|
-
const relativeDir =
|
|
7650
|
+
const relativeDir = dirname19(vttFile.relativePath);
|
|
7363
7651
|
const outputDir = resolveOutputDir(relativeDir, transcriptsDir);
|
|
7364
|
-
const outputPath =
|
|
7652
|
+
const outputPath = join27(outputDir, mdFile);
|
|
7365
7653
|
return { outputDir, outputPath, mdFile, relativeDir };
|
|
7366
7654
|
}
|
|
7367
7655
|
function logSkipped(relativeDir, mdFile) {
|
|
7368
|
-
console.log(`Skipping (already exists): ${
|
|
7656
|
+
console.log(`Skipping (already exists): ${join27(relativeDir, mdFile)}`);
|
|
7369
7657
|
return "skipped";
|
|
7370
7658
|
}
|
|
7371
7659
|
function ensureDirectory(dir, label2) {
|
|
7372
|
-
if (!
|
|
7660
|
+
if (!existsSync28(dir)) {
|
|
7373
7661
|
mkdirSync7(dir, { recursive: true });
|
|
7374
7662
|
console.log(`Created ${label2}: ${dir}`);
|
|
7375
7663
|
}
|
|
@@ -7392,7 +7680,7 @@ function logReduction(cueCount, messageCount) {
|
|
|
7392
7680
|
}
|
|
7393
7681
|
function readAndParseCues(inputPath) {
|
|
7394
7682
|
console.log(`Reading: ${inputPath}`);
|
|
7395
|
-
return processCues(
|
|
7683
|
+
return processCues(readFileSync23(inputPath, "utf-8"));
|
|
7396
7684
|
}
|
|
7397
7685
|
function writeFormatted(outputPath, content) {
|
|
7398
7686
|
writeFileSync22(outputPath, content, "utf-8");
|
|
@@ -7405,7 +7693,7 @@ function convertVttToMarkdown(inputPath, outputPath) {
|
|
|
7405
7693
|
logReduction(cues.length, chatMessages.length);
|
|
7406
7694
|
}
|
|
7407
7695
|
function tryProcessVtt(vttFile, paths) {
|
|
7408
|
-
if (
|
|
7696
|
+
if (existsSync28(paths.outputPath))
|
|
7409
7697
|
return logSkipped(paths.relativeDir, paths.mdFile);
|
|
7410
7698
|
convertVttToMarkdown(vttFile.absolutePath, paths.outputPath);
|
|
7411
7699
|
return "processed";
|
|
@@ -7431,7 +7719,7 @@ function processAllFiles(vttFiles, transcriptsDir) {
|
|
|
7431
7719
|
logSummary(counts);
|
|
7432
7720
|
}
|
|
7433
7721
|
function requireVttDir(vttDir) {
|
|
7434
|
-
if (!
|
|
7722
|
+
if (!existsSync29(vttDir)) {
|
|
7435
7723
|
console.error(`VTT directory not found: ${vttDir}`);
|
|
7436
7724
|
process.exit(1);
|
|
7437
7725
|
}
|
|
@@ -7463,28 +7751,28 @@ async function format() {
|
|
|
7463
7751
|
}
|
|
7464
7752
|
|
|
7465
7753
|
// src/commands/transcript/summarise/index.ts
|
|
7466
|
-
import { existsSync as
|
|
7467
|
-
import { basename as basename6, dirname as
|
|
7754
|
+
import { existsSync as existsSync31 } from "fs";
|
|
7755
|
+
import { basename as basename6, dirname as dirname21, join as join29, relative as relative2 } from "path";
|
|
7468
7756
|
|
|
7469
7757
|
// src/commands/transcript/summarise/processStagedFile/index.ts
|
|
7470
7758
|
import {
|
|
7471
|
-
existsSync as
|
|
7759
|
+
existsSync as existsSync30,
|
|
7472
7760
|
mkdirSync as mkdirSync8,
|
|
7473
|
-
readFileSync as
|
|
7761
|
+
readFileSync as readFileSync24,
|
|
7474
7762
|
renameSync as renameSync2,
|
|
7475
7763
|
rmSync
|
|
7476
7764
|
} from "fs";
|
|
7477
|
-
import { dirname as
|
|
7765
|
+
import { dirname as dirname20, join as join28 } from "path";
|
|
7478
7766
|
|
|
7479
7767
|
// src/commands/transcript/summarise/processStagedFile/validateStagedContent.ts
|
|
7480
|
-
import
|
|
7768
|
+
import chalk81 from "chalk";
|
|
7481
7769
|
var FULL_TRANSCRIPT_REGEX = /^\[Full Transcript\]\(([^)]+)\)/;
|
|
7482
7770
|
function validateStagedContent(filename, content) {
|
|
7483
7771
|
const firstLine = content.split("\n")[0];
|
|
7484
7772
|
const match = firstLine.match(FULL_TRANSCRIPT_REGEX);
|
|
7485
7773
|
if (!match) {
|
|
7486
7774
|
console.error(
|
|
7487
|
-
|
|
7775
|
+
chalk81.red(
|
|
7488
7776
|
`Staged file ${filename} missing [Full Transcript](<path>) link on first line.`
|
|
7489
7777
|
)
|
|
7490
7778
|
);
|
|
@@ -7493,7 +7781,7 @@ function validateStagedContent(filename, content) {
|
|
|
7493
7781
|
const contentAfterLink = content.slice(firstLine.length).trim();
|
|
7494
7782
|
if (!contentAfterLink) {
|
|
7495
7783
|
console.error(
|
|
7496
|
-
|
|
7784
|
+
chalk81.red(
|
|
7497
7785
|
`Staged file ${filename} has no summary content after the transcript link.`
|
|
7498
7786
|
)
|
|
7499
7787
|
);
|
|
@@ -7503,9 +7791,9 @@ function validateStagedContent(filename, content) {
|
|
|
7503
7791
|
}
|
|
7504
7792
|
|
|
7505
7793
|
// src/commands/transcript/summarise/processStagedFile/index.ts
|
|
7506
|
-
var STAGING_DIR =
|
|
7794
|
+
var STAGING_DIR = join28(process.cwd(), ".assist", "transcript");
|
|
7507
7795
|
function processStagedFile() {
|
|
7508
|
-
if (!
|
|
7796
|
+
if (!existsSync30(STAGING_DIR)) {
|
|
7509
7797
|
return false;
|
|
7510
7798
|
}
|
|
7511
7799
|
const stagedFiles = findMdFilesRecursive(STAGING_DIR);
|
|
@@ -7514,7 +7802,7 @@ function processStagedFile() {
|
|
|
7514
7802
|
}
|
|
7515
7803
|
const { transcriptsDir, summaryDir } = getTranscriptConfig();
|
|
7516
7804
|
const stagedFile = stagedFiles[0];
|
|
7517
|
-
const content =
|
|
7805
|
+
const content = readFileSync24(stagedFile.absolutePath, "utf-8");
|
|
7518
7806
|
validateStagedContent(stagedFile.filename, content);
|
|
7519
7807
|
const stagedBaseName = getTranscriptBaseName(stagedFile.filename);
|
|
7520
7808
|
const transcriptFiles = findMdFilesRecursive(transcriptsDir);
|
|
@@ -7527,9 +7815,9 @@ function processStagedFile() {
|
|
|
7527
7815
|
);
|
|
7528
7816
|
process.exit(1);
|
|
7529
7817
|
}
|
|
7530
|
-
const destPath =
|
|
7531
|
-
const destDir =
|
|
7532
|
-
if (!
|
|
7818
|
+
const destPath = join28(summaryDir, matchingTranscript.relativePath);
|
|
7819
|
+
const destDir = dirname20(destPath);
|
|
7820
|
+
if (!existsSync30(destDir)) {
|
|
7533
7821
|
mkdirSync8(destDir, { recursive: true });
|
|
7534
7822
|
}
|
|
7535
7823
|
renameSync2(stagedFile.absolutePath, destPath);
|
|
@@ -7542,8 +7830,8 @@ function processStagedFile() {
|
|
|
7542
7830
|
|
|
7543
7831
|
// src/commands/transcript/summarise/index.ts
|
|
7544
7832
|
function buildRelativeKey(relativePath, baseName) {
|
|
7545
|
-
const relDir =
|
|
7546
|
-
return relDir === "." ? baseName :
|
|
7833
|
+
const relDir = dirname21(relativePath);
|
|
7834
|
+
return relDir === "." ? baseName : join29(relDir, baseName);
|
|
7547
7835
|
}
|
|
7548
7836
|
function buildSummaryIndex(summaryDir) {
|
|
7549
7837
|
const summaryFiles = findMdFilesRecursive(summaryDir);
|
|
@@ -7556,7 +7844,7 @@ function buildSummaryIndex(summaryDir) {
|
|
|
7556
7844
|
function summarise2() {
|
|
7557
7845
|
processStagedFile();
|
|
7558
7846
|
const { transcriptsDir, summaryDir } = getTranscriptConfig();
|
|
7559
|
-
if (!
|
|
7847
|
+
if (!existsSync31(transcriptsDir)) {
|
|
7560
7848
|
console.log("No transcripts directory found.");
|
|
7561
7849
|
return;
|
|
7562
7850
|
}
|
|
@@ -7577,8 +7865,8 @@ function summarise2() {
|
|
|
7577
7865
|
}
|
|
7578
7866
|
const next2 = missing[0];
|
|
7579
7867
|
const outputFilename = `${getTranscriptBaseName(next2.filename)}.md`;
|
|
7580
|
-
const outputPath =
|
|
7581
|
-
const summaryFileDir =
|
|
7868
|
+
const outputPath = join29(STAGING_DIR, outputFilename);
|
|
7869
|
+
const summaryFileDir = join29(summaryDir, dirname21(next2.relativePath));
|
|
7582
7870
|
const relativeTranscriptPath = encodeURI(
|
|
7583
7871
|
relative2(summaryFileDir, next2.absolutePath).replace(/\\/g, "/")
|
|
7584
7872
|
);
|
|
@@ -7624,50 +7912,50 @@ function registerVerify(program2) {
|
|
|
7624
7912
|
|
|
7625
7913
|
// src/commands/voice/devices.ts
|
|
7626
7914
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
7627
|
-
import { join as
|
|
7915
|
+
import { join as join31 } from "path";
|
|
7628
7916
|
|
|
7629
7917
|
// src/commands/voice/shared.ts
|
|
7630
7918
|
import { homedir as homedir7 } from "os";
|
|
7631
|
-
import { dirname as
|
|
7919
|
+
import { dirname as dirname22, join as join30 } from "path";
|
|
7632
7920
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
7633
|
-
var __dirname6 =
|
|
7634
|
-
var VOICE_DIR =
|
|
7921
|
+
var __dirname6 = dirname22(fileURLToPath6(import.meta.url));
|
|
7922
|
+
var VOICE_DIR = join30(homedir7(), ".assist", "voice");
|
|
7635
7923
|
var voicePaths = {
|
|
7636
7924
|
dir: VOICE_DIR,
|
|
7637
|
-
pid:
|
|
7638
|
-
log:
|
|
7639
|
-
venv:
|
|
7640
|
-
lock:
|
|
7925
|
+
pid: join30(VOICE_DIR, "voice.pid"),
|
|
7926
|
+
log: join30(VOICE_DIR, "voice.log"),
|
|
7927
|
+
venv: join30(VOICE_DIR, ".venv"),
|
|
7928
|
+
lock: join30(VOICE_DIR, "voice.lock")
|
|
7641
7929
|
};
|
|
7642
7930
|
function getPythonDir() {
|
|
7643
|
-
return
|
|
7931
|
+
return join30(__dirname6, "commands", "voice", "python");
|
|
7644
7932
|
}
|
|
7645
7933
|
function getVenvPython() {
|
|
7646
|
-
return process.platform === "win32" ?
|
|
7934
|
+
return process.platform === "win32" ? join30(voicePaths.venv, "Scripts", "python.exe") : join30(voicePaths.venv, "bin", "python");
|
|
7647
7935
|
}
|
|
7648
7936
|
function getLockDir() {
|
|
7649
7937
|
const config = loadConfig();
|
|
7650
7938
|
return config.voice?.lockDir ?? VOICE_DIR;
|
|
7651
7939
|
}
|
|
7652
7940
|
function getLockFile() {
|
|
7653
|
-
return
|
|
7941
|
+
return join30(getLockDir(), "voice.lock");
|
|
7654
7942
|
}
|
|
7655
7943
|
|
|
7656
7944
|
// src/commands/voice/devices.ts
|
|
7657
7945
|
function devices() {
|
|
7658
|
-
const script =
|
|
7946
|
+
const script = join31(getPythonDir(), "list_devices.py");
|
|
7659
7947
|
spawnSync3(getVenvPython(), [script], { stdio: "inherit" });
|
|
7660
7948
|
}
|
|
7661
7949
|
|
|
7662
7950
|
// src/commands/voice/logs.ts
|
|
7663
|
-
import { existsSync as
|
|
7951
|
+
import { existsSync as existsSync32, readFileSync as readFileSync25 } from "fs";
|
|
7664
7952
|
function logs(options2) {
|
|
7665
|
-
if (!
|
|
7953
|
+
if (!existsSync32(voicePaths.log)) {
|
|
7666
7954
|
console.log("No voice log file found");
|
|
7667
7955
|
return;
|
|
7668
7956
|
}
|
|
7669
7957
|
const count = Number.parseInt(options2.lines ?? "150", 10);
|
|
7670
|
-
const content =
|
|
7958
|
+
const content = readFileSync25(voicePaths.log, "utf-8").trim();
|
|
7671
7959
|
if (!content) {
|
|
7672
7960
|
console.log("Voice log is empty");
|
|
7673
7961
|
return;
|
|
@@ -7690,12 +7978,12 @@ function logs(options2) {
|
|
|
7690
7978
|
// src/commands/voice/setup.ts
|
|
7691
7979
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
7692
7980
|
import { mkdirSync as mkdirSync10 } from "fs";
|
|
7693
|
-
import { join as
|
|
7981
|
+
import { join as join33 } from "path";
|
|
7694
7982
|
|
|
7695
7983
|
// src/commands/voice/checkLockFile.ts
|
|
7696
|
-
import { execSync as
|
|
7697
|
-
import { existsSync as
|
|
7698
|
-
import { join as
|
|
7984
|
+
import { execSync as execSync34 } from "child_process";
|
|
7985
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync9, readFileSync as readFileSync26, writeFileSync as writeFileSync23 } from "fs";
|
|
7986
|
+
import { join as join32 } from "path";
|
|
7699
7987
|
function isProcessAlive(pid) {
|
|
7700
7988
|
try {
|
|
7701
7989
|
process.kill(pid, 0);
|
|
@@ -7706,9 +7994,9 @@ function isProcessAlive(pid) {
|
|
|
7706
7994
|
}
|
|
7707
7995
|
function checkLockFile() {
|
|
7708
7996
|
const lockFile = getLockFile();
|
|
7709
|
-
if (!
|
|
7997
|
+
if (!existsSync33(lockFile)) return;
|
|
7710
7998
|
try {
|
|
7711
|
-
const lock = JSON.parse(
|
|
7999
|
+
const lock = JSON.parse(readFileSync26(lockFile, "utf-8"));
|
|
7712
8000
|
if (lock.pid && isProcessAlive(lock.pid)) {
|
|
7713
8001
|
console.error(
|
|
7714
8002
|
`Voice daemon already running (PID ${lock.pid}, env: ${lock.env}). Stop it first with: assist voice stop`
|
|
@@ -7719,10 +8007,10 @@ function checkLockFile() {
|
|
|
7719
8007
|
}
|
|
7720
8008
|
}
|
|
7721
8009
|
function bootstrapVenv() {
|
|
7722
|
-
if (
|
|
8010
|
+
if (existsSync33(getVenvPython())) return;
|
|
7723
8011
|
console.log("Setting up Python environment...");
|
|
7724
8012
|
const pythonDir = getPythonDir();
|
|
7725
|
-
|
|
8013
|
+
execSync34(
|
|
7726
8014
|
`uv sync --project "${pythonDir}" --extra runtime --no-install-project`,
|
|
7727
8015
|
{
|
|
7728
8016
|
stdio: "inherit",
|
|
@@ -7732,7 +8020,7 @@ function bootstrapVenv() {
|
|
|
7732
8020
|
}
|
|
7733
8021
|
function writeLockFile(pid) {
|
|
7734
8022
|
const lockFile = getLockFile();
|
|
7735
|
-
mkdirSync9(
|
|
8023
|
+
mkdirSync9(join32(lockFile, ".."), { recursive: true });
|
|
7736
8024
|
writeFileSync23(
|
|
7737
8025
|
lockFile,
|
|
7738
8026
|
JSON.stringify({
|
|
@@ -7748,7 +8036,7 @@ function setup() {
|
|
|
7748
8036
|
mkdirSync10(voicePaths.dir, { recursive: true });
|
|
7749
8037
|
bootstrapVenv();
|
|
7750
8038
|
console.log("\nDownloading models...\n");
|
|
7751
|
-
const script =
|
|
8039
|
+
const script = join33(getPythonDir(), "setup_models.py");
|
|
7752
8040
|
const result = spawnSync4(getVenvPython(), [script], {
|
|
7753
8041
|
stdio: "inherit",
|
|
7754
8042
|
env: { ...process.env, VOICE_LOG_FILE: voicePaths.log }
|
|
@@ -7762,7 +8050,7 @@ function setup() {
|
|
|
7762
8050
|
// src/commands/voice/start.ts
|
|
7763
8051
|
import { spawn as spawn4 } from "child_process";
|
|
7764
8052
|
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync24 } from "fs";
|
|
7765
|
-
import { join as
|
|
8053
|
+
import { join as join34 } from "path";
|
|
7766
8054
|
|
|
7767
8055
|
// src/commands/voice/buildDaemonEnv.ts
|
|
7768
8056
|
function buildDaemonEnv(options2) {
|
|
@@ -7800,7 +8088,7 @@ function start2(options2) {
|
|
|
7800
8088
|
bootstrapVenv();
|
|
7801
8089
|
const debug = options2.debug || options2.foreground || process.platform === "win32";
|
|
7802
8090
|
const env = buildDaemonEnv({ debug });
|
|
7803
|
-
const script =
|
|
8091
|
+
const script = join34(getPythonDir(), "voice_daemon.py");
|
|
7804
8092
|
const python = getVenvPython();
|
|
7805
8093
|
if (options2.foreground) {
|
|
7806
8094
|
spawnForeground(python, script, env);
|
|
@@ -7810,7 +8098,7 @@ function start2(options2) {
|
|
|
7810
8098
|
}
|
|
7811
8099
|
|
|
7812
8100
|
// src/commands/voice/status.ts
|
|
7813
|
-
import { existsSync as
|
|
8101
|
+
import { existsSync as existsSync34, readFileSync as readFileSync27 } from "fs";
|
|
7814
8102
|
function isProcessAlive2(pid) {
|
|
7815
8103
|
try {
|
|
7816
8104
|
process.kill(pid, 0);
|
|
@@ -7820,16 +8108,16 @@ function isProcessAlive2(pid) {
|
|
|
7820
8108
|
}
|
|
7821
8109
|
}
|
|
7822
8110
|
function readRecentLogs(count) {
|
|
7823
|
-
if (!
|
|
7824
|
-
const lines =
|
|
8111
|
+
if (!existsSync34(voicePaths.log)) return [];
|
|
8112
|
+
const lines = readFileSync27(voicePaths.log, "utf-8").trim().split("\n");
|
|
7825
8113
|
return lines.slice(-count);
|
|
7826
8114
|
}
|
|
7827
8115
|
function status() {
|
|
7828
|
-
if (!
|
|
8116
|
+
if (!existsSync34(voicePaths.pid)) {
|
|
7829
8117
|
console.log("Voice daemon: not running (no PID file)");
|
|
7830
8118
|
return;
|
|
7831
8119
|
}
|
|
7832
|
-
const pid = Number.parseInt(
|
|
8120
|
+
const pid = Number.parseInt(readFileSync27(voicePaths.pid, "utf-8").trim(), 10);
|
|
7833
8121
|
const alive = isProcessAlive2(pid);
|
|
7834
8122
|
console.log(`Voice daemon: ${alive ? "running" : "dead"} (PID ${pid})`);
|
|
7835
8123
|
const recent = readRecentLogs(5);
|
|
@@ -7848,13 +8136,13 @@ function status() {
|
|
|
7848
8136
|
}
|
|
7849
8137
|
|
|
7850
8138
|
// src/commands/voice/stop.ts
|
|
7851
|
-
import { existsSync as
|
|
8139
|
+
import { existsSync as existsSync35, readFileSync as readFileSync28, unlinkSync as unlinkSync8 } from "fs";
|
|
7852
8140
|
function stop() {
|
|
7853
|
-
if (!
|
|
8141
|
+
if (!existsSync35(voicePaths.pid)) {
|
|
7854
8142
|
console.log("Voice daemon is not running (no PID file)");
|
|
7855
8143
|
return;
|
|
7856
8144
|
}
|
|
7857
|
-
const pid = Number.parseInt(
|
|
8145
|
+
const pid = Number.parseInt(readFileSync28(voicePaths.pid, "utf-8").trim(), 10);
|
|
7858
8146
|
try {
|
|
7859
8147
|
process.kill(pid, "SIGTERM");
|
|
7860
8148
|
console.log(`Sent SIGTERM to voice daemon (PID ${pid})`);
|
|
@@ -7862,12 +8150,12 @@ function stop() {
|
|
|
7862
8150
|
console.log(`Voice daemon (PID ${pid}) is not running`);
|
|
7863
8151
|
}
|
|
7864
8152
|
try {
|
|
7865
|
-
|
|
8153
|
+
unlinkSync8(voicePaths.pid);
|
|
7866
8154
|
} catch {
|
|
7867
8155
|
}
|
|
7868
8156
|
try {
|
|
7869
8157
|
const lockFile = getLockFile();
|
|
7870
|
-
if (
|
|
8158
|
+
if (existsSync35(lockFile)) unlinkSync8(lockFile);
|
|
7871
8159
|
} catch {
|
|
7872
8160
|
}
|
|
7873
8161
|
console.log("Voice daemon stopped");
|
|
@@ -7886,14 +8174,14 @@ function registerVoice(program2) {
|
|
|
7886
8174
|
|
|
7887
8175
|
// src/commands/roam/auth.ts
|
|
7888
8176
|
import { randomBytes } from "crypto";
|
|
7889
|
-
import
|
|
8177
|
+
import chalk82 from "chalk";
|
|
7890
8178
|
|
|
7891
8179
|
// src/lib/openBrowser.ts
|
|
7892
|
-
import { execSync as
|
|
8180
|
+
import { execSync as execSync35 } from "child_process";
|
|
7893
8181
|
function tryExec(commands) {
|
|
7894
8182
|
for (const cmd of commands) {
|
|
7895
8183
|
try {
|
|
7896
|
-
|
|
8184
|
+
execSync35(cmd);
|
|
7897
8185
|
return true;
|
|
7898
8186
|
} catch {
|
|
7899
8187
|
}
|
|
@@ -8061,13 +8349,13 @@ async function auth() {
|
|
|
8061
8349
|
saveGlobalConfig(config);
|
|
8062
8350
|
const state = randomBytes(16).toString("hex");
|
|
8063
8351
|
console.log(
|
|
8064
|
-
|
|
8352
|
+
chalk82.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
|
|
8065
8353
|
);
|
|
8066
|
-
console.log(
|
|
8067
|
-
console.log(
|
|
8068
|
-
console.log(
|
|
8354
|
+
console.log(chalk82.white("http://localhost:14523/callback\n"));
|
|
8355
|
+
console.log(chalk82.blue("Opening browser for authorization..."));
|
|
8356
|
+
console.log(chalk82.dim("Waiting for authorization callback..."));
|
|
8069
8357
|
const { code, redirectUri } = await authorizeInBrowser(clientId, state);
|
|
8070
|
-
console.log(
|
|
8358
|
+
console.log(chalk82.dim("Exchanging code for tokens..."));
|
|
8071
8359
|
const tokens = await exchangeToken({
|
|
8072
8360
|
code,
|
|
8073
8361
|
clientId,
|
|
@@ -8083,7 +8371,7 @@ async function auth() {
|
|
|
8083
8371
|
};
|
|
8084
8372
|
saveGlobalConfig(config);
|
|
8085
8373
|
console.log(
|
|
8086
|
-
|
|
8374
|
+
chalk82.green("Roam credentials and tokens saved to ~/.assist.yml")
|
|
8087
8375
|
);
|
|
8088
8376
|
}
|
|
8089
8377
|
|
|
@@ -8094,16 +8382,7 @@ function registerRoam(program2) {
|
|
|
8094
8382
|
}
|
|
8095
8383
|
|
|
8096
8384
|
// 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
|
-
}
|
|
8385
|
+
import { execSync as execSync36 } from "child_process";
|
|
8107
8386
|
|
|
8108
8387
|
// src/commands/run/resolveParams.ts
|
|
8109
8388
|
function resolveParams(params, cliArgs) {
|
|
@@ -8129,9 +8408,30 @@ function resolveParams(params, cliArgs) {
|
|
|
8129
8408
|
return resolved;
|
|
8130
8409
|
}
|
|
8131
8410
|
|
|
8411
|
+
// src/commands/run/spawnRunCommand.ts
|
|
8412
|
+
import { spawn as spawn5 } from "child_process";
|
|
8413
|
+
function spawnRunCommand(fullCommand, env) {
|
|
8414
|
+
const start3 = Date.now();
|
|
8415
|
+
const child = spawn5(fullCommand, [], {
|
|
8416
|
+
stdio: "inherit",
|
|
8417
|
+
shell: true,
|
|
8418
|
+
env: env ? { ...process.env, ...expandEnv(env) } : void 0
|
|
8419
|
+
});
|
|
8420
|
+
child.on("close", (code) => {
|
|
8421
|
+
const elapsed = formatElapsed(Date.now() - start3);
|
|
8422
|
+
console.log(`
|
|
8423
|
+
Done in ${elapsed}`);
|
|
8424
|
+
process.exit(code ?? 0);
|
|
8425
|
+
});
|
|
8426
|
+
child.on("error", (err) => {
|
|
8427
|
+
console.error(`Failed to execute command: ${err.message}`);
|
|
8428
|
+
process.exit(1);
|
|
8429
|
+
});
|
|
8430
|
+
}
|
|
8431
|
+
|
|
8132
8432
|
// src/commands/run/add.ts
|
|
8133
8433
|
import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync25 } from "fs";
|
|
8134
|
-
import { join as
|
|
8434
|
+
import { join as join35 } from "path";
|
|
8135
8435
|
function findAddIndex() {
|
|
8136
8436
|
const addIndex = process.argv.indexOf("add");
|
|
8137
8437
|
if (addIndex === -1 || addIndex + 2 >= process.argv.length) return -1;
|
|
@@ -8185,7 +8485,7 @@ function saveNewRunConfig(name, command, args) {
|
|
|
8185
8485
|
saveConfig(config);
|
|
8186
8486
|
}
|
|
8187
8487
|
function createCommandFile(name) {
|
|
8188
|
-
const dir =
|
|
8488
|
+
const dir = join35(".claude", "commands");
|
|
8189
8489
|
mkdirSync12(dir, { recursive: true });
|
|
8190
8490
|
const content = `---
|
|
8191
8491
|
description: Run ${name}
|
|
@@ -8193,7 +8493,7 @@ description: Run ${name}
|
|
|
8193
8493
|
|
|
8194
8494
|
Run \`assist run ${name} $ARGUMENTS 2>&1\`.
|
|
8195
8495
|
`;
|
|
8196
|
-
const filePath =
|
|
8496
|
+
const filePath = join35(dir, `${name}.md`);
|
|
8197
8497
|
writeFileSync25(filePath, content);
|
|
8198
8498
|
console.log(`Created command file: ${filePath}`);
|
|
8199
8499
|
}
|
|
@@ -8235,25 +8535,6 @@ function findRunConfig(name) {
|
|
|
8235
8535
|
const configs = requireRunConfigs();
|
|
8236
8536
|
return configs.find((r) => r.name === name) ?? exitWithConfigNotFound(name, configs);
|
|
8237
8537
|
}
|
|
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
8538
|
function listRunConfigs() {
|
|
8258
8539
|
const configs = requireRunConfigs();
|
|
8259
8540
|
for (const config of configs) {
|
|
@@ -8261,17 +8542,28 @@ function listRunConfigs() {
|
|
|
8261
8542
|
console.log(`${config.name}: ${config.command}${args}`);
|
|
8262
8543
|
}
|
|
8263
8544
|
}
|
|
8545
|
+
function runPreCommands(pre) {
|
|
8546
|
+
for (const cmd of pre) {
|
|
8547
|
+
try {
|
|
8548
|
+
execSync36(cmd, { stdio: "inherit" });
|
|
8549
|
+
} catch (err) {
|
|
8550
|
+
const code = err && typeof err === "object" && "status" in err ? err.status : 1;
|
|
8551
|
+
process.exit(code);
|
|
8552
|
+
}
|
|
8553
|
+
}
|
|
8554
|
+
}
|
|
8264
8555
|
function run2(name, args) {
|
|
8265
8556
|
const runConfig = findRunConfig(name);
|
|
8557
|
+
if (runConfig.pre) runPreCommands(runConfig.pre);
|
|
8266
8558
|
const resolved = resolveParams(runConfig.params, args);
|
|
8267
|
-
|
|
8559
|
+
spawnRunCommand(
|
|
8268
8560
|
buildCommand(runConfig.command, runConfig.args ?? [], resolved),
|
|
8269
8561
|
runConfig.env
|
|
8270
8562
|
);
|
|
8271
8563
|
}
|
|
8272
8564
|
|
|
8273
8565
|
// src/commands/statusLine.ts
|
|
8274
|
-
import
|
|
8566
|
+
import chalk83 from "chalk";
|
|
8275
8567
|
function formatNumber(num) {
|
|
8276
8568
|
return num.toLocaleString("en-US");
|
|
8277
8569
|
}
|
|
@@ -8286,8 +8578,8 @@ function formatTimeLeft(resetsAt) {
|
|
|
8286
8578
|
}
|
|
8287
8579
|
function colorizePercent(pct) {
|
|
8288
8580
|
const label2 = `${Math.round(pct)}%`;
|
|
8289
|
-
if (pct > 80) return
|
|
8290
|
-
if (pct > 40) return
|
|
8581
|
+
if (pct > 80) return chalk83.red(label2);
|
|
8582
|
+
if (pct > 40) return chalk83.yellow(label2);
|
|
8291
8583
|
return label2;
|
|
8292
8584
|
}
|
|
8293
8585
|
function formatLimit(limit, fallbackLabel) {
|
|
@@ -8322,27 +8614,27 @@ async function statusLine() {
|
|
|
8322
8614
|
// src/commands/sync.ts
|
|
8323
8615
|
import * as fs24 from "fs";
|
|
8324
8616
|
import * as os from "os";
|
|
8325
|
-
import * as
|
|
8617
|
+
import * as path42 from "path";
|
|
8326
8618
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
8327
8619
|
|
|
8328
8620
|
// src/commands/sync/syncClaudeMd.ts
|
|
8329
8621
|
import * as fs22 from "fs";
|
|
8330
|
-
import * as
|
|
8331
|
-
import
|
|
8622
|
+
import * as path40 from "path";
|
|
8623
|
+
import chalk84 from "chalk";
|
|
8332
8624
|
async function syncClaudeMd(claudeDir, targetBase) {
|
|
8333
|
-
const source =
|
|
8334
|
-
const target =
|
|
8625
|
+
const source = path40.join(claudeDir, "CLAUDE.md");
|
|
8626
|
+
const target = path40.join(targetBase, "CLAUDE.md");
|
|
8335
8627
|
const sourceContent = fs22.readFileSync(source, "utf-8");
|
|
8336
8628
|
if (fs22.existsSync(target)) {
|
|
8337
8629
|
const targetContent = fs22.readFileSync(target, "utf-8");
|
|
8338
8630
|
if (sourceContent !== targetContent) {
|
|
8339
8631
|
console.log(
|
|
8340
|
-
|
|
8632
|
+
chalk84.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
|
|
8341
8633
|
);
|
|
8342
8634
|
console.log();
|
|
8343
8635
|
printDiff(targetContent, sourceContent);
|
|
8344
8636
|
const confirm = await promptConfirm(
|
|
8345
|
-
|
|
8637
|
+
chalk84.red("Overwrite existing CLAUDE.md?"),
|
|
8346
8638
|
false
|
|
8347
8639
|
);
|
|
8348
8640
|
if (!confirm) {
|
|
@@ -8357,11 +8649,11 @@ async function syncClaudeMd(claudeDir, targetBase) {
|
|
|
8357
8649
|
|
|
8358
8650
|
// src/commands/sync/syncSettings.ts
|
|
8359
8651
|
import * as fs23 from "fs";
|
|
8360
|
-
import * as
|
|
8361
|
-
import
|
|
8652
|
+
import * as path41 from "path";
|
|
8653
|
+
import chalk85 from "chalk";
|
|
8362
8654
|
async function syncSettings(claudeDir, targetBase, options2) {
|
|
8363
|
-
const source =
|
|
8364
|
-
const target =
|
|
8655
|
+
const source = path41.join(claudeDir, "settings.json");
|
|
8656
|
+
const target = path41.join(targetBase, "settings.json");
|
|
8365
8657
|
const sourceContent = fs23.readFileSync(source, "utf-8");
|
|
8366
8658
|
const mergedContent = JSON.stringify(JSON.parse(sourceContent), null, " ");
|
|
8367
8659
|
if (fs23.existsSync(target)) {
|
|
@@ -8374,14 +8666,14 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
8374
8666
|
if (mergedContent !== normalizedTarget) {
|
|
8375
8667
|
if (!options2?.yes) {
|
|
8376
8668
|
console.log(
|
|
8377
|
-
|
|
8669
|
+
chalk85.yellow(
|
|
8378
8670
|
"\n\u26A0\uFE0F Warning: settings.json differs from existing file"
|
|
8379
8671
|
)
|
|
8380
8672
|
);
|
|
8381
8673
|
console.log();
|
|
8382
8674
|
printDiff(targetContent, mergedContent);
|
|
8383
8675
|
const confirm = await promptConfirm(
|
|
8384
|
-
|
|
8676
|
+
chalk85.red("Overwrite existing settings.json?"),
|
|
8385
8677
|
false
|
|
8386
8678
|
);
|
|
8387
8679
|
if (!confirm) {
|
|
@@ -8397,37 +8689,37 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
8397
8689
|
|
|
8398
8690
|
// src/commands/sync.ts
|
|
8399
8691
|
var __filename4 = fileURLToPath7(import.meta.url);
|
|
8400
|
-
var __dirname7 =
|
|
8692
|
+
var __dirname7 = path42.dirname(__filename4);
|
|
8401
8693
|
async function sync(options2) {
|
|
8402
|
-
const claudeDir =
|
|
8403
|
-
const targetBase =
|
|
8694
|
+
const claudeDir = path42.join(__dirname7, "..", "claude");
|
|
8695
|
+
const targetBase = path42.join(os.homedir(), ".claude");
|
|
8404
8696
|
syncCommands(claudeDir, targetBase);
|
|
8405
8697
|
await syncSettings(claudeDir, targetBase, { yes: options2?.yes });
|
|
8406
8698
|
await syncClaudeMd(claudeDir, targetBase);
|
|
8407
8699
|
}
|
|
8408
8700
|
function syncCommands(claudeDir, targetBase) {
|
|
8409
|
-
const sourceDir =
|
|
8410
|
-
const targetDir =
|
|
8701
|
+
const sourceDir = path42.join(claudeDir, "commands");
|
|
8702
|
+
const targetDir = path42.join(targetBase, "commands");
|
|
8411
8703
|
fs24.mkdirSync(targetDir, { recursive: true });
|
|
8412
8704
|
const files = fs24.readdirSync(sourceDir);
|
|
8413
8705
|
for (const file of files) {
|
|
8414
|
-
fs24.copyFileSync(
|
|
8706
|
+
fs24.copyFileSync(path42.join(sourceDir, file), path42.join(targetDir, file));
|
|
8415
8707
|
console.log(`Copied ${file} to ${targetDir}`);
|
|
8416
8708
|
}
|
|
8417
8709
|
console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
|
|
8418
8710
|
}
|
|
8419
8711
|
|
|
8420
8712
|
// src/commands/update.ts
|
|
8421
|
-
import { execSync as
|
|
8422
|
-
import * as
|
|
8713
|
+
import { execSync as execSync37 } from "child_process";
|
|
8714
|
+
import * as path43 from "path";
|
|
8423
8715
|
function isGlobalNpmInstall(dir) {
|
|
8424
8716
|
try {
|
|
8425
|
-
const resolved =
|
|
8426
|
-
if (resolved.split(
|
|
8717
|
+
const resolved = path43.resolve(dir);
|
|
8718
|
+
if (resolved.split(path43.sep).includes("node_modules")) {
|
|
8427
8719
|
return true;
|
|
8428
8720
|
}
|
|
8429
|
-
const globalPrefix =
|
|
8430
|
-
return resolved.toLowerCase().startsWith(
|
|
8721
|
+
const globalPrefix = execSync37("npm prefix -g", { stdio: "pipe" }).toString().trim();
|
|
8722
|
+
return resolved.toLowerCase().startsWith(path43.resolve(globalPrefix).toLowerCase());
|
|
8431
8723
|
} catch {
|
|
8432
8724
|
return false;
|
|
8433
8725
|
}
|
|
@@ -8437,18 +8729,18 @@ async function update() {
|
|
|
8437
8729
|
console.log(`Assist is installed at: ${installDir}`);
|
|
8438
8730
|
if (isGitRepo(installDir)) {
|
|
8439
8731
|
console.log("Detected git repo installation, pulling latest...");
|
|
8440
|
-
|
|
8732
|
+
execSync37("git pull", { cwd: installDir, stdio: "inherit" });
|
|
8441
8733
|
console.log("Installing dependencies...");
|
|
8442
|
-
|
|
8734
|
+
execSync37("npm i", { cwd: installDir, stdio: "inherit" });
|
|
8443
8735
|
console.log("Building...");
|
|
8444
|
-
|
|
8736
|
+
execSync37("npm run build", { cwd: installDir, stdio: "inherit" });
|
|
8445
8737
|
console.log("Syncing commands...");
|
|
8446
|
-
|
|
8738
|
+
execSync37("assist sync", { stdio: "inherit" });
|
|
8447
8739
|
} else if (isGlobalNpmInstall(installDir)) {
|
|
8448
8740
|
console.log("Detected global npm installation, updating...");
|
|
8449
|
-
|
|
8741
|
+
execSync37("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
|
|
8450
8742
|
console.log("Syncing commands...");
|
|
8451
|
-
|
|
8743
|
+
execSync37("assist sync", { stdio: "inherit" });
|
|
8452
8744
|
} else {
|
|
8453
8745
|
console.error(
|
|
8454
8746
|
"Could not determine installation method. Expected a git repo or global npm install."
|
|
@@ -8495,7 +8787,7 @@ registerRefactor(program);
|
|
|
8495
8787
|
registerDevlog(program);
|
|
8496
8788
|
registerDeploy(program);
|
|
8497
8789
|
registerComplexity(program);
|
|
8498
|
-
|
|
8790
|
+
registerDotnet(program);
|
|
8499
8791
|
registerNews(program);
|
|
8500
8792
|
registerRavendb(program);
|
|
8501
8793
|
registerTranscript(program);
|