@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/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.94.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 settingsPath = path9.join(process.cwd(), ".vscode", "settings.json");
1102
- fs3.writeFileSync(settingsPath, `${JSON.stringify(settings, null, " ")}
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 chalk35 from "chalk";
2920
+ import chalk36 from "chalk";
2514
2921
 
2515
2922
  // src/commands/complexity/cyclomatic.ts
2516
- import chalk31 from "chalk";
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 chalk30 from "chalk";
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 prefix = ts.isGetAccessor(node) ? "get " : "set ";
2599
- return `${prefix}${getIdentifierText(node.name)}`;
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(chalk30.yellow("No files found matching pattern"));
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 ? chalk31.red : chalk31.white;
2801
- console.log(`${color(`${file}:${name}`)} \u2192 ${chalk31.cyan(complexity)}`);
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
- chalk31.dim(
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 chalk32 from "chalk";
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 ? chalk32.red : chalk32.white;
3238
+ const color = exceedsThreshold ? chalk33.red : chalk33.white;
2832
3239
  console.log(
2833
- `${color(`${file}:${name}`)} \u2192 volume: ${chalk32.cyan(metrics.volume.toFixed(1))}, difficulty: ${chalk32.yellow(metrics.difficulty.toFixed(1))}, effort: ${chalk32.magenta(metrics.effort.toFixed(1))}`
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
- chalk32.dim(
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 chalk33 from "chalk";
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(chalk33.green("All files pass maintainability threshold"));
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 ? chalk33.red : chalk33.white;
3266
+ const color = threshold !== void 0 ? chalk34.red : chalk34.white;
2860
3267
  console.log(
2861
- `${color(file)} \u2192 avg: ${chalk33.cyan(avgMaintainability.toFixed(1))}, min: ${chalk33.yellow(minMaintainability.toFixed(1))}`
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(chalk33.dim(`
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
- chalk33.red(
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 ${chalk33.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.`
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 chalk34 from "chalk";
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 ? chalk34.red : chalk34.white;
2947
- console.log(`${color(file)} \u2192 ${chalk34.cyan(lines)} lines`);
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
- chalk34.dim(`
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(chalk35.yellow("No files found matching pattern"));
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(chalk35.bold.underline("SLOC"));
3377
+ console.log(chalk36.bold.underline("SLOC"));
2971
3378
  await sloc(file);
2972
3379
  console.log();
2973
- console.log(chalk35.bold.underline("Cyclomatic Complexity"));
3380
+ console.log(chalk36.bold.underline("Cyclomatic Complexity"));
2974
3381
  await cyclomatic(file);
2975
3382
  console.log();
2976
- console.log(chalk35.bold.underline("Halstead Metrics"));
3383
+ console.log(chalk36.bold.underline("Halstead Metrics"));
2977
3384
  await halstead(file);
2978
3385
  console.log();
2979
- console.log(chalk35.bold.underline("Maintainability Index"));
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 existsSync15, readFileSync as readFileSync12, writeFileSync as writeFileSync12 } from "fs";
3007
- import chalk36 from "chalk";
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 (!existsSync15(indexPath)) {
3016
- console.log(chalk36.yellow("No index.html found"));
3422
+ if (!existsSync17(indexPath)) {
3423
+ console.log(chalk37.yellow("No index.html found"));
3017
3424
  return;
3018
3425
  }
3019
- const content = readFileSync12(indexPath, "utf-8");
3426
+ const content = readFileSync14(indexPath, "utf-8");
3020
3427
  if (content.includes("window.location.pathname.endsWith('/')")) {
3021
- console.log(chalk36.dim("Trailing slash script already present"));
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(chalk36.red("Could not find </head> tag in index.html"));
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
- writeFileSync12(indexPath, newContent);
3031
- console.log(chalk36.green("Added trailing slash redirect to index.html"));
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 execSync14 } from "child_process";
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 execSync13 } from "child_process";
3047
- import chalk37 from "chalk";
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 readFileSync13 } from "fs";
3051
- import { homedir as homedir3 } from "os";
3052
- import { join as join11 } from "path";
3053
- var DEVLOG_DIR = join11(homedir3(), "git/blog/src/content/devlog");
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 = readFileSync13(join11(DEVLOG_DIR, file), "utf-8");
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 = execSync13(`git show --name-only --format="" ${hash}`, {
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(` ${chalk37.yellow(commit2.hash)} ${commit2.message}`);
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(` ${chalk37.dim(file)}`);
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 chalk38 from "chalk";
3546
+ import chalk39 from "chalk";
3140
3547
  function printDateHeader(date, isSkipped, entries) {
3141
3548
  if (isSkipped) {
3142
- console.log(`${chalk38.bold.blue(date)} ${chalk38.dim("skipped")}`);
3549
+ console.log(`${chalk39.bold.blue(date)} ${chalk39.dim("skipped")}`);
3143
3550
  } else if (entries && entries.length > 0) {
3144
- const entryInfo = entries.map((e) => `${chalk38.green(e.version)} ${e.title}`).join(" | ");
3145
- console.log(`${chalk38.bold.blue(date)} ${entryInfo}`);
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(`${chalk38.bold.blue(date)} ${chalk38.red("\u26A0 devlog missing")}`);
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 = execSync14(
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 execSync15 } from "child_process";
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 = execSync15(`git show ${hash}:package.json`, {
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 = execSync15(
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 execSync16 } from "child_process";
3250
- import chalk40 from "chalk";
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 chalk39 from "chalk";
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(`${chalk39.bold("version:")} ${stripToMinor(version2)}`);
3665
+ console.log(`${chalk40.bold("version:")} ${stripToMinor(version2)}`);
3259
3666
  } else {
3260
- console.log(`${chalk39.bold("version:")} ${chalk39.red("unknown")}`);
3667
+ console.log(`${chalk40.bold("version:")} ${chalk40.red("unknown")}`);
3261
3668
  }
3262
3669
  } else if (patchVersion && minorVersion) {
3263
3670
  console.log(
3264
- `${chalk39.bold("version:")} ${patchVersion} (patch) or ${minorVersion} (minor)`
3671
+ `${chalk40.bold("version:")} ${patchVersion} (patch) or ${minorVersion} (minor)`
3265
3672
  );
3266
3673
  } else {
3267
- console.log(`${chalk39.bold("version:")} v0.1 (initial)`);
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 = execSync16(
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(`${chalk40.bold("name:")} ${repoName}`);
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(chalk40.bold.blue(targetDate));
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(chalk40.dim(noCommitsMessage(!!lastInfo)));
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 chalk41 from "chalk";
3765
+ import chalk42 from "chalk";
3359
3766
  function skip(date) {
3360
3767
  if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
3361
- console.log(chalk41.red("Invalid date format. Use YYYY-MM-DD"));
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(chalk41.yellow(`${date} is already in skip list`));
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(chalk41.green(`Added ${date} to skip list`));
3785
+ console.log(chalk42.green(`Added ${date} to skip list`));
3379
3786
  }
3380
3787
 
3381
3788
  // src/commands/devlog/version.ts
3382
- import chalk42 from "chalk";
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(`${chalk42.bold("name:")} ${name}`);
3390
- console.log(`${chalk42.bold("last:")} ${lastVersion ?? chalk42.dim("none")}`);
3391
- console.log(`${chalk42.bold("next:")} ${nextVersion ?? chalk42.dim("none")}`);
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 writeFileSync13 } from "fs";
3816
+ import { unlinkSync as unlinkSync3, writeFileSync as writeFileSync15 } from "fs";
3410
3817
  import { tmpdir as tmpdir2 } from "os";
3411
- import { join as join12 } from "path";
3818
+ import { join as join14 } from "path";
3412
3819
 
3413
3820
  // src/commands/prs/shared.ts
3414
- import { execSync as execSync17 } from "child_process";
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
- execSync17("gh repo view --json owner,name", { encoding: "utf-8" })
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
- execSync17("gh pr view --json number", { encoding: "utf-8" })
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
- execSync17("gh pr view --json id", { encoding: "utf-8" })
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 = join12(tmpdir2(), `gh-query-${Date.now()}.graphql`);
3484
- writeFileSync13(queryFile, MUTATION);
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 execSync19 } from "child_process";
3929
+ import { execSync as execSync20 } from "child_process";
3523
3930
 
3524
3931
  // src/commands/prs/resolveCommentWithReply.ts
3525
- import { execSync as execSync18 } from "child_process";
3526
- import { unlinkSync as unlinkSync5, writeFileSync as writeFileSync14 } from "fs";
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 join14 } from "path";
3935
+ import { join as join16 } from "path";
3529
3936
 
3530
3937
  // src/commands/prs/loadCommentsCache.ts
3531
- import { existsSync as existsSync16, readFileSync as readFileSync14, unlinkSync as unlinkSync4 } from "fs";
3532
- import { join as join13 } from "path";
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 join13(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`);
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 (!existsSync16(cachePath)) {
3946
+ if (!existsSync18(cachePath)) {
3540
3947
  return null;
3541
3948
  }
3542
- const content = readFileSync14(cachePath, "utf-8");
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 (existsSync16(cachePath)) {
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
- execSync18(
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 = join14(tmpdir3(), `gh-mutation-${Date.now()}.graphql`);
3563
- writeFileSync14(queryFile, mutation);
3969
+ const queryFile = join16(tmpdir3(), `gh-mutation-${Date.now()}.graphql`);
3970
+ writeFileSync16(queryFile, mutation);
3564
3971
  try {
3565
- execSync18(
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 execSync19(`git rev-parse --verify ${sha}`, {
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 existsSync17, mkdirSync as mkdirSync4, writeFileSync as writeFileSync16 } from "fs";
3644
- import { join as join16 } from "path";
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 execSync20 } from "child_process";
3654
- import { unlinkSync as unlinkSync6, writeFileSync as writeFileSync15 } from "fs";
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 join15 } from "path";
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 = join15(tmpdir4(), `gh-query-${Date.now()}.graphql`);
3660
- writeFileSync15(queryFile, THREAD_QUERY);
4061
+ const queryFile = join17(tmpdir4(), `gh-query-${Date.now()}.graphql`);
4062
+ writeFileSync17(queryFile, THREAD_QUERY);
3661
4063
  try {
3662
- const result = execSync20(
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 execSync21 } from "child_process";
4086
+ import { execSync as execSync22 } from "child_process";
3685
4087
  function fetchJson(endpoint) {
3686
- const result = execSync21(`gh api --paginate ${endpoint}`, {
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 chalk43 from "chalk";
4130
+ import chalk44 from "chalk";
3729
4131
  function formatForHuman(comment2) {
3730
4132
  if (comment2.type === "review") {
3731
- const stateColor = comment2.state === "APPROVED" ? chalk43.green : comment2.state === "CHANGES_REQUESTED" ? chalk43.red : chalk43.yellow;
4133
+ const stateColor = comment2.state === "APPROVED" ? chalk44.green : comment2.state === "CHANGES_REQUESTED" ? chalk44.red : chalk44.yellow;
3732
4134
  return [
3733
- `${chalk43.cyan("Review")} by ${chalk43.bold(comment2.user)} ${stateColor(`[${comment2.state}]`)}`,
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
- `${chalk43.cyan("Line comment")} by ${chalk43.bold(comment2.user)} on ${chalk43.dim(`${comment2.path}${location}`)}`,
3741
- chalk43.dim(comment2.diff_hunk.split("\n").slice(-3).join("\n")),
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 = join16(process.cwd(), ".assist");
3765
- if (!existsSync17(assistDir)) {
3766
- mkdirSync4(assistDir, { recursive: true });
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 = join16(assistDir, `pr-${prNumber}-comments.yaml`);
3774
- writeFileSync16(cachePath, stringify(cacheData));
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 execSync22 } from "child_process";
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 chalk44 from "chalk";
4222
+ import chalk45 from "chalk";
3821
4223
  var STATUS_MAP = {
3822
- MERGED: (pr) => pr.mergedAt ? { label: chalk44.magenta("merged"), date: pr.mergedAt } : null,
3823
- CLOSED: (pr) => pr.closedAt ? { label: chalk44.red("closed"), date: pr.closedAt } : null
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: chalk44.green("opened"), date: pr.createdAt };
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 `${chalk44.cyan(`#${pr.number}`)} ${pr.title} ${chalk44.dim(`(${pr.author.login},`)} ${status2.label} ${chalk44.dim(`${formatDate(status2.date)})`)}`;
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
- chalk44.dim(` ${pr.changedFiles.toLocaleString()} files | ${pr.url}`)
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 = execSync22(
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 execSync23 } from "child_process";
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
- execSync23(`git cat-file -t ${sha}`, { stdio: "pipe" });
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 chalk45 from "chalk";
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(chalk45.red(`
4420
+ console.error(chalk46.red(`
4019
4421
  Refactor check failed:
4020
4422
  `));
4021
- console.error(chalk45.red(` The following files exceed ${maxLines} lines:
4423
+ console.error(chalk46.red(` The following files exceed ${maxLines} lines:
4022
4424
  `));
4023
4425
  for (const violation of violations) {
4024
- console.error(chalk45.red(` ${violation.file} (${violation.lines} lines)`));
4426
+ console.error(chalk46.red(` ${violation.file} (${violation.lines} lines)`));
4025
4427
  }
4026
4428
  console.error(
4027
- chalk45.yellow(
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(chalk45.gray(` assist refactor ignore <file>
4436
+ console.error(chalk46.gray(` assist refactor ignore <file>
4035
4437
  `));
4036
4438
  if (process.env.CLAUDECODE) {
4037
- console.error(chalk45.cyan(`
4439
+ console.error(chalk46.cyan(`
4038
4440
  ## Extracting Code to New Files
4039
4441
  `));
4040
4442
  console.error(
4041
- chalk45.cyan(
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
- chalk45.cyan(
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
- chalk45.cyan(
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 execSync24 } from "child_process";
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 = execSync24("git diff --cached --name-only", {
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 = execSync24("git diff --name-only", { encoding: "utf-8" });
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 chalk46 from "chalk";
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(chalk46.red(`Error: File does not exist: ${file}`));
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
- chalk46.green(
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 chalk49 from "chalk";
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 chalk47 from "chalk";
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(chalk47.bold("\nFile moves:"));
4889
+ console.log(chalk48.bold("\nFile moves:"));
4488
4890
  for (const move of plan.moves) {
4489
4891
  console.log(
4490
- ` ${chalk47.red(relPath(move.from))} \u2192 ${chalk47.green(relPath(move.to))}`
4892
+ ` ${chalk48.red(relPath(move.from))} \u2192 ${chalk48.green(relPath(move.to))}`
4491
4893
  );
4492
- console.log(chalk47.dim(` ${move.reason}`));
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(chalk47.bold(`
4900
+ console.log(chalk48.bold(`
4499
4901
  Import rewrites (${affectedFiles.size} files):`));
4500
4902
  for (const file of affectedFiles) {
4501
- console.log(` ${chalk47.cyan(relPath(file))}:`);
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
- ` ${chalk47.red(`"${oldSpecifier}"`)} \u2192 ${chalk47.green(`"${newSpecifier}"`)}`
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(chalk47.yellow("\nWarnings:"));
4514
- for (const w of plan.warnings) console.log(chalk47.yellow(` ${w}`));
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(chalk47.bold("\nNew directories:"));
4919
+ console.log(chalk48.bold("\nNew directories:"));
4518
4920
  for (const dir of plan.newDirectories)
4519
- console.log(chalk47.green(` ${dir}/`));
4921
+ console.log(chalk48.green(` ${dir}/`));
4520
4922
  }
4521
4923
  displayMoves(plan);
4522
4924
  displayRewrites(plan.rewrites);
4523
4925
  console.log(
4524
- chalk47.dim(
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 chalk48 from "chalk";
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
- chalk48.cyan(` Rewrote imports in ${path23.relative(process.cwd(), file)}`)
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(chalk48.green(` Created ${path23.relative(process.cwd(), dir)}/`));
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
- chalk48.white(
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
- chalk48.dim(
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(chalk49.yellow("No files found matching pattern"));
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(chalk49.green("No restructuring needed"));
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(chalk49.bold("\nApplying changes..."));
5113
+ console.log(chalk50.bold("\nApplying changes..."));
4712
5114
  executePlan(plan);
4713
- console.log(chalk49.green("\nRestructuring complete"));
5115
+ console.log(chalk50.green("\nRestructuring complete"));
4714
5116
  } else {
4715
- console.log(chalk49.dim("\nDry run. Use --apply to execute."));
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 existsSync18, readdirSync as readdirSync2, statSync } from "fs";
4739
- import { basename as basename4, join as join17, relative } from "path";
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 (!existsSync18(dir)) return [];
5156
+ if (!existsSync20(dir)) return [];
4755
5157
  const results = [];
4756
5158
  for (const entry of readdirSync2(dir)) {
4757
- const fullPath = join17(dir, entry);
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 existsSync20 } from "fs";
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 join19 } from "path";
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 join18 } from "path";
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, prefix) {
4872
- const newFilename = `${prefix}.${vttFile}`;
4873
- renameSync(join18(vttDir, vttFile), join18(vttDir, newFilename));
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 prefix = await resolveDate(rl, choice);
5292
+ const prefix2 = await resolveDate(rl, choice);
4891
5293
  rl.close();
4892
- return prefix ? renameWithPrefix(vttDir, vttFile, prefix) : null;
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 = join19(
5309
+ const newRelativePath = join21(
4908
5310
  dirname13(vttFile.relativePath),
4909
5311
  newFilename
4910
5312
  );
4911
5313
  vttFiles[i] = {
4912
- absolutePath: join19(vttFileDir, newFilename),
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 existsSync19, mkdirSync as mkdirSync5, readFileSync as readFileSync15, writeFileSync as writeFileSync17 } from "fs";
4926
- import { basename as basename5, dirname as dirname14, join as join20 } from "path";
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 prefix = nextWords.slice(0, j).join(" ");
4997
- if (suffix === prefix) return j;
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 : join20(transcriptsDir, relativeDir);
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 = join20(outputDir, mdFile);
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): ${join20(relativeDir, mdFile)}`);
5548
+ console.log(`Skipping (already exists): ${join22(relativeDir, mdFile)}`);
5147
5549
  return "skipped";
5148
5550
  }
5149
5551
  function ensureDirectory(dir, label2) {
5150
- if (!existsSync19(dir)) {
5151
- mkdirSync5(dir, { recursive: true });
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(readFileSync15(inputPath, "utf-8"));
5575
+ return processCues(readFileSync17(inputPath, "utf-8"));
5174
5576
  }
5175
5577
  function writeFormatted(outputPath, content) {
5176
- writeFileSync17(outputPath, content, "utf-8");
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 (existsSync19(paths.outputPath))
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 (!existsSync20(vttDir)) {
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 existsSync22 } from "fs";
5245
- import { basename as basename6, dirname as dirname16, join as join22, relative as relative2 } from "path";
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 existsSync21,
5250
- mkdirSync as mkdirSync6,
5251
- readFileSync as readFileSync16,
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 join21 } from "path";
5657
+ import { dirname as dirname15, join as join23 } from "path";
5256
5658
 
5257
5659
  // src/commands/transcript/summarise/processStagedFile/validateStagedContent.ts
5258
- import chalk50 from "chalk";
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
- chalk50.red(
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
- chalk50.red(
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 = join21(process.cwd(), ".assist", "transcript");
5686
+ var STAGING_DIR = join23(process.cwd(), ".assist", "transcript");
5285
5687
  function processStagedFile() {
5286
- if (!existsSync21(STAGING_DIR)) {
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 = readFileSync16(stagedFile.absolutePath, "utf-8");
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 = join21(summaryDir, matchingTranscript.relativePath);
5710
+ const destPath = join23(summaryDir, matchingTranscript.relativePath);
5309
5711
  const destDir = dirname15(destPath);
5310
- if (!existsSync21(destDir)) {
5311
- mkdirSync6(destDir, { recursive: true });
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 : join22(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 (!existsSync22(transcriptsDir)) {
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 = join22(STAGING_DIR, outputFilename);
5359
- const summaryFileDir = join22(summaryDir, dirname16(next2.relativePath));
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 join24 } from "path";
5807
+ import { join as join26 } from "path";
5406
5808
 
5407
5809
  // src/commands/voice/shared.ts
5408
- import { homedir as homedir4 } from "os";
5409
- import { dirname as dirname17, join as join23 } from "path";
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 = join23(homedir4(), ".assist", "voice");
5814
+ var VOICE_DIR = join25(homedir5(), ".assist", "voice");
5413
5815
  var voicePaths = {
5414
5816
  dir: VOICE_DIR,
5415
- pid: join23(VOICE_DIR, "voice.pid"),
5416
- log: join23(VOICE_DIR, "voice.log"),
5417
- venv: join23(VOICE_DIR, ".venv"),
5418
- lock: join23(VOICE_DIR, "voice.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 join23(__dirname5, "commands", "voice", "python");
5823
+ return join25(__dirname5, "commands", "voice", "python");
5422
5824
  }
5423
5825
  function getVenvPython() {
5424
- return process.platform === "win32" ? join23(voicePaths.venv, "Scripts", "python.exe") : join23(voicePaths.venv, "bin", "python");
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 join23(getLockDir(), "voice.lock");
5833
+ return join25(getLockDir(), "voice.lock");
5432
5834
  }
5433
5835
 
5434
5836
  // src/commands/voice/devices.ts
5435
5837
  function devices() {
5436
- const script = join24(getPythonDir(), "list_devices.py");
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 existsSync23, readFileSync as readFileSync17 } from "fs";
5843
+ import { existsSync as existsSync25, readFileSync as readFileSync19 } from "fs";
5442
5844
  function logs(options2) {
5443
- if (!existsSync23(voicePaths.log)) {
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 = readFileSync17(voicePaths.log, "utf-8").trim();
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 mkdirSync8 } from "fs";
5471
- import { join as join26 } from "path";
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 execSync25 } from "child_process";
5475
- import { existsSync as existsSync24, mkdirSync as mkdirSync7, readFileSync as readFileSync18, writeFileSync as writeFileSync18 } from "fs";
5476
- import { join as join25 } from "path";
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 (!existsSync24(lockFile)) return;
5889
+ if (!existsSync26(lockFile)) return;
5488
5890
  try {
5489
- const lock = JSON.parse(readFileSync18(lockFile, "utf-8"));
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 (existsSync24(getVenvPython())) return;
5902
+ if (existsSync26(getVenvPython())) return;
5501
5903
  console.log("Setting up Python environment...");
5502
5904
  const pythonDir = getPythonDir();
5503
- execSync25(
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
- mkdirSync7(join25(lockFile, ".."), { recursive: true });
5514
- writeFileSync18(
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
- mkdirSync8(voicePaths.dir, { recursive: true });
5928
+ mkdirSync9(voicePaths.dir, { recursive: true });
5527
5929
  bootstrapVenv();
5528
5930
  console.log("\nDownloading models...\n");
5529
- const script = join26(getPythonDir(), "setup_models.py");
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 mkdirSync9, writeFileSync as writeFileSync19 } from "fs";
5543
- import { join as join27 } from "path";
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
- writeFileSync19(voicePaths.pid, String(pid));
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
- mkdirSync9(voicePaths.dir, { recursive: true });
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 = join27(getPythonDir(), "voice_daemon.py");
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 existsSync25, readFileSync as readFileSync19 } from "fs";
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 (!existsSync25(voicePaths.log)) return [];
5602
- const lines = readFileSync19(voicePaths.log, "utf-8").trim().split("\n");
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 (!existsSync25(voicePaths.pid)) {
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(readFileSync19(voicePaths.pid, "utf-8").trim(), 10);
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 existsSync26, readFileSync as readFileSync20, unlinkSync as unlinkSync7 } from "fs";
6031
+ import { existsSync as existsSync28, readFileSync as readFileSync22, unlinkSync as unlinkSync7 } from "fs";
5630
6032
  function stop() {
5631
- if (!existsSync26(voicePaths.pid)) {
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(readFileSync20(voicePaths.pid, "utf-8").trim(), 10);
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 (existsSync26(lockFile)) unlinkSync7(lockFile);
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 chalk51 from "chalk";
6069
+ import chalk52 from "chalk";
5668
6070
 
5669
6071
  // src/lib/openBrowser.ts
5670
- import { execSync as execSync26 } from "child_process";
6072
+ import { execSync as execSync27 } from "child_process";
5671
6073
  function tryExec(commands) {
5672
6074
  for (const cmd of commands) {
5673
6075
  try {
5674
- execSync26(cmd);
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
- chalk51.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
6244
+ chalk52.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
5843
6245
  );
5844
- console.log(chalk51.white("http://localhost:14523/callback\n"));
5845
- console.log(chalk51.blue("Opening browser for authorization..."));
5846
- console.log(chalk51.dim("Waiting for authorization callback..."));
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(chalk51.dim("Exchanging code for tokens..."));
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
- chalk51.green("Roam credentials and tokens saved to ~/.assist.yml")
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 mkdirSync10, writeFileSync as writeFileSync20 } from "fs";
5879
- import { join as join28 } from "path";
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 = join28(".claude", "commands");
5934
- mkdirSync10(dir, { recursive: true });
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 = join28(dir, `${name}.md`);
5942
- writeFileSync20(filePath, content);
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 chalk52 from "chalk";
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 chalk52.red(label2);
6019
- if (pct > 40) return chalk52.yellow(label2);
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 chalk53 from "chalk";
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
- chalk53.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
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
- chalk53.red("Overwrite existing CLAUDE.md?"),
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 chalk54 from "chalk";
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
- chalk54.yellow(
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
- chalk54.red("Overwrite existing settings.json?"),
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 execSync27 } from "child_process";
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 = execSync27("git rev-parse --show-toplevel", {
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 = execSync27("npm prefix -g", { stdio: "pipe" }).toString().trim();
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
- execSync27("git pull", { cwd: installDir, stdio: "inherit" });
6565
+ execSync28("git pull", { cwd: installDir, stdio: "inherit" });
6164
6566
  console.log("Building...");
6165
- execSync27("npm run build", { cwd: installDir, stdio: "inherit" });
6567
+ execSync28("npm run build", { cwd: installDir, stdio: "inherit" });
6166
6568
  console.log("Syncing commands...");
6167
- execSync27("assist sync", { stdio: "inherit" });
6569
+ execSync28("assist sync", { stdio: "inherit" });
6168
6570
  } else if (isGlobalNpmInstall(installDir)) {
6169
6571
  console.log("Detected global npm installation, updating...");
6170
- execSync27("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
6572
+ execSync28("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
6171
6573
  console.log("Syncing commands...");
6172
- execSync27("assist sync", { stdio: "inherit" });
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);