@staff0rd/assist 0.94.0 → 0.95.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 +3 -0
- package/claude/commands/generate-cli-read-verbs.md +22 -0
- package/claude/settings.json +1949 -3
- package/dist/index.js +701 -297
- 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.95.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -129,6 +129,7 @@ var assistConfigSchema = z.strictObject({
|
|
|
129
129
|
}).optional(),
|
|
130
130
|
run: z.array(runConfigSchema).optional(),
|
|
131
131
|
transcript: transcriptConfigSchema.optional(),
|
|
132
|
+
cliReadVerbs: z.record(z.string(), z.array(z.string())).optional(),
|
|
132
133
|
voice: z.strictObject({
|
|
133
134
|
wakeWords: z.array(z.string()).default(DEFAULT_WAKE_WORDS),
|
|
134
135
|
mic: z.string().optional(),
|
|
@@ -1098,8 +1099,8 @@ function createSettingsJson() {
|
|
|
1098
1099
|
"source.organizeImports.biome": "explicit"
|
|
1099
1100
|
}
|
|
1100
1101
|
};
|
|
1101
|
-
const
|
|
1102
|
-
fs3.writeFileSync(
|
|
1102
|
+
const settingsPath2 = path9.join(process.cwd(), ".vscode", "settings.json");
|
|
1103
|
+
fs3.writeFileSync(settingsPath2, `${JSON.stringify(settings, null, " ")}
|
|
1103
1104
|
`);
|
|
1104
1105
|
console.log(chalk16.green("Created .vscode/settings.json"));
|
|
1105
1106
|
}
|
|
@@ -2509,16 +2510,422 @@ function registerBacklog(program2) {
|
|
|
2509
2510
|
backlogCommand.command("web").description("Start a web view of the backlog").option("-p, --port <number>", "Port to listen on", "3000").action(web);
|
|
2510
2511
|
}
|
|
2511
2512
|
|
|
2513
|
+
// src/commands/cliDiscover/index.ts
|
|
2514
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync4, readFileSync as readFileSync13, writeFileSync as writeFileSync13 } from "fs";
|
|
2515
|
+
import { homedir as homedir3 } from "os";
|
|
2516
|
+
import { join as join12 } from "path";
|
|
2517
|
+
|
|
2518
|
+
// src/commands/cliDiscover/assertCliExists.ts
|
|
2519
|
+
import { execSync as execSync13 } from "child_process";
|
|
2520
|
+
function assertCliExists(cli) {
|
|
2521
|
+
const binary = cli.split(/\s+/)[0];
|
|
2522
|
+
const opts = {
|
|
2523
|
+
encoding: "utf-8",
|
|
2524
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
2525
|
+
};
|
|
2526
|
+
try {
|
|
2527
|
+
execSync13(`command -v ${binary}`, opts);
|
|
2528
|
+
} catch {
|
|
2529
|
+
try {
|
|
2530
|
+
execSync13(`where ${binary}`, opts);
|
|
2531
|
+
} catch {
|
|
2532
|
+
console.error(`CLI "${cli}" not found in PATH`);
|
|
2533
|
+
process.exit(1);
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2538
|
+
// src/commands/cliDiscover/colorize.ts
|
|
2539
|
+
import chalk30 from "chalk";
|
|
2540
|
+
function colorize(plainOutput) {
|
|
2541
|
+
return plainOutput.split("\n").map((line) => {
|
|
2542
|
+
if (line.startsWith(" R ")) return chalk30.green(line);
|
|
2543
|
+
if (line.startsWith(" W ")) return chalk30.red(line);
|
|
2544
|
+
return line;
|
|
2545
|
+
}).join("\n");
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
// src/lib/isClaudeCode.ts
|
|
2549
|
+
function isClaudeCode() {
|
|
2550
|
+
return process.env.CLAUDECODE !== void 0;
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2553
|
+
// src/commands/cliDiscover/mapAsync.ts
|
|
2554
|
+
async function mapAsync(items, concurrency, fn) {
|
|
2555
|
+
const results = new Array(items.length);
|
|
2556
|
+
let next2 = 0;
|
|
2557
|
+
async function worker() {
|
|
2558
|
+
while (next2 < items.length) {
|
|
2559
|
+
const idx = next2++;
|
|
2560
|
+
results[idx] = await fn(items[idx]);
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
const workers = Array.from(
|
|
2564
|
+
{ length: Math.min(concurrency, items.length) },
|
|
2565
|
+
() => worker()
|
|
2566
|
+
);
|
|
2567
|
+
await Promise.all(workers);
|
|
2568
|
+
return results;
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
// src/commands/cliDiscover/parseCommands.ts
|
|
2572
|
+
var COMMAND_SECTION_RE = /^((?:core |general |available |additional |other |management |targeted |alias |github actions )?(?:commands|subgroups)):?$/i;
|
|
2573
|
+
function isSkippable(name) {
|
|
2574
|
+
return name.startsWith("-") || name.startsWith("<") || name.startsWith("[");
|
|
2575
|
+
}
|
|
2576
|
+
function parseCommandLine(trimmed) {
|
|
2577
|
+
const azMatch = trimmed.match(/^(\S+)\s+(?:\[.*?]\s+)?:\s*(.+)/);
|
|
2578
|
+
if (azMatch && !isSkippable(azMatch[1])) {
|
|
2579
|
+
return { name: azMatch[1], description: azMatch[2].trim() };
|
|
2580
|
+
}
|
|
2581
|
+
const colonMatch = trimmed.match(/^(\S+?):\s{2,}(.+)/);
|
|
2582
|
+
if (colonMatch && !isSkippable(colonMatch[1])) {
|
|
2583
|
+
return { name: colonMatch[1], description: colonMatch[2].trim() };
|
|
2584
|
+
}
|
|
2585
|
+
const spaceMatch = trimmed.match(/^(\S+)(?:,\s*\S+)?\s{2,}(.+)/);
|
|
2586
|
+
if (spaceMatch && !isSkippable(spaceMatch[1])) {
|
|
2587
|
+
return { name: spaceMatch[1], description: spaceMatch[2].trim() };
|
|
2588
|
+
}
|
|
2589
|
+
if (/^\S+$/.test(trimmed) && !isSkippable(trimmed)) {
|
|
2590
|
+
return { name: trimmed, description: "" };
|
|
2591
|
+
}
|
|
2592
|
+
return void 0;
|
|
2593
|
+
}
|
|
2594
|
+
function parseCommands(helpText) {
|
|
2595
|
+
const commands = [];
|
|
2596
|
+
let inCommandSection = false;
|
|
2597
|
+
for (const line of helpText.split("\n")) {
|
|
2598
|
+
const trimmed = line.trim();
|
|
2599
|
+
if (COMMAND_SECTION_RE.test(trimmed)) {
|
|
2600
|
+
inCommandSection = true;
|
|
2601
|
+
continue;
|
|
2602
|
+
}
|
|
2603
|
+
if (inCommandSection && trimmed && !line.startsWith(" ") && !line.startsWith(" ")) {
|
|
2604
|
+
inCommandSection = false;
|
|
2605
|
+
continue;
|
|
2606
|
+
}
|
|
2607
|
+
if (!inCommandSection || !trimmed) continue;
|
|
2608
|
+
if (trimmed.startsWith("-") || trimmed.startsWith("=")) continue;
|
|
2609
|
+
const parsed = parseCommandLine(trimmed);
|
|
2610
|
+
if (parsed) commands.push(parsed);
|
|
2611
|
+
}
|
|
2612
|
+
return commands;
|
|
2613
|
+
}
|
|
2614
|
+
var COMMAND_SECTION_MULTILINE_RE = new RegExp(
|
|
2615
|
+
COMMAND_SECTION_RE.source,
|
|
2616
|
+
"im"
|
|
2617
|
+
);
|
|
2618
|
+
function hasSubcommands(helpText) {
|
|
2619
|
+
return COMMAND_SECTION_MULTILINE_RE.test(helpText);
|
|
2620
|
+
}
|
|
2621
|
+
|
|
2622
|
+
// src/commands/cliDiscover/runHelp.ts
|
|
2623
|
+
import { exec as exec2 } from "child_process";
|
|
2624
|
+
function runHelp(args) {
|
|
2625
|
+
return new Promise((resolve3) => {
|
|
2626
|
+
exec2(
|
|
2627
|
+
`${args.join(" ")} --help`,
|
|
2628
|
+
{ encoding: "utf-8", timeout: 3e4 },
|
|
2629
|
+
(_err, stdout, stderr) => {
|
|
2630
|
+
resolve3(stdout || stderr || "");
|
|
2631
|
+
}
|
|
2632
|
+
);
|
|
2633
|
+
});
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
// src/commands/cliDiscover/discoverAll.ts
|
|
2637
|
+
var SAFETY_DEPTH = 10;
|
|
2638
|
+
var CONCURRENCY = 8;
|
|
2639
|
+
var interactive = !isClaudeCode();
|
|
2640
|
+
function showProgress(p, label2) {
|
|
2641
|
+
if (!interactive) return;
|
|
2642
|
+
const pct = Math.round(p.done / p.total * 100);
|
|
2643
|
+
process.stderr.write(`\r\x1B[K[${pct}%] Scanning ${label2}...`);
|
|
2644
|
+
}
|
|
2645
|
+
async function resolveCommand(cli, path31, description, depth, p) {
|
|
2646
|
+
showProgress(p, path31.join(" "));
|
|
2647
|
+
const subHelp = await runHelp([cli, ...path31]);
|
|
2648
|
+
if (!subHelp || !hasSubcommands(subHelp)) {
|
|
2649
|
+
return [{ path: path31, description }];
|
|
2650
|
+
}
|
|
2651
|
+
const children = await discoverAt(cli, path31, depth + 1, p);
|
|
2652
|
+
return children.length > 0 ? children : [{ path: path31, description }];
|
|
2653
|
+
}
|
|
2654
|
+
async function discoverAt(cli, parentPath, depth, p) {
|
|
2655
|
+
if (depth > SAFETY_DEPTH) return [];
|
|
2656
|
+
const helpText = await runHelp([cli, ...parentPath]);
|
|
2657
|
+
if (!helpText) return [];
|
|
2658
|
+
const cmds = parseCommands(helpText);
|
|
2659
|
+
const results = await mapAsync(
|
|
2660
|
+
cmds,
|
|
2661
|
+
CONCURRENCY,
|
|
2662
|
+
(cmd) => resolveCommand(cli, [...parentPath, cmd.name], cmd.description, depth, p)
|
|
2663
|
+
);
|
|
2664
|
+
return results.flat();
|
|
2665
|
+
}
|
|
2666
|
+
async function discoverAll(cli) {
|
|
2667
|
+
const topLevel = parseCommands(await runHelp([cli]));
|
|
2668
|
+
const p = { done: 0, total: topLevel.length };
|
|
2669
|
+
const results = await mapAsync(topLevel, CONCURRENCY, async (cmd) => {
|
|
2670
|
+
showProgress(p, cmd.name);
|
|
2671
|
+
const resolved = await resolveCommand(
|
|
2672
|
+
cli,
|
|
2673
|
+
[cmd.name],
|
|
2674
|
+
cmd.description,
|
|
2675
|
+
1,
|
|
2676
|
+
p
|
|
2677
|
+
);
|
|
2678
|
+
p.done++;
|
|
2679
|
+
showProgress(p, cmd.name);
|
|
2680
|
+
return resolved;
|
|
2681
|
+
});
|
|
2682
|
+
if (interactive) process.stderr.write("\r\x1B[K");
|
|
2683
|
+
return results.flat();
|
|
2684
|
+
}
|
|
2685
|
+
|
|
2686
|
+
// src/commands/cliDiscover/classifyVerb.ts
|
|
2687
|
+
var READ_VERBS = /* @__PURE__ */ new Set([
|
|
2688
|
+
"list",
|
|
2689
|
+
"show",
|
|
2690
|
+
"view",
|
|
2691
|
+
"export",
|
|
2692
|
+
"get",
|
|
2693
|
+
"diff",
|
|
2694
|
+
"status",
|
|
2695
|
+
"search",
|
|
2696
|
+
"checks",
|
|
2697
|
+
"describe",
|
|
2698
|
+
"inspect",
|
|
2699
|
+
"logs",
|
|
2700
|
+
"cat",
|
|
2701
|
+
"top",
|
|
2702
|
+
"explain",
|
|
2703
|
+
"exists",
|
|
2704
|
+
"browse",
|
|
2705
|
+
"watch"
|
|
2706
|
+
]);
|
|
2707
|
+
var WRITE_VERBS = /* @__PURE__ */ new Set([
|
|
2708
|
+
"create",
|
|
2709
|
+
"delete",
|
|
2710
|
+
"import",
|
|
2711
|
+
"set",
|
|
2712
|
+
"update",
|
|
2713
|
+
"merge",
|
|
2714
|
+
"close",
|
|
2715
|
+
"reopen",
|
|
2716
|
+
"edit",
|
|
2717
|
+
"apply",
|
|
2718
|
+
"patch",
|
|
2719
|
+
"drain",
|
|
2720
|
+
"cordon",
|
|
2721
|
+
"taint",
|
|
2722
|
+
"push",
|
|
2723
|
+
"deploy",
|
|
2724
|
+
"add",
|
|
2725
|
+
"remove",
|
|
2726
|
+
"assign",
|
|
2727
|
+
"unassign",
|
|
2728
|
+
"lock",
|
|
2729
|
+
"unlock",
|
|
2730
|
+
"start",
|
|
2731
|
+
"stop",
|
|
2732
|
+
"restart",
|
|
2733
|
+
"enable",
|
|
2734
|
+
"disable",
|
|
2735
|
+
"revoke",
|
|
2736
|
+
"rotate"
|
|
2737
|
+
]);
|
|
2738
|
+
function classifyVerb(verb) {
|
|
2739
|
+
if (READ_VERBS.has(verb)) return "r";
|
|
2740
|
+
if (WRITE_VERBS.has(verb)) return "w";
|
|
2741
|
+
return "?";
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2744
|
+
// src/commands/cliDiscover/formatHuman.ts
|
|
2745
|
+
function prefix(kind) {
|
|
2746
|
+
if (kind === "r") return " R ";
|
|
2747
|
+
if (kind === "w") return " W ";
|
|
2748
|
+
return " ? ";
|
|
2749
|
+
}
|
|
2750
|
+
function formatHuman(cli, commands) {
|
|
2751
|
+
const sorted = [...commands].sort(
|
|
2752
|
+
(a, b) => a.path.join(" ").localeCompare(b.path.join(" "))
|
|
2753
|
+
);
|
|
2754
|
+
const lines = [`Discovered ${commands.length} commands for "${cli}":
|
|
2755
|
+
`];
|
|
2756
|
+
for (const cmd of sorted) {
|
|
2757
|
+
const verb = cmd.path[cmd.path.length - 1];
|
|
2758
|
+
const full = `${cli} ${cmd.path.join(" ")}`;
|
|
2759
|
+
const text = cmd.description ? `${full} \u2014 ${cmd.description}` : full;
|
|
2760
|
+
lines.push(`${prefix(classifyVerb(verb))}${text}`);
|
|
2761
|
+
}
|
|
2762
|
+
return lines.join("\n");
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
// src/commands/cliDiscover/parseCached.ts
|
|
2766
|
+
function parseCached(cli, cached) {
|
|
2767
|
+
const prefix2 = `${cli} `;
|
|
2768
|
+
const commands = [];
|
|
2769
|
+
for (const line of cached.split("\n")) {
|
|
2770
|
+
const trimmed = line.replace(/^ [RW?] {2}/, "").trim();
|
|
2771
|
+
if (!trimmed.startsWith(prefix2)) continue;
|
|
2772
|
+
const rest = trimmed.slice(prefix2.length);
|
|
2773
|
+
const dashIdx = rest.indexOf(" \u2014 ");
|
|
2774
|
+
const pathStr = dashIdx >= 0 ? rest.slice(0, dashIdx) : rest;
|
|
2775
|
+
const description = dashIdx >= 0 ? rest.slice(dashIdx + 3) : "";
|
|
2776
|
+
commands.push({ path: pathStr.split(" "), description });
|
|
2777
|
+
}
|
|
2778
|
+
return commands;
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
// src/commands/cliDiscover/updateSettings.ts
|
|
2782
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12, writeFileSync as writeFileSync12 } from "fs";
|
|
2783
|
+
import { join as join11 } from "path";
|
|
2784
|
+
function settingsPath() {
|
|
2785
|
+
return join11(process.cwd(), "claude", "settings.json");
|
|
2786
|
+
}
|
|
2787
|
+
function updateSettings(cli, commands) {
|
|
2788
|
+
const path31 = settingsPath();
|
|
2789
|
+
if (!existsSync15(path31)) return;
|
|
2790
|
+
const settings = JSON.parse(readFileSync12(path31, "utf-8"));
|
|
2791
|
+
const allow = settings.permissions?.allow ?? [];
|
|
2792
|
+
const readCommands = commands.filter(
|
|
2793
|
+
(cmd) => classifyVerb(cmd.path[cmd.path.length - 1]) === "r"
|
|
2794
|
+
);
|
|
2795
|
+
const newEntries = readCommands.map(
|
|
2796
|
+
(cmd) => `Bash(${cli} ${cmd.path.join(" ")}:*)`
|
|
2797
|
+
);
|
|
2798
|
+
const existing = new Set(allow);
|
|
2799
|
+
const added = newEntries.filter((e) => !existing.has(e));
|
|
2800
|
+
if (added.length === 0) return;
|
|
2801
|
+
settings.permissions = settings.permissions ?? {};
|
|
2802
|
+
settings.permissions.allow = [...allow, ...added];
|
|
2803
|
+
writeFileSync12(path31, `${JSON.stringify(settings, null, " ")}
|
|
2804
|
+
`);
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
// src/commands/cliDiscover/index.ts
|
|
2808
|
+
function logPath(cli) {
|
|
2809
|
+
const safeName = cli.replace(/\s+/g, "-");
|
|
2810
|
+
return join12(homedir3(), ".assist", `cli-discover-${safeName}.log`);
|
|
2811
|
+
}
|
|
2812
|
+
function readCache(cli) {
|
|
2813
|
+
const path31 = logPath(cli);
|
|
2814
|
+
if (!existsSync16(path31)) return void 0;
|
|
2815
|
+
return readFileSync13(path31, "utf-8");
|
|
2816
|
+
}
|
|
2817
|
+
function writeCache(cli, output) {
|
|
2818
|
+
const dir = join12(homedir3(), ".assist");
|
|
2819
|
+
mkdirSync4(dir, { recursive: true });
|
|
2820
|
+
writeFileSync13(logPath(cli), output);
|
|
2821
|
+
}
|
|
2822
|
+
async function cliDiscover(cli, options2 = { noCache: false }) {
|
|
2823
|
+
if (!cli) {
|
|
2824
|
+
console.error("Usage: assist cli-discover <cli>");
|
|
2825
|
+
process.exit(1);
|
|
2826
|
+
}
|
|
2827
|
+
if (!options2.noCache) {
|
|
2828
|
+
const cached = readCache(cli);
|
|
2829
|
+
if (cached) {
|
|
2830
|
+
console.log(colorize(cached));
|
|
2831
|
+
updateSettings(cli, parseCached(cli, cached));
|
|
2832
|
+
return;
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
assertCliExists(cli);
|
|
2836
|
+
const commands = await discoverAll(cli);
|
|
2837
|
+
const output = formatHuman(cli, commands);
|
|
2838
|
+
console.log(colorize(output));
|
|
2839
|
+
writeCache(cli, output);
|
|
2840
|
+
updateSettings(cli, commands);
|
|
2841
|
+
}
|
|
2842
|
+
|
|
2843
|
+
// src/commands/registerCliDiscover.ts
|
|
2844
|
+
function registerCliDiscover(program2) {
|
|
2845
|
+
program2.command("cli-discover").description("Discover a CLI's command tree via recursive --help parsing").argument(
|
|
2846
|
+
"<cli...>",
|
|
2847
|
+
"CLI binary and optional subcommand (e.g. gh, az, acli jira)"
|
|
2848
|
+
).option("--no-cache", "Force fresh discovery, ignoring cached results").action((cli, options2) => {
|
|
2849
|
+
cliDiscover(cli.join(" "), { noCache: !options2.cache });
|
|
2850
|
+
});
|
|
2851
|
+
}
|
|
2852
|
+
|
|
2853
|
+
// src/commands/cliHook/extractVerb.ts
|
|
2854
|
+
var SHELL_OPERATORS = /* @__PURE__ */ new Set(["|", ">", ">>", ";", "&&", "||"]);
|
|
2855
|
+
function isShellBreak(token) {
|
|
2856
|
+
return token.startsWith("-") || SHELL_OPERATORS.has(token);
|
|
2857
|
+
}
|
|
2858
|
+
function looksLikeArgument(token) {
|
|
2859
|
+
return /[/=.]/.test(token) || /^\d+$/.test(token);
|
|
2860
|
+
}
|
|
2861
|
+
function extractVerb(command, readVerbs) {
|
|
2862
|
+
const tokens = command.split(/\s+/);
|
|
2863
|
+
for (let i = 1; i < tokens.length; i++) {
|
|
2864
|
+
if (isShellBreak(tokens[i])) break;
|
|
2865
|
+
if (readVerbs.includes(tokens[i])) return tokens[i];
|
|
2866
|
+
}
|
|
2867
|
+
for (let i = tokens.length - 1; i >= 1; i--) {
|
|
2868
|
+
const token = tokens[i];
|
|
2869
|
+
if (isShellBreak(token) || looksLikeArgument(token)) continue;
|
|
2870
|
+
return token;
|
|
2871
|
+
}
|
|
2872
|
+
return void 0;
|
|
2873
|
+
}
|
|
2874
|
+
|
|
2875
|
+
// src/commands/cliHook/index.ts
|
|
2876
|
+
async function cliHook() {
|
|
2877
|
+
const inputData = await readStdin();
|
|
2878
|
+
let data;
|
|
2879
|
+
try {
|
|
2880
|
+
data = JSON.parse(inputData);
|
|
2881
|
+
} catch {
|
|
2882
|
+
return;
|
|
2883
|
+
}
|
|
2884
|
+
if (data.tool_name !== "Bash" || !data.tool_input?.command) {
|
|
2885
|
+
return;
|
|
2886
|
+
}
|
|
2887
|
+
const command = data.tool_input.command.trim();
|
|
2888
|
+
const config = loadConfig();
|
|
2889
|
+
const cliReadVerbs = config.cliReadVerbs;
|
|
2890
|
+
if (!cliReadVerbs) return;
|
|
2891
|
+
const cliKeys = Object.keys(cliReadVerbs).sort((a, b) => b.length - a.length);
|
|
2892
|
+
const cli = cliKeys.find(
|
|
2893
|
+
(key) => command === key || command.startsWith(`${key} `)
|
|
2894
|
+
);
|
|
2895
|
+
if (!cli) return;
|
|
2896
|
+
const readVerbs = cliReadVerbs[cli];
|
|
2897
|
+
if (!readVerbs || readVerbs.length === 0) return;
|
|
2898
|
+
const verb = extractVerb(command, readVerbs);
|
|
2899
|
+
if (verb && readVerbs.includes(verb)) {
|
|
2900
|
+
console.log(
|
|
2901
|
+
JSON.stringify({
|
|
2902
|
+
hookSpecificOutput: {
|
|
2903
|
+
hookEventName: "PreToolUse",
|
|
2904
|
+
permissionDecision: "allow",
|
|
2905
|
+
permissionDecisionReason: `Read-only ${cli} command: ${verb}`
|
|
2906
|
+
}
|
|
2907
|
+
})
|
|
2908
|
+
);
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
|
|
2912
|
+
// src/commands/registerCliHook.ts
|
|
2913
|
+
function registerCliHook(program2) {
|
|
2914
|
+
program2.command("cli-hook").description("PreToolUse hook for auto-approving read-only CLI commands").action(() => {
|
|
2915
|
+
cliHook();
|
|
2916
|
+
});
|
|
2917
|
+
}
|
|
2918
|
+
|
|
2512
2919
|
// src/commands/complexity/analyze.ts
|
|
2513
|
-
import
|
|
2920
|
+
import chalk36 from "chalk";
|
|
2514
2921
|
|
|
2515
2922
|
// src/commands/complexity/cyclomatic.ts
|
|
2516
|
-
import
|
|
2923
|
+
import chalk32 from "chalk";
|
|
2517
2924
|
|
|
2518
2925
|
// src/commands/complexity/shared/index.ts
|
|
2519
2926
|
import fs11 from "fs";
|
|
2520
2927
|
import path16 from "path";
|
|
2521
|
-
import
|
|
2928
|
+
import chalk31 from "chalk";
|
|
2522
2929
|
import ts5 from "typescript";
|
|
2523
2930
|
|
|
2524
2931
|
// src/commands/complexity/findSourceFiles.ts
|
|
@@ -2595,8 +3002,8 @@ function getNodeName(node) {
|
|
|
2595
3002
|
return getIdentifierText(node.name);
|
|
2596
3003
|
if (ts.isArrowFunction(node)) return getArrowFunctionName(node);
|
|
2597
3004
|
if (ts.isGetAccessor(node) || ts.isSetAccessor(node)) {
|
|
2598
|
-
const
|
|
2599
|
-
return `${
|
|
3005
|
+
const prefix2 = ts.isGetAccessor(node) ? "get " : "set ";
|
|
3006
|
+
return `${prefix2}${getIdentifierText(node.name)}`;
|
|
2600
3007
|
}
|
|
2601
3008
|
if (ts.isConstructorDeclaration(node)) return "constructor";
|
|
2602
3009
|
return "<unknown>";
|
|
@@ -2764,7 +3171,7 @@ function createSourceFromFile(filePath) {
|
|
|
2764
3171
|
function withSourceFiles(pattern2, callback) {
|
|
2765
3172
|
const files = findSourceFiles2(pattern2);
|
|
2766
3173
|
if (files.length === 0) {
|
|
2767
|
-
console.log(
|
|
3174
|
+
console.log(chalk31.yellow("No files found matching pattern"));
|
|
2768
3175
|
return void 0;
|
|
2769
3176
|
}
|
|
2770
3177
|
return callback(files);
|
|
@@ -2797,11 +3204,11 @@ async function cyclomatic(pattern2 = "**/*.ts", options2 = {}) {
|
|
|
2797
3204
|
results.sort((a, b) => b.complexity - a.complexity);
|
|
2798
3205
|
for (const { file, name, complexity } of results) {
|
|
2799
3206
|
const exceedsThreshold = options2.threshold !== void 0 && complexity > options2.threshold;
|
|
2800
|
-
const color = exceedsThreshold ?
|
|
2801
|
-
console.log(`${color(`${file}:${name}`)} \u2192 ${
|
|
3207
|
+
const color = exceedsThreshold ? chalk32.red : chalk32.white;
|
|
3208
|
+
console.log(`${color(`${file}:${name}`)} \u2192 ${chalk32.cyan(complexity)}`);
|
|
2802
3209
|
}
|
|
2803
3210
|
console.log(
|
|
2804
|
-
|
|
3211
|
+
chalk32.dim(
|
|
2805
3212
|
`
|
|
2806
3213
|
Analyzed ${results.length} functions across ${files.length} files`
|
|
2807
3214
|
)
|
|
@@ -2813,7 +3220,7 @@ Analyzed ${results.length} functions across ${files.length} files`
|
|
|
2813
3220
|
}
|
|
2814
3221
|
|
|
2815
3222
|
// src/commands/complexity/halstead.ts
|
|
2816
|
-
import
|
|
3223
|
+
import chalk33 from "chalk";
|
|
2817
3224
|
async function halstead(pattern2 = "**/*.ts", options2 = {}) {
|
|
2818
3225
|
withSourceFiles(pattern2, (files) => {
|
|
2819
3226
|
const results = [];
|
|
@@ -2828,13 +3235,13 @@ async function halstead(pattern2 = "**/*.ts", options2 = {}) {
|
|
|
2828
3235
|
results.sort((a, b) => b.metrics.effort - a.metrics.effort);
|
|
2829
3236
|
for (const { file, name, metrics } of results) {
|
|
2830
3237
|
const exceedsThreshold = options2.threshold !== void 0 && metrics.volume > options2.threshold;
|
|
2831
|
-
const color = exceedsThreshold ?
|
|
3238
|
+
const color = exceedsThreshold ? chalk33.red : chalk33.white;
|
|
2832
3239
|
console.log(
|
|
2833
|
-
`${color(`${file}:${name}`)} \u2192 volume: ${
|
|
3240
|
+
`${color(`${file}:${name}`)} \u2192 volume: ${chalk33.cyan(metrics.volume.toFixed(1))}, difficulty: ${chalk33.yellow(metrics.difficulty.toFixed(1))}, effort: ${chalk33.magenta(metrics.effort.toFixed(1))}`
|
|
2834
3241
|
);
|
|
2835
3242
|
}
|
|
2836
3243
|
console.log(
|
|
2837
|
-
|
|
3244
|
+
chalk33.dim(
|
|
2838
3245
|
`
|
|
2839
3246
|
Analyzed ${results.length} functions across ${files.length} files`
|
|
2840
3247
|
)
|
|
@@ -2849,28 +3256,28 @@ Analyzed ${results.length} functions across ${files.length} files`
|
|
|
2849
3256
|
import fs12 from "fs";
|
|
2850
3257
|
|
|
2851
3258
|
// src/commands/complexity/maintainability/displayMaintainabilityResults.ts
|
|
2852
|
-
import
|
|
3259
|
+
import chalk34 from "chalk";
|
|
2853
3260
|
function displayMaintainabilityResults(results, threshold) {
|
|
2854
3261
|
const filtered = threshold !== void 0 ? results.filter((r) => r.minMaintainability < threshold) : results;
|
|
2855
3262
|
if (threshold !== void 0 && filtered.length === 0) {
|
|
2856
|
-
console.log(
|
|
3263
|
+
console.log(chalk34.green("All files pass maintainability threshold"));
|
|
2857
3264
|
} else {
|
|
2858
3265
|
for (const { file, avgMaintainability, minMaintainability } of filtered) {
|
|
2859
|
-
const color = threshold !== void 0 ?
|
|
3266
|
+
const color = threshold !== void 0 ? chalk34.red : chalk34.white;
|
|
2860
3267
|
console.log(
|
|
2861
|
-
`${color(file)} \u2192 avg: ${
|
|
3268
|
+
`${color(file)} \u2192 avg: ${chalk34.cyan(avgMaintainability.toFixed(1))}, min: ${chalk34.yellow(minMaintainability.toFixed(1))}`
|
|
2862
3269
|
);
|
|
2863
3270
|
}
|
|
2864
3271
|
}
|
|
2865
|
-
console.log(
|
|
3272
|
+
console.log(chalk34.dim(`
|
|
2866
3273
|
Analyzed ${results.length} files`));
|
|
2867
3274
|
if (filtered.length > 0 && threshold !== void 0) {
|
|
2868
3275
|
console.error(
|
|
2869
|
-
|
|
3276
|
+
chalk34.red(
|
|
2870
3277
|
`
|
|
2871
3278
|
Fail: ${filtered.length} file(s) below threshold ${threshold}. Maintainability index (0\u2013100) is derived from Halstead volume, cyclomatic complexity, and lines of code.
|
|
2872
3279
|
|
|
2873
|
-
\u26A0\uFE0F ${
|
|
3280
|
+
\u26A0\uFE0F ${chalk34.bold("Diagnose and fix one file at a time")} \u2014 do not investigate or fix multiple files in parallel. Run 'assist complexity <file>' to see all metrics. For larger files, start by extracting responsibilities into smaller files.`
|
|
2874
3281
|
)
|
|
2875
3282
|
);
|
|
2876
3283
|
process.exit(1);
|
|
@@ -2927,7 +3334,7 @@ async function maintainability(pattern2 = "**/*.ts", options2 = {}) {
|
|
|
2927
3334
|
|
|
2928
3335
|
// src/commands/complexity/sloc.ts
|
|
2929
3336
|
import fs13 from "fs";
|
|
2930
|
-
import
|
|
3337
|
+
import chalk35 from "chalk";
|
|
2931
3338
|
async function sloc(pattern2 = "**/*.ts", options2 = {}) {
|
|
2932
3339
|
withSourceFiles(pattern2, (files) => {
|
|
2933
3340
|
const results = [];
|
|
@@ -2943,12 +3350,12 @@ async function sloc(pattern2 = "**/*.ts", options2 = {}) {
|
|
|
2943
3350
|
results.sort((a, b) => b.lines - a.lines);
|
|
2944
3351
|
for (const { file, lines } of results) {
|
|
2945
3352
|
const exceedsThreshold = options2.threshold !== void 0 && lines > options2.threshold;
|
|
2946
|
-
const color = exceedsThreshold ?
|
|
2947
|
-
console.log(`${color(file)} \u2192 ${
|
|
3353
|
+
const color = exceedsThreshold ? chalk35.red : chalk35.white;
|
|
3354
|
+
console.log(`${color(file)} \u2192 ${chalk35.cyan(lines)} lines`);
|
|
2948
3355
|
}
|
|
2949
3356
|
const total = results.reduce((sum, r) => sum + r.lines, 0);
|
|
2950
3357
|
console.log(
|
|
2951
|
-
|
|
3358
|
+
chalk35.dim(`
|
|
2952
3359
|
Total: ${total} lines across ${files.length} files`)
|
|
2953
3360
|
);
|
|
2954
3361
|
if (hasViolation) {
|
|
@@ -2962,21 +3369,21 @@ async function analyze(pattern2) {
|
|
|
2962
3369
|
const searchPattern = pattern2.includes("*") || pattern2.includes("/") ? pattern2 : `**/${pattern2}`;
|
|
2963
3370
|
const files = findSourceFiles2(searchPattern);
|
|
2964
3371
|
if (files.length === 0) {
|
|
2965
|
-
console.log(
|
|
3372
|
+
console.log(chalk36.yellow("No files found matching pattern"));
|
|
2966
3373
|
return;
|
|
2967
3374
|
}
|
|
2968
3375
|
if (files.length === 1) {
|
|
2969
3376
|
const file = files[0];
|
|
2970
|
-
console.log(
|
|
3377
|
+
console.log(chalk36.bold.underline("SLOC"));
|
|
2971
3378
|
await sloc(file);
|
|
2972
3379
|
console.log();
|
|
2973
|
-
console.log(
|
|
3380
|
+
console.log(chalk36.bold.underline("Cyclomatic Complexity"));
|
|
2974
3381
|
await cyclomatic(file);
|
|
2975
3382
|
console.log();
|
|
2976
|
-
console.log(
|
|
3383
|
+
console.log(chalk36.bold.underline("Halstead Metrics"));
|
|
2977
3384
|
await halstead(file);
|
|
2978
3385
|
console.log();
|
|
2979
|
-
console.log(
|
|
3386
|
+
console.log(chalk36.bold.underline("Maintainability Index"));
|
|
2980
3387
|
await maintainability(file);
|
|
2981
3388
|
return;
|
|
2982
3389
|
}
|
|
@@ -3003,8 +3410,8 @@ function registerComplexity(program2) {
|
|
|
3003
3410
|
}
|
|
3004
3411
|
|
|
3005
3412
|
// src/commands/deploy/redirect.ts
|
|
3006
|
-
import { existsSync as
|
|
3007
|
-
import
|
|
3413
|
+
import { existsSync as existsSync17, readFileSync as readFileSync14, writeFileSync as writeFileSync14 } from "fs";
|
|
3414
|
+
import chalk37 from "chalk";
|
|
3008
3415
|
var TRAILING_SLASH_SCRIPT = ` <script>
|
|
3009
3416
|
if (!window.location.pathname.endsWith('/')) {
|
|
3010
3417
|
window.location.href = \`\${window.location.pathname}/\${window.location.search}\${window.location.hash}\`;
|
|
@@ -3012,23 +3419,23 @@ var TRAILING_SLASH_SCRIPT = ` <script>
|
|
|
3012
3419
|
</script>`;
|
|
3013
3420
|
function redirect() {
|
|
3014
3421
|
const indexPath = "index.html";
|
|
3015
|
-
if (!
|
|
3016
|
-
console.log(
|
|
3422
|
+
if (!existsSync17(indexPath)) {
|
|
3423
|
+
console.log(chalk37.yellow("No index.html found"));
|
|
3017
3424
|
return;
|
|
3018
3425
|
}
|
|
3019
|
-
const content =
|
|
3426
|
+
const content = readFileSync14(indexPath, "utf-8");
|
|
3020
3427
|
if (content.includes("window.location.pathname.endsWith('/')")) {
|
|
3021
|
-
console.log(
|
|
3428
|
+
console.log(chalk37.dim("Trailing slash script already present"));
|
|
3022
3429
|
return;
|
|
3023
3430
|
}
|
|
3024
3431
|
const headCloseIndex = content.indexOf("</head>");
|
|
3025
3432
|
if (headCloseIndex === -1) {
|
|
3026
|
-
console.log(
|
|
3433
|
+
console.log(chalk37.red("Could not find </head> tag in index.html"));
|
|
3027
3434
|
return;
|
|
3028
3435
|
}
|
|
3029
3436
|
const newContent = content.slice(0, headCloseIndex) + TRAILING_SLASH_SCRIPT + "\n " + content.slice(headCloseIndex);
|
|
3030
|
-
|
|
3031
|
-
console.log(
|
|
3437
|
+
writeFileSync14(indexPath, newContent);
|
|
3438
|
+
console.log(chalk37.green("Added trailing slash redirect to index.html"));
|
|
3032
3439
|
}
|
|
3033
3440
|
|
|
3034
3441
|
// src/commands/registerDeploy.ts
|
|
@@ -3039,24 +3446,24 @@ function registerDeploy(program2) {
|
|
|
3039
3446
|
}
|
|
3040
3447
|
|
|
3041
3448
|
// src/commands/devlog/list/index.ts
|
|
3042
|
-
import { execSync as
|
|
3449
|
+
import { execSync as execSync15 } from "child_process";
|
|
3043
3450
|
import { basename as basename3 } from "path";
|
|
3044
3451
|
|
|
3045
3452
|
// src/commands/devlog/shared.ts
|
|
3046
|
-
import { execSync as
|
|
3047
|
-
import
|
|
3453
|
+
import { execSync as execSync14 } from "child_process";
|
|
3454
|
+
import chalk38 from "chalk";
|
|
3048
3455
|
|
|
3049
3456
|
// src/commands/devlog/loadDevlogEntries.ts
|
|
3050
|
-
import { readdirSync, readFileSync as
|
|
3051
|
-
import { homedir as
|
|
3052
|
-
import { join as
|
|
3053
|
-
var DEVLOG_DIR =
|
|
3457
|
+
import { readdirSync, readFileSync as readFileSync15 } from "fs";
|
|
3458
|
+
import { homedir as homedir4 } from "os";
|
|
3459
|
+
import { join as join13 } from "path";
|
|
3460
|
+
var DEVLOG_DIR = join13(homedir4(), "git/blog/src/content/devlog");
|
|
3054
3461
|
function loadDevlogEntries(repoName) {
|
|
3055
3462
|
const entries = /* @__PURE__ */ new Map();
|
|
3056
3463
|
try {
|
|
3057
3464
|
const files = readdirSync(DEVLOG_DIR).filter((f) => f.endsWith(".md"));
|
|
3058
3465
|
for (const file of files) {
|
|
3059
|
-
const content =
|
|
3466
|
+
const content = readFileSync15(join13(DEVLOG_DIR, file), "utf-8");
|
|
3060
3467
|
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
3061
3468
|
if (frontmatterMatch) {
|
|
3062
3469
|
const frontmatter = frontmatterMatch[1];
|
|
@@ -3087,7 +3494,7 @@ function loadDevlogEntries(repoName) {
|
|
|
3087
3494
|
// src/commands/devlog/shared.ts
|
|
3088
3495
|
function getCommitFiles(hash) {
|
|
3089
3496
|
try {
|
|
3090
|
-
const output =
|
|
3497
|
+
const output = execSync14(`git show --name-only --format="" ${hash}`, {
|
|
3091
3498
|
encoding: "utf-8"
|
|
3092
3499
|
});
|
|
3093
3500
|
return output.trim().split("\n").filter(Boolean);
|
|
@@ -3105,13 +3512,13 @@ function shouldIgnoreCommit(files, ignorePaths) {
|
|
|
3105
3512
|
}
|
|
3106
3513
|
function printCommitsWithFiles(commits, ignore2, verbose) {
|
|
3107
3514
|
for (const commit2 of commits) {
|
|
3108
|
-
console.log(` ${
|
|
3515
|
+
console.log(` ${chalk38.yellow(commit2.hash)} ${commit2.message}`);
|
|
3109
3516
|
if (verbose) {
|
|
3110
3517
|
const visibleFiles = commit2.files.filter(
|
|
3111
3518
|
(file) => !ignore2.some((p) => file.startsWith(p))
|
|
3112
3519
|
);
|
|
3113
3520
|
for (const file of visibleFiles) {
|
|
3114
|
-
console.log(` ${
|
|
3521
|
+
console.log(` ${chalk38.dim(file)}`);
|
|
3115
3522
|
}
|
|
3116
3523
|
}
|
|
3117
3524
|
}
|
|
@@ -3136,15 +3543,15 @@ function parseGitLogCommits(output, ignore2, afterDate) {
|
|
|
3136
3543
|
}
|
|
3137
3544
|
|
|
3138
3545
|
// src/commands/devlog/list/printDateHeader.ts
|
|
3139
|
-
import
|
|
3546
|
+
import chalk39 from "chalk";
|
|
3140
3547
|
function printDateHeader(date, isSkipped, entries) {
|
|
3141
3548
|
if (isSkipped) {
|
|
3142
|
-
console.log(`${
|
|
3549
|
+
console.log(`${chalk39.bold.blue(date)} ${chalk39.dim("skipped")}`);
|
|
3143
3550
|
} else if (entries && entries.length > 0) {
|
|
3144
|
-
const entryInfo = entries.map((e) => `${
|
|
3145
|
-
console.log(`${
|
|
3551
|
+
const entryInfo = entries.map((e) => `${chalk39.green(e.version)} ${e.title}`).join(" | ");
|
|
3552
|
+
console.log(`${chalk39.bold.blue(date)} ${entryInfo}`);
|
|
3146
3553
|
} else {
|
|
3147
|
-
console.log(`${
|
|
3554
|
+
console.log(`${chalk39.bold.blue(date)} ${chalk39.red("\u26A0 devlog missing")}`);
|
|
3148
3555
|
}
|
|
3149
3556
|
}
|
|
3150
3557
|
|
|
@@ -3158,7 +3565,7 @@ function list3(options2) {
|
|
|
3158
3565
|
const devlogEntries = loadDevlogEntries(repoName);
|
|
3159
3566
|
const reverseFlag = options2.reverse ? "--reverse " : "";
|
|
3160
3567
|
const limitFlag = options2.reverse ? "" : "-n 500 ";
|
|
3161
|
-
const output =
|
|
3568
|
+
const output = execSync15(
|
|
3162
3569
|
`git log ${reverseFlag}${limitFlag}--pretty=format:'%ad|%h|%s' --date=short`,
|
|
3163
3570
|
{ encoding: "utf-8" }
|
|
3164
3571
|
);
|
|
@@ -3184,11 +3591,11 @@ function list3(options2) {
|
|
|
3184
3591
|
}
|
|
3185
3592
|
|
|
3186
3593
|
// src/commands/devlog/getLastVersionInfo.ts
|
|
3187
|
-
import { execSync as
|
|
3594
|
+
import { execSync as execSync16 } from "child_process";
|
|
3188
3595
|
import semver from "semver";
|
|
3189
3596
|
function getVersionAtCommit(hash) {
|
|
3190
3597
|
try {
|
|
3191
|
-
const content =
|
|
3598
|
+
const content = execSync16(`git show ${hash}:package.json`, {
|
|
3192
3599
|
encoding: "utf-8"
|
|
3193
3600
|
});
|
|
3194
3601
|
const pkg = JSON.parse(content);
|
|
@@ -3203,7 +3610,7 @@ function stripToMinor(version2) {
|
|
|
3203
3610
|
}
|
|
3204
3611
|
function getLastVersionInfoFromGit() {
|
|
3205
3612
|
try {
|
|
3206
|
-
const output =
|
|
3613
|
+
const output = execSync16(
|
|
3207
3614
|
"git log -1 --pretty=format:'%ad|%h' --date=short",
|
|
3208
3615
|
{
|
|
3209
3616
|
encoding: "utf-8"
|
|
@@ -3246,25 +3653,25 @@ function bumpVersion(version2, type) {
|
|
|
3246
3653
|
}
|
|
3247
3654
|
|
|
3248
3655
|
// src/commands/devlog/next/displayNextEntry/index.ts
|
|
3249
|
-
import { execSync as
|
|
3250
|
-
import
|
|
3656
|
+
import { execSync as execSync17 } from "child_process";
|
|
3657
|
+
import chalk41 from "chalk";
|
|
3251
3658
|
|
|
3252
3659
|
// src/commands/devlog/next/displayNextEntry/displayVersion.ts
|
|
3253
|
-
import
|
|
3660
|
+
import chalk40 from "chalk";
|
|
3254
3661
|
function displayVersion(conventional, firstHash, patchVersion, minorVersion) {
|
|
3255
3662
|
if (conventional && firstHash) {
|
|
3256
3663
|
const version2 = getVersionAtCommit(firstHash);
|
|
3257
3664
|
if (version2) {
|
|
3258
|
-
console.log(`${
|
|
3665
|
+
console.log(`${chalk40.bold("version:")} ${stripToMinor(version2)}`);
|
|
3259
3666
|
} else {
|
|
3260
|
-
console.log(`${
|
|
3667
|
+
console.log(`${chalk40.bold("version:")} ${chalk40.red("unknown")}`);
|
|
3261
3668
|
}
|
|
3262
3669
|
} else if (patchVersion && minorVersion) {
|
|
3263
3670
|
console.log(
|
|
3264
|
-
`${
|
|
3671
|
+
`${chalk40.bold("version:")} ${patchVersion} (patch) or ${minorVersion} (minor)`
|
|
3265
3672
|
);
|
|
3266
3673
|
} else {
|
|
3267
|
-
console.log(`${
|
|
3674
|
+
console.log(`${chalk40.bold("version:")} v0.1 (initial)`);
|
|
3268
3675
|
}
|
|
3269
3676
|
}
|
|
3270
3677
|
|
|
@@ -3280,7 +3687,7 @@ function findTargetDate(commitsByDate, skipDays) {
|
|
|
3280
3687
|
return Array.from(commitsByDate.keys()).filter((d) => !skipDays.has(d)).sort()[0];
|
|
3281
3688
|
}
|
|
3282
3689
|
function fetchCommitsByDate(ignore2, lastDate) {
|
|
3283
|
-
const output =
|
|
3690
|
+
const output = execSync17(
|
|
3284
3691
|
"git log --pretty=format:'%ad|%h|%s' --date=short -n 500",
|
|
3285
3692
|
{ encoding: "utf-8" }
|
|
3286
3693
|
);
|
|
@@ -3311,16 +3718,16 @@ function noCommitsMessage(hasLastInfo) {
|
|
|
3311
3718
|
return hasLastInfo ? "No commits after last versioned entry" : "No commits found";
|
|
3312
3719
|
}
|
|
3313
3720
|
function logName(repoName) {
|
|
3314
|
-
console.log(`${
|
|
3721
|
+
console.log(`${chalk41.bold("name:")} ${repoName}`);
|
|
3315
3722
|
}
|
|
3316
3723
|
function displayNextEntry(ctx, targetDate, commits) {
|
|
3317
3724
|
logName(ctx.repoName);
|
|
3318
3725
|
printVersionInfo(ctx.config, ctx.lastInfo, commits[0]?.hash);
|
|
3319
|
-
console.log(
|
|
3726
|
+
console.log(chalk41.bold.blue(targetDate));
|
|
3320
3727
|
printCommitsWithFiles(commits, ctx.ignore, ctx.verbose);
|
|
3321
3728
|
}
|
|
3322
3729
|
function logNoCommits(lastInfo) {
|
|
3323
|
-
console.log(
|
|
3730
|
+
console.log(chalk41.dim(noCommitsMessage(!!lastInfo)));
|
|
3324
3731
|
}
|
|
3325
3732
|
|
|
3326
3733
|
// src/commands/devlog/next/index.ts
|
|
@@ -3355,10 +3762,10 @@ function next(options2) {
|
|
|
3355
3762
|
}
|
|
3356
3763
|
|
|
3357
3764
|
// src/commands/devlog/skip.ts
|
|
3358
|
-
import
|
|
3765
|
+
import chalk42 from "chalk";
|
|
3359
3766
|
function skip(date) {
|
|
3360
3767
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
|
3361
|
-
console.log(
|
|
3768
|
+
console.log(chalk42.red("Invalid date format. Use YYYY-MM-DD"));
|
|
3362
3769
|
process.exit(1);
|
|
3363
3770
|
}
|
|
3364
3771
|
const config = loadProjectConfig();
|
|
@@ -3366,7 +3773,7 @@ function skip(date) {
|
|
|
3366
3773
|
const skip2 = devlog.skip ?? {};
|
|
3367
3774
|
const skipDays = skip2.days ?? [];
|
|
3368
3775
|
if (skipDays.includes(date)) {
|
|
3369
|
-
console.log(
|
|
3776
|
+
console.log(chalk42.yellow(`${date} is already in skip list`));
|
|
3370
3777
|
return;
|
|
3371
3778
|
}
|
|
3372
3779
|
skipDays.push(date);
|
|
@@ -3375,20 +3782,20 @@ function skip(date) {
|
|
|
3375
3782
|
devlog.skip = skip2;
|
|
3376
3783
|
config.devlog = devlog;
|
|
3377
3784
|
saveConfig(config);
|
|
3378
|
-
console.log(
|
|
3785
|
+
console.log(chalk42.green(`Added ${date} to skip list`));
|
|
3379
3786
|
}
|
|
3380
3787
|
|
|
3381
3788
|
// src/commands/devlog/version.ts
|
|
3382
|
-
import
|
|
3789
|
+
import chalk43 from "chalk";
|
|
3383
3790
|
function version() {
|
|
3384
3791
|
const config = loadConfig();
|
|
3385
3792
|
const name = getRepoName();
|
|
3386
3793
|
const lastInfo = getLastVersionInfo(name, config);
|
|
3387
3794
|
const lastVersion = lastInfo?.version ?? null;
|
|
3388
3795
|
const nextVersion = lastVersion ? bumpVersion(lastVersion, "patch") : null;
|
|
3389
|
-
console.log(`${
|
|
3390
|
-
console.log(`${
|
|
3391
|
-
console.log(`${
|
|
3796
|
+
console.log(`${chalk43.bold("name:")} ${name}`);
|
|
3797
|
+
console.log(`${chalk43.bold("last:")} ${lastVersion ?? chalk43.dim("none")}`);
|
|
3798
|
+
console.log(`${chalk43.bold("next:")} ${nextVersion ?? chalk43.dim("none")}`);
|
|
3392
3799
|
}
|
|
3393
3800
|
|
|
3394
3801
|
// src/commands/registerDevlog.ts
|
|
@@ -3406,12 +3813,12 @@ function registerDevlog(program2) {
|
|
|
3406
3813
|
|
|
3407
3814
|
// src/commands/prs/comment.ts
|
|
3408
3815
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
3409
|
-
import { unlinkSync as unlinkSync3, writeFileSync as
|
|
3816
|
+
import { unlinkSync as unlinkSync3, writeFileSync as writeFileSync15 } from "fs";
|
|
3410
3817
|
import { tmpdir as tmpdir2 } from "os";
|
|
3411
|
-
import { join as
|
|
3818
|
+
import { join as join14 } from "path";
|
|
3412
3819
|
|
|
3413
3820
|
// src/commands/prs/shared.ts
|
|
3414
|
-
import { execSync as
|
|
3821
|
+
import { execSync as execSync18 } from "child_process";
|
|
3415
3822
|
function isGhNotInstalled(error) {
|
|
3416
3823
|
if (error instanceof Error) {
|
|
3417
3824
|
const msg = error.message.toLowerCase();
|
|
@@ -3427,14 +3834,14 @@ function isNotFound(error) {
|
|
|
3427
3834
|
}
|
|
3428
3835
|
function getRepoInfo() {
|
|
3429
3836
|
const repoInfo = JSON.parse(
|
|
3430
|
-
|
|
3837
|
+
execSync18("gh repo view --json owner,name", { encoding: "utf-8" })
|
|
3431
3838
|
);
|
|
3432
3839
|
return { org: repoInfo.owner.login, repo: repoInfo.name };
|
|
3433
3840
|
}
|
|
3434
3841
|
function getCurrentPrNumber() {
|
|
3435
3842
|
try {
|
|
3436
3843
|
const prInfo = JSON.parse(
|
|
3437
|
-
|
|
3844
|
+
execSync18("gh pr view --json number", { encoding: "utf-8" })
|
|
3438
3845
|
);
|
|
3439
3846
|
return prInfo.number;
|
|
3440
3847
|
} catch (error) {
|
|
@@ -3448,7 +3855,7 @@ function getCurrentPrNumber() {
|
|
|
3448
3855
|
function getCurrentPrNodeId() {
|
|
3449
3856
|
try {
|
|
3450
3857
|
const prInfo = JSON.parse(
|
|
3451
|
-
|
|
3858
|
+
execSync18("gh pr view --json id", { encoding: "utf-8" })
|
|
3452
3859
|
);
|
|
3453
3860
|
return prInfo.id;
|
|
3454
3861
|
} catch (error) {
|
|
@@ -3480,8 +3887,8 @@ function comment(path31, line, body) {
|
|
|
3480
3887
|
validateLine(line);
|
|
3481
3888
|
try {
|
|
3482
3889
|
const prId = getCurrentPrNodeId();
|
|
3483
|
-
const queryFile =
|
|
3484
|
-
|
|
3890
|
+
const queryFile = join14(tmpdir2(), `gh-query-${Date.now()}.graphql`);
|
|
3891
|
+
writeFileSync15(queryFile, MUTATION);
|
|
3485
3892
|
try {
|
|
3486
3893
|
const result = spawnSync2(
|
|
3487
3894
|
"gh",
|
|
@@ -3519,32 +3926,32 @@ function comment(path31, line, body) {
|
|
|
3519
3926
|
}
|
|
3520
3927
|
|
|
3521
3928
|
// src/commands/prs/fixed.ts
|
|
3522
|
-
import { execSync as
|
|
3929
|
+
import { execSync as execSync20 } from "child_process";
|
|
3523
3930
|
|
|
3524
3931
|
// src/commands/prs/resolveCommentWithReply.ts
|
|
3525
|
-
import { execSync as
|
|
3526
|
-
import { unlinkSync as unlinkSync5, writeFileSync as
|
|
3932
|
+
import { execSync as execSync19 } from "child_process";
|
|
3933
|
+
import { unlinkSync as unlinkSync5, writeFileSync as writeFileSync16 } from "fs";
|
|
3527
3934
|
import { tmpdir as tmpdir3 } from "os";
|
|
3528
|
-
import { join as
|
|
3935
|
+
import { join as join16 } from "path";
|
|
3529
3936
|
|
|
3530
3937
|
// src/commands/prs/loadCommentsCache.ts
|
|
3531
|
-
import { existsSync as
|
|
3532
|
-
import { join as
|
|
3938
|
+
import { existsSync as existsSync18, readFileSync as readFileSync16, unlinkSync as unlinkSync4 } from "fs";
|
|
3939
|
+
import { join as join15 } from "path";
|
|
3533
3940
|
import { parse } from "yaml";
|
|
3534
3941
|
function getCachePath(prNumber) {
|
|
3535
|
-
return
|
|
3942
|
+
return join15(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`);
|
|
3536
3943
|
}
|
|
3537
3944
|
function loadCommentsCache(prNumber) {
|
|
3538
3945
|
const cachePath = getCachePath(prNumber);
|
|
3539
|
-
if (!
|
|
3946
|
+
if (!existsSync18(cachePath)) {
|
|
3540
3947
|
return null;
|
|
3541
3948
|
}
|
|
3542
|
-
const content =
|
|
3949
|
+
const content = readFileSync16(cachePath, "utf-8");
|
|
3543
3950
|
return parse(content);
|
|
3544
3951
|
}
|
|
3545
3952
|
function deleteCommentsCache(prNumber) {
|
|
3546
3953
|
const cachePath = getCachePath(prNumber);
|
|
3547
|
-
if (
|
|
3954
|
+
if (existsSync18(cachePath)) {
|
|
3548
3955
|
unlinkSync4(cachePath);
|
|
3549
3956
|
console.log("No more unresolved line comments. Cache dropped.");
|
|
3550
3957
|
}
|
|
@@ -3552,17 +3959,17 @@ function deleteCommentsCache(prNumber) {
|
|
|
3552
3959
|
|
|
3553
3960
|
// src/commands/prs/resolveCommentWithReply.ts
|
|
3554
3961
|
function replyToComment(org, repo, prNumber, commentId, message) {
|
|
3555
|
-
|
|
3962
|
+
execSync19(
|
|
3556
3963
|
`gh api repos/${org}/${repo}/pulls/${prNumber}/comments -f body="${message.replace(/"/g, '\\"')}" -F in_reply_to=${commentId}`,
|
|
3557
3964
|
{ stdio: "inherit" }
|
|
3558
3965
|
);
|
|
3559
3966
|
}
|
|
3560
3967
|
function resolveThread(threadId) {
|
|
3561
3968
|
const mutation = `mutation($threadId: ID!) { resolveReviewThread(input: {threadId: $threadId}) { thread { isResolved } } }`;
|
|
3562
|
-
const queryFile =
|
|
3563
|
-
|
|
3969
|
+
const queryFile = join16(tmpdir3(), `gh-mutation-${Date.now()}.graphql`);
|
|
3970
|
+
writeFileSync16(queryFile, mutation);
|
|
3564
3971
|
try {
|
|
3565
|
-
|
|
3972
|
+
execSync19(
|
|
3566
3973
|
`gh api graphql -F query=@${queryFile} -f threadId="${threadId}"`,
|
|
3567
3974
|
{ stdio: "inherit" }
|
|
3568
3975
|
);
|
|
@@ -3614,7 +4021,7 @@ function resolveCommentWithReply(commentId, message) {
|
|
|
3614
4021
|
// src/commands/prs/fixed.ts
|
|
3615
4022
|
function verifySha(sha) {
|
|
3616
4023
|
try {
|
|
3617
|
-
return
|
|
4024
|
+
return execSync20(`git rev-parse --verify ${sha}`, {
|
|
3618
4025
|
encoding: "utf-8"
|
|
3619
4026
|
}).trim();
|
|
3620
4027
|
} catch {
|
|
@@ -3640,26 +4047,21 @@ function fixed(commentId, sha) {
|
|
|
3640
4047
|
}
|
|
3641
4048
|
|
|
3642
4049
|
// src/commands/prs/listComments/index.ts
|
|
3643
|
-
import { existsSync as
|
|
3644
|
-
import { join as
|
|
4050
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync5, writeFileSync as writeFileSync18 } from "fs";
|
|
4051
|
+
import { join as join18 } from "path";
|
|
3645
4052
|
import { stringify } from "yaml";
|
|
3646
4053
|
|
|
3647
|
-
// src/lib/isClaudeCode.ts
|
|
3648
|
-
function isClaudeCode() {
|
|
3649
|
-
return process.env.CLAUDECODE !== void 0;
|
|
3650
|
-
}
|
|
3651
|
-
|
|
3652
4054
|
// src/commands/prs/fetchThreadIds.ts
|
|
3653
|
-
import { execSync as
|
|
3654
|
-
import { unlinkSync as unlinkSync6, writeFileSync as
|
|
4055
|
+
import { execSync as execSync21 } from "child_process";
|
|
4056
|
+
import { unlinkSync as unlinkSync6, writeFileSync as writeFileSync17 } from "fs";
|
|
3655
4057
|
import { tmpdir as tmpdir4 } from "os";
|
|
3656
|
-
import { join as
|
|
4058
|
+
import { join as join17 } from "path";
|
|
3657
4059
|
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 } } } } } } }`;
|
|
3658
4060
|
function fetchThreadIds(org, repo, prNumber) {
|
|
3659
|
-
const queryFile =
|
|
3660
|
-
|
|
4061
|
+
const queryFile = join17(tmpdir4(), `gh-query-${Date.now()}.graphql`);
|
|
4062
|
+
writeFileSync17(queryFile, THREAD_QUERY);
|
|
3661
4063
|
try {
|
|
3662
|
-
const result =
|
|
4064
|
+
const result = execSync21(
|
|
3663
4065
|
`gh api graphql -F query=@${queryFile} -F owner="${org}" -F repo="${repo}" -F prNumber=${prNumber}`,
|
|
3664
4066
|
{ encoding: "utf-8" }
|
|
3665
4067
|
);
|
|
@@ -3681,9 +4083,9 @@ function fetchThreadIds(org, repo, prNumber) {
|
|
|
3681
4083
|
}
|
|
3682
4084
|
|
|
3683
4085
|
// src/commands/prs/listComments/fetchReviewComments.ts
|
|
3684
|
-
import { execSync as
|
|
4086
|
+
import { execSync as execSync22 } from "child_process";
|
|
3685
4087
|
function fetchJson(endpoint) {
|
|
3686
|
-
const result =
|
|
4088
|
+
const result = execSync22(`gh api --paginate ${endpoint}`, {
|
|
3687
4089
|
encoding: "utf-8"
|
|
3688
4090
|
});
|
|
3689
4091
|
if (!result.trim()) return [];
|
|
@@ -3725,20 +4127,20 @@ function fetchLineComments(org, repo, prNumber, threadInfo) {
|
|
|
3725
4127
|
}
|
|
3726
4128
|
|
|
3727
4129
|
// src/commands/prs/listComments/formatForHuman.ts
|
|
3728
|
-
import
|
|
4130
|
+
import chalk44 from "chalk";
|
|
3729
4131
|
function formatForHuman(comment2) {
|
|
3730
4132
|
if (comment2.type === "review") {
|
|
3731
|
-
const stateColor = comment2.state === "APPROVED" ?
|
|
4133
|
+
const stateColor = comment2.state === "APPROVED" ? chalk44.green : comment2.state === "CHANGES_REQUESTED" ? chalk44.red : chalk44.yellow;
|
|
3732
4134
|
return [
|
|
3733
|
-
`${
|
|
4135
|
+
`${chalk44.cyan("Review")} by ${chalk44.bold(comment2.user)} ${stateColor(`[${comment2.state}]`)}`,
|
|
3734
4136
|
comment2.body,
|
|
3735
4137
|
""
|
|
3736
4138
|
].join("\n");
|
|
3737
4139
|
}
|
|
3738
4140
|
const location = comment2.line ? `:${comment2.line}` : "";
|
|
3739
4141
|
return [
|
|
3740
|
-
`${
|
|
3741
|
-
|
|
4142
|
+
`${chalk44.cyan("Line comment")} by ${chalk44.bold(comment2.user)} on ${chalk44.dim(`${comment2.path}${location}`)}`,
|
|
4143
|
+
chalk44.dim(comment2.diff_hunk.split("\n").slice(-3).join("\n")),
|
|
3742
4144
|
comment2.body,
|
|
3743
4145
|
""
|
|
3744
4146
|
].join("\n");
|
|
@@ -3761,17 +4163,17 @@ function printComments(comments) {
|
|
|
3761
4163
|
}
|
|
3762
4164
|
}
|
|
3763
4165
|
function writeCommentsCache(prNumber, comments) {
|
|
3764
|
-
const assistDir =
|
|
3765
|
-
if (!
|
|
3766
|
-
|
|
4166
|
+
const assistDir = join18(process.cwd(), ".assist");
|
|
4167
|
+
if (!existsSync19(assistDir)) {
|
|
4168
|
+
mkdirSync5(assistDir, { recursive: true });
|
|
3767
4169
|
}
|
|
3768
4170
|
const cacheData = {
|
|
3769
4171
|
prNumber,
|
|
3770
4172
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3771
4173
|
comments
|
|
3772
4174
|
};
|
|
3773
|
-
const cachePath =
|
|
3774
|
-
|
|
4175
|
+
const cachePath = join18(assistDir, `pr-${prNumber}-comments.yaml`);
|
|
4176
|
+
writeFileSync18(cachePath, stringify(cacheData));
|
|
3775
4177
|
}
|
|
3776
4178
|
function handleKnownErrors(error) {
|
|
3777
4179
|
if (isGhNotInstalled(error)) {
|
|
@@ -3811,19 +4213,19 @@ async function listComments() {
|
|
|
3811
4213
|
}
|
|
3812
4214
|
|
|
3813
4215
|
// src/commands/prs/prs/index.ts
|
|
3814
|
-
import { execSync as
|
|
4216
|
+
import { execSync as execSync23 } from "child_process";
|
|
3815
4217
|
|
|
3816
4218
|
// src/commands/prs/prs/displayPaginated/index.ts
|
|
3817
4219
|
import enquirer5 from "enquirer";
|
|
3818
4220
|
|
|
3819
4221
|
// src/commands/prs/prs/displayPaginated/printPr.ts
|
|
3820
|
-
import
|
|
4222
|
+
import chalk45 from "chalk";
|
|
3821
4223
|
var STATUS_MAP = {
|
|
3822
|
-
MERGED: (pr) => pr.mergedAt ? { label:
|
|
3823
|
-
CLOSED: (pr) => pr.closedAt ? { label:
|
|
4224
|
+
MERGED: (pr) => pr.mergedAt ? { label: chalk45.magenta("merged"), date: pr.mergedAt } : null,
|
|
4225
|
+
CLOSED: (pr) => pr.closedAt ? { label: chalk45.red("closed"), date: pr.closedAt } : null
|
|
3824
4226
|
};
|
|
3825
4227
|
function defaultStatus(pr) {
|
|
3826
|
-
return { label:
|
|
4228
|
+
return { label: chalk45.green("opened"), date: pr.createdAt };
|
|
3827
4229
|
}
|
|
3828
4230
|
function getStatus(pr) {
|
|
3829
4231
|
return STATUS_MAP[pr.state]?.(pr) ?? defaultStatus(pr);
|
|
@@ -3832,11 +4234,11 @@ function formatDate(dateStr) {
|
|
|
3832
4234
|
return new Date(dateStr).toISOString().split("T")[0];
|
|
3833
4235
|
}
|
|
3834
4236
|
function formatPrHeader(pr, status2) {
|
|
3835
|
-
return `${
|
|
4237
|
+
return `${chalk45.cyan(`#${pr.number}`)} ${pr.title} ${chalk45.dim(`(${pr.author.login},`)} ${status2.label} ${chalk45.dim(`${formatDate(status2.date)})`)}`;
|
|
3836
4238
|
}
|
|
3837
4239
|
function logPrDetails(pr) {
|
|
3838
4240
|
console.log(
|
|
3839
|
-
|
|
4241
|
+
chalk45.dim(` ${pr.changedFiles.toLocaleString()} files | ${pr.url}`)
|
|
3840
4242
|
);
|
|
3841
4243
|
console.log();
|
|
3842
4244
|
}
|
|
@@ -3917,7 +4319,7 @@ async function displayPaginated(pullRequests) {
|
|
|
3917
4319
|
async function prs(options2) {
|
|
3918
4320
|
const state = options2.open ? "open" : options2.closed ? "closed" : "all";
|
|
3919
4321
|
try {
|
|
3920
|
-
const result =
|
|
4322
|
+
const result = execSync23(
|
|
3921
4323
|
`gh pr list --state ${state} --json number,title,url,author,createdAt,mergedAt,closedAt,state,changedFiles --limit 100`,
|
|
3922
4324
|
{ encoding: "utf-8" }
|
|
3923
4325
|
);
|
|
@@ -3940,7 +4342,7 @@ async function prs(options2) {
|
|
|
3940
4342
|
}
|
|
3941
4343
|
|
|
3942
4344
|
// src/commands/prs/wontfix.ts
|
|
3943
|
-
import { execSync as
|
|
4345
|
+
import { execSync as execSync24 } from "child_process";
|
|
3944
4346
|
function validateReason(reason) {
|
|
3945
4347
|
const lowerReason = reason.toLowerCase();
|
|
3946
4348
|
if (lowerReason.includes("claude") || lowerReason.includes("opus")) {
|
|
@@ -3957,7 +4359,7 @@ function validateShaReferences(reason) {
|
|
|
3957
4359
|
const invalidShas = [];
|
|
3958
4360
|
for (const sha of shas) {
|
|
3959
4361
|
try {
|
|
3960
|
-
|
|
4362
|
+
execSync24(`git cat-file -t ${sha}`, { stdio: "pipe" });
|
|
3961
4363
|
} catch {
|
|
3962
4364
|
invalidShas.push(sha);
|
|
3963
4365
|
}
|
|
@@ -4006,7 +4408,7 @@ import { spawn as spawn3 } from "child_process";
|
|
|
4006
4408
|
import * as path17 from "path";
|
|
4007
4409
|
|
|
4008
4410
|
// src/commands/refactor/logViolations.ts
|
|
4009
|
-
import
|
|
4411
|
+
import chalk46 from "chalk";
|
|
4010
4412
|
var DEFAULT_MAX_LINES = 100;
|
|
4011
4413
|
function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
|
|
4012
4414
|
if (violations.length === 0) {
|
|
@@ -4015,43 +4417,43 @@ function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
|
|
|
4015
4417
|
}
|
|
4016
4418
|
return;
|
|
4017
4419
|
}
|
|
4018
|
-
console.error(
|
|
4420
|
+
console.error(chalk46.red(`
|
|
4019
4421
|
Refactor check failed:
|
|
4020
4422
|
`));
|
|
4021
|
-
console.error(
|
|
4423
|
+
console.error(chalk46.red(` The following files exceed ${maxLines} lines:
|
|
4022
4424
|
`));
|
|
4023
4425
|
for (const violation of violations) {
|
|
4024
|
-
console.error(
|
|
4426
|
+
console.error(chalk46.red(` ${violation.file} (${violation.lines} lines)`));
|
|
4025
4427
|
}
|
|
4026
4428
|
console.error(
|
|
4027
|
-
|
|
4429
|
+
chalk46.yellow(
|
|
4028
4430
|
`
|
|
4029
4431
|
Each file needs to be sensibly refactored, or if there is no sensible
|
|
4030
4432
|
way to refactor it, ignore it with:
|
|
4031
4433
|
`
|
|
4032
4434
|
)
|
|
4033
4435
|
);
|
|
4034
|
-
console.error(
|
|
4436
|
+
console.error(chalk46.gray(` assist refactor ignore <file>
|
|
4035
4437
|
`));
|
|
4036
4438
|
if (process.env.CLAUDECODE) {
|
|
4037
|
-
console.error(
|
|
4439
|
+
console.error(chalk46.cyan(`
|
|
4038
4440
|
## Extracting Code to New Files
|
|
4039
4441
|
`));
|
|
4040
4442
|
console.error(
|
|
4041
|
-
|
|
4443
|
+
chalk46.cyan(
|
|
4042
4444
|
` When extracting logic from one file to another, consider where the extracted code belongs:
|
|
4043
4445
|
`
|
|
4044
4446
|
)
|
|
4045
4447
|
);
|
|
4046
4448
|
console.error(
|
|
4047
|
-
|
|
4449
|
+
chalk46.cyan(
|
|
4048
4450
|
` 1. Keep related logic together: If the extracted code is tightly coupled to the
|
|
4049
4451
|
original file's domain, create a new folder containing both the original and extracted files.
|
|
4050
4452
|
`
|
|
4051
4453
|
)
|
|
4052
4454
|
);
|
|
4053
4455
|
console.error(
|
|
4054
|
-
|
|
4456
|
+
chalk46.cyan(
|
|
4055
4457
|
` 2. Share common utilities: If the extracted code can be reused across multiple
|
|
4056
4458
|
domains, move it to a common/shared folder.
|
|
4057
4459
|
`
|
|
@@ -4061,7 +4463,7 @@ Refactor check failed:
|
|
|
4061
4463
|
}
|
|
4062
4464
|
|
|
4063
4465
|
// src/commands/refactor/check/getViolations/index.ts
|
|
4064
|
-
import { execSync as
|
|
4466
|
+
import { execSync as execSync25 } from "child_process";
|
|
4065
4467
|
import fs15 from "fs";
|
|
4066
4468
|
import { minimatch as minimatch4 } from "minimatch";
|
|
4067
4469
|
|
|
@@ -4111,7 +4513,7 @@ function getGitFiles(options2) {
|
|
|
4111
4513
|
}
|
|
4112
4514
|
const files = /* @__PURE__ */ new Set();
|
|
4113
4515
|
if (options2.staged || options2.modified) {
|
|
4114
|
-
const staged =
|
|
4516
|
+
const staged = execSync25("git diff --cached --name-only", {
|
|
4115
4517
|
encoding: "utf-8"
|
|
4116
4518
|
});
|
|
4117
4519
|
for (const file of staged.trim().split("\n").filter(Boolean)) {
|
|
@@ -4119,7 +4521,7 @@ function getGitFiles(options2) {
|
|
|
4119
4521
|
}
|
|
4120
4522
|
}
|
|
4121
4523
|
if (options2.unstaged || options2.modified) {
|
|
4122
|
-
const unstaged =
|
|
4524
|
+
const unstaged = execSync25("git diff --name-only", { encoding: "utf-8" });
|
|
4123
4525
|
for (const file of unstaged.trim().split("\n").filter(Boolean)) {
|
|
4124
4526
|
files.add(file);
|
|
4125
4527
|
}
|
|
@@ -4207,11 +4609,11 @@ async function check(pattern2, options2) {
|
|
|
4207
4609
|
|
|
4208
4610
|
// src/commands/refactor/ignore.ts
|
|
4209
4611
|
import fs16 from "fs";
|
|
4210
|
-
import
|
|
4612
|
+
import chalk47 from "chalk";
|
|
4211
4613
|
var REFACTOR_YML_PATH2 = "refactor.yml";
|
|
4212
4614
|
function ignore(file) {
|
|
4213
4615
|
if (!fs16.existsSync(file)) {
|
|
4214
|
-
console.error(
|
|
4616
|
+
console.error(chalk47.red(`Error: File does not exist: ${file}`));
|
|
4215
4617
|
process.exit(1);
|
|
4216
4618
|
}
|
|
4217
4619
|
const content = fs16.readFileSync(file, "utf-8");
|
|
@@ -4227,7 +4629,7 @@ function ignore(file) {
|
|
|
4227
4629
|
fs16.writeFileSync(REFACTOR_YML_PATH2, entry);
|
|
4228
4630
|
}
|
|
4229
4631
|
console.log(
|
|
4230
|
-
|
|
4632
|
+
chalk47.green(
|
|
4231
4633
|
`Added ${file} to refactor ignore list (max ${maxLines} lines)`
|
|
4232
4634
|
)
|
|
4233
4635
|
);
|
|
@@ -4235,7 +4637,7 @@ function ignore(file) {
|
|
|
4235
4637
|
|
|
4236
4638
|
// src/commands/refactor/restructure/index.ts
|
|
4237
4639
|
import path26 from "path";
|
|
4238
|
-
import
|
|
4640
|
+
import chalk50 from "chalk";
|
|
4239
4641
|
|
|
4240
4642
|
// src/commands/refactor/restructure/buildImportGraph/index.ts
|
|
4241
4643
|
import path18 from "path";
|
|
@@ -4478,50 +4880,50 @@ function computeRewrites(moves, edges, allProjectFiles) {
|
|
|
4478
4880
|
|
|
4479
4881
|
// src/commands/refactor/restructure/displayPlan.ts
|
|
4480
4882
|
import path22 from "path";
|
|
4481
|
-
import
|
|
4883
|
+
import chalk48 from "chalk";
|
|
4482
4884
|
function relPath(filePath) {
|
|
4483
4885
|
return path22.relative(process.cwd(), filePath);
|
|
4484
4886
|
}
|
|
4485
4887
|
function displayMoves(plan) {
|
|
4486
4888
|
if (plan.moves.length === 0) return;
|
|
4487
|
-
console.log(
|
|
4889
|
+
console.log(chalk48.bold("\nFile moves:"));
|
|
4488
4890
|
for (const move of plan.moves) {
|
|
4489
4891
|
console.log(
|
|
4490
|
-
` ${
|
|
4892
|
+
` ${chalk48.red(relPath(move.from))} \u2192 ${chalk48.green(relPath(move.to))}`
|
|
4491
4893
|
);
|
|
4492
|
-
console.log(
|
|
4894
|
+
console.log(chalk48.dim(` ${move.reason}`));
|
|
4493
4895
|
}
|
|
4494
4896
|
}
|
|
4495
4897
|
function displayRewrites(rewrites) {
|
|
4496
4898
|
if (rewrites.length === 0) return;
|
|
4497
4899
|
const affectedFiles = new Set(rewrites.map((r) => r.file));
|
|
4498
|
-
console.log(
|
|
4900
|
+
console.log(chalk48.bold(`
|
|
4499
4901
|
Import rewrites (${affectedFiles.size} files):`));
|
|
4500
4902
|
for (const file of affectedFiles) {
|
|
4501
|
-
console.log(` ${
|
|
4903
|
+
console.log(` ${chalk48.cyan(relPath(file))}:`);
|
|
4502
4904
|
for (const { oldSpecifier, newSpecifier } of rewrites.filter(
|
|
4503
4905
|
(r) => r.file === file
|
|
4504
4906
|
)) {
|
|
4505
4907
|
console.log(
|
|
4506
|
-
` ${
|
|
4908
|
+
` ${chalk48.red(`"${oldSpecifier}"`)} \u2192 ${chalk48.green(`"${newSpecifier}"`)}`
|
|
4507
4909
|
);
|
|
4508
4910
|
}
|
|
4509
4911
|
}
|
|
4510
4912
|
}
|
|
4511
4913
|
function displayPlan(plan) {
|
|
4512
4914
|
if (plan.warnings.length > 0) {
|
|
4513
|
-
console.log(
|
|
4514
|
-
for (const w of plan.warnings) console.log(
|
|
4915
|
+
console.log(chalk48.yellow("\nWarnings:"));
|
|
4916
|
+
for (const w of plan.warnings) console.log(chalk48.yellow(` ${w}`));
|
|
4515
4917
|
}
|
|
4516
4918
|
if (plan.newDirectories.length > 0) {
|
|
4517
|
-
console.log(
|
|
4919
|
+
console.log(chalk48.bold("\nNew directories:"));
|
|
4518
4920
|
for (const dir of plan.newDirectories)
|
|
4519
|
-
console.log(
|
|
4921
|
+
console.log(chalk48.green(` ${dir}/`));
|
|
4520
4922
|
}
|
|
4521
4923
|
displayMoves(plan);
|
|
4522
4924
|
displayRewrites(plan.rewrites);
|
|
4523
4925
|
console.log(
|
|
4524
|
-
|
|
4926
|
+
chalk48.dim(
|
|
4525
4927
|
`
|
|
4526
4928
|
Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rewritten`
|
|
4527
4929
|
)
|
|
@@ -4531,18 +4933,18 @@ Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rew
|
|
|
4531
4933
|
// src/commands/refactor/restructure/executePlan.ts
|
|
4532
4934
|
import fs18 from "fs";
|
|
4533
4935
|
import path23 from "path";
|
|
4534
|
-
import
|
|
4936
|
+
import chalk49 from "chalk";
|
|
4535
4937
|
function executePlan(plan) {
|
|
4536
4938
|
const updatedContents = applyRewrites(plan.rewrites);
|
|
4537
4939
|
for (const [file, content] of updatedContents) {
|
|
4538
4940
|
fs18.writeFileSync(file, content, "utf-8");
|
|
4539
4941
|
console.log(
|
|
4540
|
-
|
|
4942
|
+
chalk49.cyan(` Rewrote imports in ${path23.relative(process.cwd(), file)}`)
|
|
4541
4943
|
);
|
|
4542
4944
|
}
|
|
4543
4945
|
for (const dir of plan.newDirectories) {
|
|
4544
4946
|
fs18.mkdirSync(dir, { recursive: true });
|
|
4545
|
-
console.log(
|
|
4947
|
+
console.log(chalk49.green(` Created ${path23.relative(process.cwd(), dir)}/`));
|
|
4546
4948
|
}
|
|
4547
4949
|
for (const move of plan.moves) {
|
|
4548
4950
|
const targetDir = path23.dirname(move.to);
|
|
@@ -4551,7 +4953,7 @@ function executePlan(plan) {
|
|
|
4551
4953
|
}
|
|
4552
4954
|
fs18.renameSync(move.from, move.to);
|
|
4553
4955
|
console.log(
|
|
4554
|
-
|
|
4956
|
+
chalk49.white(
|
|
4555
4957
|
` Moved ${path23.relative(process.cwd(), move.from)} \u2192 ${path23.relative(process.cwd(), move.to)}`
|
|
4556
4958
|
)
|
|
4557
4959
|
);
|
|
@@ -4566,7 +4968,7 @@ function removeEmptyDirectories(dirs) {
|
|
|
4566
4968
|
if (entries.length === 0) {
|
|
4567
4969
|
fs18.rmdirSync(dir);
|
|
4568
4970
|
console.log(
|
|
4569
|
-
|
|
4971
|
+
chalk49.dim(
|
|
4570
4972
|
` Removed empty directory ${path23.relative(process.cwd(), dir)}`
|
|
4571
4973
|
)
|
|
4572
4974
|
);
|
|
@@ -4697,22 +5099,22 @@ async function restructure(pattern2, options2 = {}) {
|
|
|
4697
5099
|
const targetPattern = pattern2 ?? "src";
|
|
4698
5100
|
const files = findSourceFiles2(targetPattern);
|
|
4699
5101
|
if (files.length === 0) {
|
|
4700
|
-
console.log(
|
|
5102
|
+
console.log(chalk50.yellow("No files found matching pattern"));
|
|
4701
5103
|
return;
|
|
4702
5104
|
}
|
|
4703
5105
|
const tsConfigPath = path26.resolve("tsconfig.json");
|
|
4704
5106
|
const plan = buildPlan(files, tsConfigPath);
|
|
4705
5107
|
if (plan.moves.length === 0) {
|
|
4706
|
-
console.log(
|
|
5108
|
+
console.log(chalk50.green("No restructuring needed"));
|
|
4707
5109
|
return;
|
|
4708
5110
|
}
|
|
4709
5111
|
displayPlan(plan);
|
|
4710
5112
|
if (options2.apply) {
|
|
4711
|
-
console.log(
|
|
5113
|
+
console.log(chalk50.bold("\nApplying changes..."));
|
|
4712
5114
|
executePlan(plan);
|
|
4713
|
-
console.log(
|
|
5115
|
+
console.log(chalk50.green("\nRestructuring complete"));
|
|
4714
5116
|
} else {
|
|
4715
|
-
console.log(
|
|
5117
|
+
console.log(chalk50.dim("\nDry run. Use --apply to execute."));
|
|
4716
5118
|
}
|
|
4717
5119
|
}
|
|
4718
5120
|
|
|
@@ -4735,8 +5137,8 @@ function registerRefactor(program2) {
|
|
|
4735
5137
|
}
|
|
4736
5138
|
|
|
4737
5139
|
// src/commands/transcript/shared.ts
|
|
4738
|
-
import { existsSync as
|
|
4739
|
-
import { basename as basename4, join as
|
|
5140
|
+
import { existsSync as existsSync20, readdirSync as readdirSync2, statSync } from "fs";
|
|
5141
|
+
import { basename as basename4, join as join19, relative } from "path";
|
|
4740
5142
|
import * as readline2 from "readline";
|
|
4741
5143
|
var DATE_PREFIX_REGEX = /^\d{4}-\d{2}-\d{2}/;
|
|
4742
5144
|
function getDatePrefix(daysOffset = 0) {
|
|
@@ -4751,10 +5153,10 @@ function isValidDatePrefix(filename) {
|
|
|
4751
5153
|
return DATE_PREFIX_REGEX.test(filename);
|
|
4752
5154
|
}
|
|
4753
5155
|
function collectFiles(dir, extension) {
|
|
4754
|
-
if (!
|
|
5156
|
+
if (!existsSync20(dir)) return [];
|
|
4755
5157
|
const results = [];
|
|
4756
5158
|
for (const entry of readdirSync2(dir)) {
|
|
4757
|
-
const fullPath =
|
|
5159
|
+
const fullPath = join19(dir, entry);
|
|
4758
5160
|
if (statSync(fullPath).isDirectory()) {
|
|
4759
5161
|
results.push(...collectFiles(fullPath, extension));
|
|
4760
5162
|
} else if (entry.endsWith(extension)) {
|
|
@@ -4848,14 +5250,14 @@ async function configure() {
|
|
|
4848
5250
|
}
|
|
4849
5251
|
|
|
4850
5252
|
// src/commands/transcript/format/index.ts
|
|
4851
|
-
import { existsSync as
|
|
5253
|
+
import { existsSync as existsSync22 } from "fs";
|
|
4852
5254
|
|
|
4853
5255
|
// src/commands/transcript/format/fixInvalidDatePrefixes/index.ts
|
|
4854
|
-
import { dirname as dirname13, join as
|
|
5256
|
+
import { dirname as dirname13, join as join21 } from "path";
|
|
4855
5257
|
|
|
4856
5258
|
// src/commands/transcript/format/fixInvalidDatePrefixes/promptForDateFix.ts
|
|
4857
5259
|
import { renameSync } from "fs";
|
|
4858
|
-
import { join as
|
|
5260
|
+
import { join as join20 } from "path";
|
|
4859
5261
|
async function resolveDate(rl, choice) {
|
|
4860
5262
|
if (choice === "1") return getDatePrefix(0);
|
|
4861
5263
|
if (choice === "2") return getDatePrefix(-1);
|
|
@@ -4868,9 +5270,9 @@ async function resolveDate(rl, choice) {
|
|
|
4868
5270
|
console.log("Cancelled.");
|
|
4869
5271
|
return null;
|
|
4870
5272
|
}
|
|
4871
|
-
function renameWithPrefix(vttDir, vttFile,
|
|
4872
|
-
const newFilename = `${
|
|
4873
|
-
renameSync(
|
|
5273
|
+
function renameWithPrefix(vttDir, vttFile, prefix2) {
|
|
5274
|
+
const newFilename = `${prefix2}.${vttFile}`;
|
|
5275
|
+
renameSync(join20(vttDir, vttFile), join20(vttDir, newFilename));
|
|
4874
5276
|
console.log(`Renamed to: ${newFilename}`);
|
|
4875
5277
|
return newFilename;
|
|
4876
5278
|
}
|
|
@@ -4887,9 +5289,9 @@ Error: File "${vttFile}" does not start with YYYY-MM-DD format.`
|
|
|
4887
5289
|
console.log(" 4. Cancel");
|
|
4888
5290
|
try {
|
|
4889
5291
|
const choice = await askQuestion(rl, "\nSelect an option (1/2/3/4): ");
|
|
4890
|
-
const
|
|
5292
|
+
const prefix2 = await resolveDate(rl, choice);
|
|
4891
5293
|
rl.close();
|
|
4892
|
-
return
|
|
5294
|
+
return prefix2 ? renameWithPrefix(vttDir, vttFile, prefix2) : null;
|
|
4893
5295
|
} catch (error) {
|
|
4894
5296
|
rl.close();
|
|
4895
5297
|
throw error;
|
|
@@ -4904,12 +5306,12 @@ async function fixInvalidDatePrefixes(vttFiles) {
|
|
|
4904
5306
|
const vttFileDir = dirname13(vttFile.absolutePath);
|
|
4905
5307
|
const newFilename = await promptForDateFix(vttFile.filename, vttFileDir);
|
|
4906
5308
|
if (newFilename) {
|
|
4907
|
-
const newRelativePath =
|
|
5309
|
+
const newRelativePath = join21(
|
|
4908
5310
|
dirname13(vttFile.relativePath),
|
|
4909
5311
|
newFilename
|
|
4910
5312
|
);
|
|
4911
5313
|
vttFiles[i] = {
|
|
4912
|
-
absolutePath:
|
|
5314
|
+
absolutePath: join21(vttFileDir, newFilename),
|
|
4913
5315
|
relativePath: newRelativePath,
|
|
4914
5316
|
filename: newFilename
|
|
4915
5317
|
};
|
|
@@ -4922,8 +5324,8 @@ async function fixInvalidDatePrefixes(vttFiles) {
|
|
|
4922
5324
|
}
|
|
4923
5325
|
|
|
4924
5326
|
// src/commands/transcript/format/processVttFile/index.ts
|
|
4925
|
-
import { existsSync as
|
|
4926
|
-
import { basename as basename5, dirname as dirname14, join as
|
|
5327
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync6, readFileSync as readFileSync17, writeFileSync as writeFileSync19 } from "fs";
|
|
5328
|
+
import { basename as basename5, dirname as dirname14, join as join22 } from "path";
|
|
4927
5329
|
|
|
4928
5330
|
// src/commands/transcript/cleanText.ts
|
|
4929
5331
|
function cleanText(text) {
|
|
@@ -4993,8 +5395,8 @@ function removeSubstringDuplicates(cues) {
|
|
|
4993
5395
|
function findWordOverlap(currentWords, nextWords) {
|
|
4994
5396
|
for (let j = Math.min(5, currentWords.length); j >= 1; j--) {
|
|
4995
5397
|
const suffix = currentWords.slice(-j).join(" ");
|
|
4996
|
-
const
|
|
4997
|
-
if (suffix ===
|
|
5398
|
+
const prefix2 = nextWords.slice(0, j).join(" ");
|
|
5399
|
+
if (suffix === prefix2) return j;
|
|
4998
5400
|
}
|
|
4999
5401
|
return 0;
|
|
5000
5402
|
}
|
|
@@ -5133,22 +5535,22 @@ function toMdFilename(vttFilename) {
|
|
|
5133
5535
|
return `${basename5(vttFilename, ".vtt").replace(/\s*Transcription\s*/g, " ").trim()}.md`;
|
|
5134
5536
|
}
|
|
5135
5537
|
function resolveOutputDir(relativeDir, transcriptsDir) {
|
|
5136
|
-
return relativeDir === "." ? transcriptsDir :
|
|
5538
|
+
return relativeDir === "." ? transcriptsDir : join22(transcriptsDir, relativeDir);
|
|
5137
5539
|
}
|
|
5138
5540
|
function buildOutputPaths(vttFile, transcriptsDir) {
|
|
5139
5541
|
const mdFile = toMdFilename(vttFile.filename);
|
|
5140
5542
|
const relativeDir = dirname14(vttFile.relativePath);
|
|
5141
5543
|
const outputDir = resolveOutputDir(relativeDir, transcriptsDir);
|
|
5142
|
-
const outputPath =
|
|
5544
|
+
const outputPath = join22(outputDir, mdFile);
|
|
5143
5545
|
return { outputDir, outputPath, mdFile, relativeDir };
|
|
5144
5546
|
}
|
|
5145
5547
|
function logSkipped(relativeDir, mdFile) {
|
|
5146
|
-
console.log(`Skipping (already exists): ${
|
|
5548
|
+
console.log(`Skipping (already exists): ${join22(relativeDir, mdFile)}`);
|
|
5147
5549
|
return "skipped";
|
|
5148
5550
|
}
|
|
5149
5551
|
function ensureDirectory(dir, label2) {
|
|
5150
|
-
if (!
|
|
5151
|
-
|
|
5552
|
+
if (!existsSync21(dir)) {
|
|
5553
|
+
mkdirSync6(dir, { recursive: true });
|
|
5152
5554
|
console.log(`Created ${label2}: ${dir}`);
|
|
5153
5555
|
}
|
|
5154
5556
|
}
|
|
@@ -5170,10 +5572,10 @@ function logReduction(cueCount, messageCount) {
|
|
|
5170
5572
|
}
|
|
5171
5573
|
function readAndParseCues(inputPath) {
|
|
5172
5574
|
console.log(`Reading: ${inputPath}`);
|
|
5173
|
-
return processCues(
|
|
5575
|
+
return processCues(readFileSync17(inputPath, "utf-8"));
|
|
5174
5576
|
}
|
|
5175
5577
|
function writeFormatted(outputPath, content) {
|
|
5176
|
-
|
|
5578
|
+
writeFileSync19(outputPath, content, "utf-8");
|
|
5177
5579
|
console.log(`Written: ${outputPath}`);
|
|
5178
5580
|
}
|
|
5179
5581
|
function convertVttToMarkdown(inputPath, outputPath) {
|
|
@@ -5183,7 +5585,7 @@ function convertVttToMarkdown(inputPath, outputPath) {
|
|
|
5183
5585
|
logReduction(cues.length, chatMessages.length);
|
|
5184
5586
|
}
|
|
5185
5587
|
function tryProcessVtt(vttFile, paths) {
|
|
5186
|
-
if (
|
|
5588
|
+
if (existsSync21(paths.outputPath))
|
|
5187
5589
|
return logSkipped(paths.relativeDir, paths.mdFile);
|
|
5188
5590
|
convertVttToMarkdown(vttFile.absolutePath, paths.outputPath);
|
|
5189
5591
|
return "processed";
|
|
@@ -5209,7 +5611,7 @@ function processAllFiles(vttFiles, transcriptsDir) {
|
|
|
5209
5611
|
logSummary(counts);
|
|
5210
5612
|
}
|
|
5211
5613
|
function requireVttDir(vttDir) {
|
|
5212
|
-
if (!
|
|
5614
|
+
if (!existsSync22(vttDir)) {
|
|
5213
5615
|
console.error(`VTT directory not found: ${vttDir}`);
|
|
5214
5616
|
process.exit(1);
|
|
5215
5617
|
}
|
|
@@ -5241,28 +5643,28 @@ async function format() {
|
|
|
5241
5643
|
}
|
|
5242
5644
|
|
|
5243
5645
|
// src/commands/transcript/summarise/index.ts
|
|
5244
|
-
import { existsSync as
|
|
5245
|
-
import { basename as basename6, dirname as dirname16, join as
|
|
5646
|
+
import { existsSync as existsSync24 } from "fs";
|
|
5647
|
+
import { basename as basename6, dirname as dirname16, join as join24, relative as relative2 } from "path";
|
|
5246
5648
|
|
|
5247
5649
|
// src/commands/transcript/summarise/processStagedFile/index.ts
|
|
5248
5650
|
import {
|
|
5249
|
-
existsSync as
|
|
5250
|
-
mkdirSync as
|
|
5251
|
-
readFileSync as
|
|
5651
|
+
existsSync as existsSync23,
|
|
5652
|
+
mkdirSync as mkdirSync7,
|
|
5653
|
+
readFileSync as readFileSync18,
|
|
5252
5654
|
renameSync as renameSync2,
|
|
5253
5655
|
rmSync
|
|
5254
5656
|
} from "fs";
|
|
5255
|
-
import { dirname as dirname15, join as
|
|
5657
|
+
import { dirname as dirname15, join as join23 } from "path";
|
|
5256
5658
|
|
|
5257
5659
|
// src/commands/transcript/summarise/processStagedFile/validateStagedContent.ts
|
|
5258
|
-
import
|
|
5660
|
+
import chalk51 from "chalk";
|
|
5259
5661
|
var FULL_TRANSCRIPT_REGEX = /^\[Full Transcript\]\(([^)]+)\)/;
|
|
5260
5662
|
function validateStagedContent(filename, content) {
|
|
5261
5663
|
const firstLine = content.split("\n")[0];
|
|
5262
5664
|
const match = firstLine.match(FULL_TRANSCRIPT_REGEX);
|
|
5263
5665
|
if (!match) {
|
|
5264
5666
|
console.error(
|
|
5265
|
-
|
|
5667
|
+
chalk51.red(
|
|
5266
5668
|
`Staged file ${filename} missing [Full Transcript](<path>) link on first line.`
|
|
5267
5669
|
)
|
|
5268
5670
|
);
|
|
@@ -5271,7 +5673,7 @@ function validateStagedContent(filename, content) {
|
|
|
5271
5673
|
const contentAfterLink = content.slice(firstLine.length).trim();
|
|
5272
5674
|
if (!contentAfterLink) {
|
|
5273
5675
|
console.error(
|
|
5274
|
-
|
|
5676
|
+
chalk51.red(
|
|
5275
5677
|
`Staged file ${filename} has no summary content after the transcript link.`
|
|
5276
5678
|
)
|
|
5277
5679
|
);
|
|
@@ -5281,9 +5683,9 @@ function validateStagedContent(filename, content) {
|
|
|
5281
5683
|
}
|
|
5282
5684
|
|
|
5283
5685
|
// src/commands/transcript/summarise/processStagedFile/index.ts
|
|
5284
|
-
var STAGING_DIR =
|
|
5686
|
+
var STAGING_DIR = join23(process.cwd(), ".assist", "transcript");
|
|
5285
5687
|
function processStagedFile() {
|
|
5286
|
-
if (!
|
|
5688
|
+
if (!existsSync23(STAGING_DIR)) {
|
|
5287
5689
|
return false;
|
|
5288
5690
|
}
|
|
5289
5691
|
const stagedFiles = findMdFilesRecursive(STAGING_DIR);
|
|
@@ -5292,7 +5694,7 @@ function processStagedFile() {
|
|
|
5292
5694
|
}
|
|
5293
5695
|
const { transcriptsDir, summaryDir } = getTranscriptConfig();
|
|
5294
5696
|
const stagedFile = stagedFiles[0];
|
|
5295
|
-
const content =
|
|
5697
|
+
const content = readFileSync18(stagedFile.absolutePath, "utf-8");
|
|
5296
5698
|
validateStagedContent(stagedFile.filename, content);
|
|
5297
5699
|
const stagedBaseName = getTranscriptBaseName(stagedFile.filename);
|
|
5298
5700
|
const transcriptFiles = findMdFilesRecursive(transcriptsDir);
|
|
@@ -5305,10 +5707,10 @@ function processStagedFile() {
|
|
|
5305
5707
|
);
|
|
5306
5708
|
process.exit(1);
|
|
5307
5709
|
}
|
|
5308
|
-
const destPath =
|
|
5710
|
+
const destPath = join23(summaryDir, matchingTranscript.relativePath);
|
|
5309
5711
|
const destDir = dirname15(destPath);
|
|
5310
|
-
if (!
|
|
5311
|
-
|
|
5712
|
+
if (!existsSync23(destDir)) {
|
|
5713
|
+
mkdirSync7(destDir, { recursive: true });
|
|
5312
5714
|
}
|
|
5313
5715
|
renameSync2(stagedFile.absolutePath, destPath);
|
|
5314
5716
|
const remaining = findMdFilesRecursive(STAGING_DIR);
|
|
@@ -5321,7 +5723,7 @@ function processStagedFile() {
|
|
|
5321
5723
|
// src/commands/transcript/summarise/index.ts
|
|
5322
5724
|
function buildRelativeKey(relativePath, baseName) {
|
|
5323
5725
|
const relDir = dirname16(relativePath);
|
|
5324
|
-
return relDir === "." ? baseName :
|
|
5726
|
+
return relDir === "." ? baseName : join24(relDir, baseName);
|
|
5325
5727
|
}
|
|
5326
5728
|
function buildSummaryIndex(summaryDir) {
|
|
5327
5729
|
const summaryFiles = findMdFilesRecursive(summaryDir);
|
|
@@ -5334,7 +5736,7 @@ function buildSummaryIndex(summaryDir) {
|
|
|
5334
5736
|
function summarise() {
|
|
5335
5737
|
processStagedFile();
|
|
5336
5738
|
const { transcriptsDir, summaryDir } = getTranscriptConfig();
|
|
5337
|
-
if (!
|
|
5739
|
+
if (!existsSync24(transcriptsDir)) {
|
|
5338
5740
|
console.log("No transcripts directory found.");
|
|
5339
5741
|
return;
|
|
5340
5742
|
}
|
|
@@ -5355,8 +5757,8 @@ function summarise() {
|
|
|
5355
5757
|
}
|
|
5356
5758
|
const next2 = missing[0];
|
|
5357
5759
|
const outputFilename = `${getTranscriptBaseName(next2.filename)}.md`;
|
|
5358
|
-
const outputPath =
|
|
5359
|
-
const summaryFileDir =
|
|
5760
|
+
const outputPath = join24(STAGING_DIR, outputFilename);
|
|
5761
|
+
const summaryFileDir = join24(summaryDir, dirname16(next2.relativePath));
|
|
5360
5762
|
const relativeTranscriptPath = encodeURI(
|
|
5361
5763
|
relative2(summaryFileDir, next2.absolutePath).replace(/\\/g, "/")
|
|
5362
5764
|
);
|
|
@@ -5402,50 +5804,50 @@ function registerVerify(program2) {
|
|
|
5402
5804
|
|
|
5403
5805
|
// src/commands/voice/devices.ts
|
|
5404
5806
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
5405
|
-
import { join as
|
|
5807
|
+
import { join as join26 } from "path";
|
|
5406
5808
|
|
|
5407
5809
|
// src/commands/voice/shared.ts
|
|
5408
|
-
import { homedir as
|
|
5409
|
-
import { dirname as dirname17, join as
|
|
5810
|
+
import { homedir as homedir5 } from "os";
|
|
5811
|
+
import { dirname as dirname17, join as join25 } from "path";
|
|
5410
5812
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
5411
5813
|
var __dirname5 = dirname17(fileURLToPath4(import.meta.url));
|
|
5412
|
-
var VOICE_DIR =
|
|
5814
|
+
var VOICE_DIR = join25(homedir5(), ".assist", "voice");
|
|
5413
5815
|
var voicePaths = {
|
|
5414
5816
|
dir: VOICE_DIR,
|
|
5415
|
-
pid:
|
|
5416
|
-
log:
|
|
5417
|
-
venv:
|
|
5418
|
-
lock:
|
|
5817
|
+
pid: join25(VOICE_DIR, "voice.pid"),
|
|
5818
|
+
log: join25(VOICE_DIR, "voice.log"),
|
|
5819
|
+
venv: join25(VOICE_DIR, ".venv"),
|
|
5820
|
+
lock: join25(VOICE_DIR, "voice.lock")
|
|
5419
5821
|
};
|
|
5420
5822
|
function getPythonDir() {
|
|
5421
|
-
return
|
|
5823
|
+
return join25(__dirname5, "commands", "voice", "python");
|
|
5422
5824
|
}
|
|
5423
5825
|
function getVenvPython() {
|
|
5424
|
-
return process.platform === "win32" ?
|
|
5826
|
+
return process.platform === "win32" ? join25(voicePaths.venv, "Scripts", "python.exe") : join25(voicePaths.venv, "bin", "python");
|
|
5425
5827
|
}
|
|
5426
5828
|
function getLockDir() {
|
|
5427
5829
|
const config = loadConfig();
|
|
5428
5830
|
return config.voice?.lockDir ?? VOICE_DIR;
|
|
5429
5831
|
}
|
|
5430
5832
|
function getLockFile() {
|
|
5431
|
-
return
|
|
5833
|
+
return join25(getLockDir(), "voice.lock");
|
|
5432
5834
|
}
|
|
5433
5835
|
|
|
5434
5836
|
// src/commands/voice/devices.ts
|
|
5435
5837
|
function devices() {
|
|
5436
|
-
const script =
|
|
5838
|
+
const script = join26(getPythonDir(), "list_devices.py");
|
|
5437
5839
|
spawnSync3(getVenvPython(), [script], { stdio: "inherit" });
|
|
5438
5840
|
}
|
|
5439
5841
|
|
|
5440
5842
|
// src/commands/voice/logs.ts
|
|
5441
|
-
import { existsSync as
|
|
5843
|
+
import { existsSync as existsSync25, readFileSync as readFileSync19 } from "fs";
|
|
5442
5844
|
function logs(options2) {
|
|
5443
|
-
if (!
|
|
5845
|
+
if (!existsSync25(voicePaths.log)) {
|
|
5444
5846
|
console.log("No voice log file found");
|
|
5445
5847
|
return;
|
|
5446
5848
|
}
|
|
5447
5849
|
const count = Number.parseInt(options2.lines ?? "150", 10);
|
|
5448
|
-
const content =
|
|
5850
|
+
const content = readFileSync19(voicePaths.log, "utf-8").trim();
|
|
5449
5851
|
if (!content) {
|
|
5450
5852
|
console.log("Voice log is empty");
|
|
5451
5853
|
return;
|
|
@@ -5467,13 +5869,13 @@ function logs(options2) {
|
|
|
5467
5869
|
|
|
5468
5870
|
// src/commands/voice/setup.ts
|
|
5469
5871
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
5470
|
-
import { mkdirSync as
|
|
5471
|
-
import { join as
|
|
5872
|
+
import { mkdirSync as mkdirSync9 } from "fs";
|
|
5873
|
+
import { join as join28 } from "path";
|
|
5472
5874
|
|
|
5473
5875
|
// src/commands/voice/checkLockFile.ts
|
|
5474
|
-
import { execSync as
|
|
5475
|
-
import { existsSync as
|
|
5476
|
-
import { join as
|
|
5876
|
+
import { execSync as execSync26 } from "child_process";
|
|
5877
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync8, readFileSync as readFileSync20, writeFileSync as writeFileSync20 } from "fs";
|
|
5878
|
+
import { join as join27 } from "path";
|
|
5477
5879
|
function isProcessAlive(pid) {
|
|
5478
5880
|
try {
|
|
5479
5881
|
process.kill(pid, 0);
|
|
@@ -5484,9 +5886,9 @@ function isProcessAlive(pid) {
|
|
|
5484
5886
|
}
|
|
5485
5887
|
function checkLockFile() {
|
|
5486
5888
|
const lockFile = getLockFile();
|
|
5487
|
-
if (!
|
|
5889
|
+
if (!existsSync26(lockFile)) return;
|
|
5488
5890
|
try {
|
|
5489
|
-
const lock = JSON.parse(
|
|
5891
|
+
const lock = JSON.parse(readFileSync20(lockFile, "utf-8"));
|
|
5490
5892
|
if (lock.pid && isProcessAlive(lock.pid)) {
|
|
5491
5893
|
console.error(
|
|
5492
5894
|
`Voice daemon already running (PID ${lock.pid}, env: ${lock.env}). Stop it first with: assist voice stop`
|
|
@@ -5497,10 +5899,10 @@ function checkLockFile() {
|
|
|
5497
5899
|
}
|
|
5498
5900
|
}
|
|
5499
5901
|
function bootstrapVenv() {
|
|
5500
|
-
if (
|
|
5902
|
+
if (existsSync26(getVenvPython())) return;
|
|
5501
5903
|
console.log("Setting up Python environment...");
|
|
5502
5904
|
const pythonDir = getPythonDir();
|
|
5503
|
-
|
|
5905
|
+
execSync26(
|
|
5504
5906
|
`uv sync --project "${pythonDir}" --extra runtime --no-install-project`,
|
|
5505
5907
|
{
|
|
5506
5908
|
stdio: "inherit",
|
|
@@ -5510,8 +5912,8 @@ function bootstrapVenv() {
|
|
|
5510
5912
|
}
|
|
5511
5913
|
function writeLockFile(pid) {
|
|
5512
5914
|
const lockFile = getLockFile();
|
|
5513
|
-
|
|
5514
|
-
|
|
5915
|
+
mkdirSync8(join27(lockFile, ".."), { recursive: true });
|
|
5916
|
+
writeFileSync20(
|
|
5515
5917
|
lockFile,
|
|
5516
5918
|
JSON.stringify({
|
|
5517
5919
|
pid,
|
|
@@ -5523,10 +5925,10 @@ function writeLockFile(pid) {
|
|
|
5523
5925
|
|
|
5524
5926
|
// src/commands/voice/setup.ts
|
|
5525
5927
|
function setup() {
|
|
5526
|
-
|
|
5928
|
+
mkdirSync9(voicePaths.dir, { recursive: true });
|
|
5527
5929
|
bootstrapVenv();
|
|
5528
5930
|
console.log("\nDownloading models...\n");
|
|
5529
|
-
const script =
|
|
5931
|
+
const script = join28(getPythonDir(), "setup_models.py");
|
|
5530
5932
|
const result = spawnSync4(getVenvPython(), [script], {
|
|
5531
5933
|
stdio: "inherit",
|
|
5532
5934
|
env: { ...process.env, VOICE_LOG_FILE: voicePaths.log }
|
|
@@ -5539,8 +5941,8 @@ function setup() {
|
|
|
5539
5941
|
|
|
5540
5942
|
// src/commands/voice/start.ts
|
|
5541
5943
|
import { spawn as spawn4 } from "child_process";
|
|
5542
|
-
import { mkdirSync as
|
|
5543
|
-
import { join as
|
|
5944
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync21 } from "fs";
|
|
5945
|
+
import { join as join29 } from "path";
|
|
5544
5946
|
|
|
5545
5947
|
// src/commands/voice/buildDaemonEnv.ts
|
|
5546
5948
|
function buildDaemonEnv(options2) {
|
|
@@ -5568,17 +5970,17 @@ function spawnBackground(python, script, env) {
|
|
|
5568
5970
|
console.error("Failed to start voice daemon");
|
|
5569
5971
|
process.exit(1);
|
|
5570
5972
|
}
|
|
5571
|
-
|
|
5973
|
+
writeFileSync21(voicePaths.pid, String(pid));
|
|
5572
5974
|
writeLockFile(pid);
|
|
5573
5975
|
console.log(`Voice daemon started (PID ${pid})`);
|
|
5574
5976
|
}
|
|
5575
5977
|
function start2(options2) {
|
|
5576
|
-
|
|
5978
|
+
mkdirSync10(voicePaths.dir, { recursive: true });
|
|
5577
5979
|
checkLockFile();
|
|
5578
5980
|
bootstrapVenv();
|
|
5579
5981
|
const debug = options2.debug || options2.foreground || process.platform === "win32";
|
|
5580
5982
|
const env = buildDaemonEnv({ debug });
|
|
5581
|
-
const script =
|
|
5983
|
+
const script = join29(getPythonDir(), "voice_daemon.py");
|
|
5582
5984
|
const python = getVenvPython();
|
|
5583
5985
|
if (options2.foreground) {
|
|
5584
5986
|
spawnForeground(python, script, env);
|
|
@@ -5588,7 +5990,7 @@ function start2(options2) {
|
|
|
5588
5990
|
}
|
|
5589
5991
|
|
|
5590
5992
|
// src/commands/voice/status.ts
|
|
5591
|
-
import { existsSync as
|
|
5993
|
+
import { existsSync as existsSync27, readFileSync as readFileSync21 } from "fs";
|
|
5592
5994
|
function isProcessAlive2(pid) {
|
|
5593
5995
|
try {
|
|
5594
5996
|
process.kill(pid, 0);
|
|
@@ -5598,16 +6000,16 @@ function isProcessAlive2(pid) {
|
|
|
5598
6000
|
}
|
|
5599
6001
|
}
|
|
5600
6002
|
function readRecentLogs(count) {
|
|
5601
|
-
if (!
|
|
5602
|
-
const lines =
|
|
6003
|
+
if (!existsSync27(voicePaths.log)) return [];
|
|
6004
|
+
const lines = readFileSync21(voicePaths.log, "utf-8").trim().split("\n");
|
|
5603
6005
|
return lines.slice(-count);
|
|
5604
6006
|
}
|
|
5605
6007
|
function status() {
|
|
5606
|
-
if (!
|
|
6008
|
+
if (!existsSync27(voicePaths.pid)) {
|
|
5607
6009
|
console.log("Voice daemon: not running (no PID file)");
|
|
5608
6010
|
return;
|
|
5609
6011
|
}
|
|
5610
|
-
const pid = Number.parseInt(
|
|
6012
|
+
const pid = Number.parseInt(readFileSync21(voicePaths.pid, "utf-8").trim(), 10);
|
|
5611
6013
|
const alive = isProcessAlive2(pid);
|
|
5612
6014
|
console.log(`Voice daemon: ${alive ? "running" : "dead"} (PID ${pid})`);
|
|
5613
6015
|
const recent = readRecentLogs(5);
|
|
@@ -5626,13 +6028,13 @@ function status() {
|
|
|
5626
6028
|
}
|
|
5627
6029
|
|
|
5628
6030
|
// src/commands/voice/stop.ts
|
|
5629
|
-
import { existsSync as
|
|
6031
|
+
import { existsSync as existsSync28, readFileSync as readFileSync22, unlinkSync as unlinkSync7 } from "fs";
|
|
5630
6032
|
function stop() {
|
|
5631
|
-
if (!
|
|
6033
|
+
if (!existsSync28(voicePaths.pid)) {
|
|
5632
6034
|
console.log("Voice daemon is not running (no PID file)");
|
|
5633
6035
|
return;
|
|
5634
6036
|
}
|
|
5635
|
-
const pid = Number.parseInt(
|
|
6037
|
+
const pid = Number.parseInt(readFileSync22(voicePaths.pid, "utf-8").trim(), 10);
|
|
5636
6038
|
try {
|
|
5637
6039
|
process.kill(pid, "SIGTERM");
|
|
5638
6040
|
console.log(`Sent SIGTERM to voice daemon (PID ${pid})`);
|
|
@@ -5645,7 +6047,7 @@ function stop() {
|
|
|
5645
6047
|
}
|
|
5646
6048
|
try {
|
|
5647
6049
|
const lockFile = getLockFile();
|
|
5648
|
-
if (
|
|
6050
|
+
if (existsSync28(lockFile)) unlinkSync7(lockFile);
|
|
5649
6051
|
} catch {
|
|
5650
6052
|
}
|
|
5651
6053
|
console.log("Voice daemon stopped");
|
|
@@ -5664,14 +6066,14 @@ function registerVoice(program2) {
|
|
|
5664
6066
|
|
|
5665
6067
|
// src/commands/roam/auth.ts
|
|
5666
6068
|
import { randomBytes } from "crypto";
|
|
5667
|
-
import
|
|
6069
|
+
import chalk52 from "chalk";
|
|
5668
6070
|
|
|
5669
6071
|
// src/lib/openBrowser.ts
|
|
5670
|
-
import { execSync as
|
|
6072
|
+
import { execSync as execSync27 } from "child_process";
|
|
5671
6073
|
function tryExec(commands) {
|
|
5672
6074
|
for (const cmd of commands) {
|
|
5673
6075
|
try {
|
|
5674
|
-
|
|
6076
|
+
execSync27(cmd);
|
|
5675
6077
|
return true;
|
|
5676
6078
|
} catch {
|
|
5677
6079
|
}
|
|
@@ -5839,13 +6241,13 @@ async function auth() {
|
|
|
5839
6241
|
saveGlobalConfig(config);
|
|
5840
6242
|
const state = randomBytes(16).toString("hex");
|
|
5841
6243
|
console.log(
|
|
5842
|
-
|
|
6244
|
+
chalk52.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
|
|
5843
6245
|
);
|
|
5844
|
-
console.log(
|
|
5845
|
-
console.log(
|
|
5846
|
-
console.log(
|
|
6246
|
+
console.log(chalk52.white("http://localhost:14523/callback\n"));
|
|
6247
|
+
console.log(chalk52.blue("Opening browser for authorization..."));
|
|
6248
|
+
console.log(chalk52.dim("Waiting for authorization callback..."));
|
|
5847
6249
|
const { code, redirectUri } = await authorizeInBrowser(clientId, state);
|
|
5848
|
-
console.log(
|
|
6250
|
+
console.log(chalk52.dim("Exchanging code for tokens..."));
|
|
5849
6251
|
const tokens = await exchangeToken({
|
|
5850
6252
|
code,
|
|
5851
6253
|
clientId,
|
|
@@ -5861,7 +6263,7 @@ async function auth() {
|
|
|
5861
6263
|
};
|
|
5862
6264
|
saveGlobalConfig(config);
|
|
5863
6265
|
console.log(
|
|
5864
|
-
|
|
6266
|
+
chalk52.green("Roam credentials and tokens saved to ~/.assist.yml")
|
|
5865
6267
|
);
|
|
5866
6268
|
}
|
|
5867
6269
|
|
|
@@ -5875,8 +6277,8 @@ function registerRoam(program2) {
|
|
|
5875
6277
|
import { spawn as spawn5 } from "child_process";
|
|
5876
6278
|
|
|
5877
6279
|
// src/commands/run/add.ts
|
|
5878
|
-
import { mkdirSync as
|
|
5879
|
-
import { join as
|
|
6280
|
+
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync22 } from "fs";
|
|
6281
|
+
import { join as join30 } from "path";
|
|
5880
6282
|
function findAddIndex() {
|
|
5881
6283
|
const addIndex = process.argv.indexOf("add");
|
|
5882
6284
|
if (addIndex === -1 || addIndex + 2 >= process.argv.length) return -1;
|
|
@@ -5930,16 +6332,16 @@ function saveNewRunConfig(name, command, args) {
|
|
|
5930
6332
|
saveConfig(config);
|
|
5931
6333
|
}
|
|
5932
6334
|
function createCommandFile(name) {
|
|
5933
|
-
const dir =
|
|
5934
|
-
|
|
6335
|
+
const dir = join30(".claude", "commands");
|
|
6336
|
+
mkdirSync11(dir, { recursive: true });
|
|
5935
6337
|
const content = `---
|
|
5936
6338
|
description: Run ${name}
|
|
5937
6339
|
---
|
|
5938
6340
|
|
|
5939
6341
|
Run \`assist run ${name} $ARGUMENTS 2>&1\`.
|
|
5940
6342
|
`;
|
|
5941
|
-
const filePath =
|
|
5942
|
-
|
|
6343
|
+
const filePath = join30(dir, `${name}.md`);
|
|
6344
|
+
writeFileSync22(filePath, content);
|
|
5943
6345
|
console.log(`Created command file: ${filePath}`);
|
|
5944
6346
|
}
|
|
5945
6347
|
function add2() {
|
|
@@ -6009,14 +6411,14 @@ function run2(name, args) {
|
|
|
6009
6411
|
}
|
|
6010
6412
|
|
|
6011
6413
|
// src/commands/statusLine.ts
|
|
6012
|
-
import
|
|
6414
|
+
import chalk53 from "chalk";
|
|
6013
6415
|
function formatNumber(num) {
|
|
6014
6416
|
return num.toLocaleString("en-US");
|
|
6015
6417
|
}
|
|
6016
6418
|
function colorizePercent(pct) {
|
|
6017
6419
|
const label2 = `${pct}%`;
|
|
6018
|
-
if (pct > 80) return
|
|
6019
|
-
if (pct > 40) return
|
|
6420
|
+
if (pct > 80) return chalk53.red(label2);
|
|
6421
|
+
if (pct > 40) return chalk53.yellow(label2);
|
|
6020
6422
|
return label2;
|
|
6021
6423
|
}
|
|
6022
6424
|
async function statusLine() {
|
|
@@ -6042,7 +6444,7 @@ import { fileURLToPath as fileURLToPath5 } from "url";
|
|
|
6042
6444
|
// src/commands/sync/syncClaudeMd.ts
|
|
6043
6445
|
import * as fs21 from "fs";
|
|
6044
6446
|
import * as path27 from "path";
|
|
6045
|
-
import
|
|
6447
|
+
import chalk54 from "chalk";
|
|
6046
6448
|
async function syncClaudeMd(claudeDir, targetBase) {
|
|
6047
6449
|
const source = path27.join(claudeDir, "CLAUDE.md");
|
|
6048
6450
|
const target = path27.join(targetBase, "CLAUDE.md");
|
|
@@ -6051,12 +6453,12 @@ async function syncClaudeMd(claudeDir, targetBase) {
|
|
|
6051
6453
|
const targetContent = fs21.readFileSync(target, "utf-8");
|
|
6052
6454
|
if (sourceContent !== targetContent) {
|
|
6053
6455
|
console.log(
|
|
6054
|
-
|
|
6456
|
+
chalk54.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
|
|
6055
6457
|
);
|
|
6056
6458
|
console.log();
|
|
6057
6459
|
printDiff(targetContent, sourceContent);
|
|
6058
6460
|
const confirm = await promptConfirm(
|
|
6059
|
-
|
|
6461
|
+
chalk54.red("Overwrite existing CLAUDE.md?"),
|
|
6060
6462
|
false
|
|
6061
6463
|
);
|
|
6062
6464
|
if (!confirm) {
|
|
@@ -6072,7 +6474,7 @@ async function syncClaudeMd(claudeDir, targetBase) {
|
|
|
6072
6474
|
// src/commands/sync/syncSettings.ts
|
|
6073
6475
|
import * as fs22 from "fs";
|
|
6074
6476
|
import * as path28 from "path";
|
|
6075
|
-
import
|
|
6477
|
+
import chalk55 from "chalk";
|
|
6076
6478
|
async function syncSettings(claudeDir, targetBase, options2) {
|
|
6077
6479
|
const source = path28.join(claudeDir, "settings.json");
|
|
6078
6480
|
const target = path28.join(targetBase, "settings.json");
|
|
@@ -6084,14 +6486,14 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
6084
6486
|
if (normalizedSource !== normalizedTarget) {
|
|
6085
6487
|
if (!options2?.yes) {
|
|
6086
6488
|
console.log(
|
|
6087
|
-
|
|
6489
|
+
chalk55.yellow(
|
|
6088
6490
|
"\n\u26A0\uFE0F Warning: settings.json differs from existing file"
|
|
6089
6491
|
)
|
|
6090
6492
|
);
|
|
6091
6493
|
console.log();
|
|
6092
6494
|
printDiff(targetContent, sourceContent);
|
|
6093
6495
|
const confirm = await promptConfirm(
|
|
6094
|
-
|
|
6496
|
+
chalk55.red("Overwrite existing settings.json?"),
|
|
6095
6497
|
false
|
|
6096
6498
|
);
|
|
6097
6499
|
if (!confirm) {
|
|
@@ -6128,7 +6530,7 @@ function syncCommands(claudeDir, targetBase) {
|
|
|
6128
6530
|
}
|
|
6129
6531
|
|
|
6130
6532
|
// src/commands/update.ts
|
|
6131
|
-
import { execSync as
|
|
6533
|
+
import { execSync as execSync28 } from "child_process";
|
|
6132
6534
|
import * as path30 from "path";
|
|
6133
6535
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
6134
6536
|
var __filename3 = fileURLToPath6(import.meta.url);
|
|
@@ -6138,7 +6540,7 @@ function getInstallDir() {
|
|
|
6138
6540
|
}
|
|
6139
6541
|
function isGitRepo(dir) {
|
|
6140
6542
|
try {
|
|
6141
|
-
const result =
|
|
6543
|
+
const result = execSync28("git rev-parse --show-toplevel", {
|
|
6142
6544
|
cwd: dir,
|
|
6143
6545
|
stdio: "pipe"
|
|
6144
6546
|
}).toString().trim();
|
|
@@ -6149,7 +6551,7 @@ function isGitRepo(dir) {
|
|
|
6149
6551
|
}
|
|
6150
6552
|
function isGlobalNpmInstall(dir) {
|
|
6151
6553
|
try {
|
|
6152
|
-
const globalPrefix =
|
|
6554
|
+
const globalPrefix = execSync28("npm prefix -g", { stdio: "pipe" }).toString().trim();
|
|
6153
6555
|
return path30.resolve(dir).toLowerCase().startsWith(path30.resolve(globalPrefix).toLowerCase());
|
|
6154
6556
|
} catch {
|
|
6155
6557
|
return false;
|
|
@@ -6160,16 +6562,16 @@ async function update() {
|
|
|
6160
6562
|
console.log(`Assist is installed at: ${installDir}`);
|
|
6161
6563
|
if (isGitRepo(installDir)) {
|
|
6162
6564
|
console.log("Detected git repo installation, pulling latest...");
|
|
6163
|
-
|
|
6565
|
+
execSync28("git pull", { cwd: installDir, stdio: "inherit" });
|
|
6164
6566
|
console.log("Building...");
|
|
6165
|
-
|
|
6567
|
+
execSync28("npm run build", { cwd: installDir, stdio: "inherit" });
|
|
6166
6568
|
console.log("Syncing commands...");
|
|
6167
|
-
|
|
6569
|
+
execSync28("assist sync", { stdio: "inherit" });
|
|
6168
6570
|
} else if (isGlobalNpmInstall(installDir)) {
|
|
6169
6571
|
console.log("Detected global npm installation, updating...");
|
|
6170
|
-
|
|
6572
|
+
execSync28("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
|
|
6171
6573
|
console.log("Syncing commands...");
|
|
6172
|
-
|
|
6574
|
+
execSync28("assist sync", { stdio: "inherit" });
|
|
6173
6575
|
} else {
|
|
6174
6576
|
console.error(
|
|
6175
6577
|
"Could not determine installation method. Expected a git repo or global npm install."
|
|
@@ -6203,6 +6605,8 @@ program.command("notify").description(
|
|
|
6203
6605
|
"Show notification from Claude Code hook (reads JSON from stdin)"
|
|
6204
6606
|
).action(notify);
|
|
6205
6607
|
program.command("update").description("Update assist to the latest version and sync commands").action(update);
|
|
6608
|
+
registerCliDiscover(program);
|
|
6609
|
+
registerCliHook(program);
|
|
6206
6610
|
registerPrs(program);
|
|
6207
6611
|
registerRoam(program);
|
|
6208
6612
|
registerBacklog(program);
|