@staff0rd/assist 0.107.0 → 0.108.1

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.107.0",
9
+ version: "0.108.1",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -40,6 +40,7 @@ var package_default = {
40
40
  commander: "^14.0.2",
41
41
  diff: "^8.0.2",
42
42
  enquirer: "^2.4.1",
43
+ entities: "^7.0.1",
43
44
  "is-wsl": "^3.1.0",
44
45
  minimatch: "^10.1.1",
45
46
  "node-notifier": "^10.0.1",
@@ -156,6 +157,9 @@ var assistConfigSchema = z.strictObject({
156
157
  run: z.array(runConfigSchema).optional(),
157
158
  transcript: transcriptConfigSchema.optional(),
158
159
  cliReadVerbs: z.record(z.string(), z.array(z.string())).optional(),
160
+ news: z.strictObject({
161
+ feeds: z.array(z.string()).default([])
162
+ }).default({ feeds: [] }),
159
163
  voice: z.strictObject({
160
164
  wakeWords: z.array(z.string()).default(DEFAULT_WAKE_WORDS),
161
165
  mic: z.string().optional(),
@@ -2346,15 +2350,63 @@ async function start(id) {
2346
2350
  }
2347
2351
  }
2348
2352
 
2349
- // src/commands/backlog/web/index.ts
2353
+ // src/shared/web.ts
2350
2354
  import { exec } from "child_process";
2351
- import { createServer } from "http";
2352
- import chalk29 from "chalk";
2353
-
2354
- // src/commands/backlog/web/handleRequest.ts
2355
2355
  import { readFileSync as readFileSync12 } from "fs";
2356
+ import {
2357
+ createServer
2358
+ } from "http";
2356
2359
  import { dirname as dirname11, join as join10 } from "path";
2357
2360
  import { fileURLToPath as fileURLToPath3 } from "url";
2361
+ import chalk29 from "chalk";
2362
+ function respondJson(res, status2, data) {
2363
+ res.writeHead(status2, { "Content-Type": "application/json" });
2364
+ res.end(JSON.stringify(data));
2365
+ }
2366
+ function createBundleHandler(importMetaUrl, bundlePath) {
2367
+ const dir = dirname11(fileURLToPath3(importMetaUrl));
2368
+ let cache;
2369
+ return (_req, res) => {
2370
+ if (!cache) {
2371
+ cache = readFileSync12(join10(dir, bundlePath), "utf-8");
2372
+ }
2373
+ res.writeHead(200, { "Content-Type": "application/javascript" });
2374
+ res.end(cache);
2375
+ };
2376
+ }
2377
+ function createHtmlHandler(getHtml3) {
2378
+ return (_req, res) => {
2379
+ res.writeHead(200, { "Content-Type": "text/html" });
2380
+ res.end(getHtml3());
2381
+ };
2382
+ }
2383
+ function parseRoute(req, port) {
2384
+ const url = new URL(req.url ?? "/", `http://localhost:${port}`);
2385
+ return { method: req.method ?? "GET", pathname: url.pathname };
2386
+ }
2387
+ function createRouteHandler(routes3) {
2388
+ return async (req, res, port) => {
2389
+ const { method, pathname } = parseRoute(req, port);
2390
+ const handler = routes3[`${method} ${pathname}`];
2391
+ if (handler) {
2392
+ await handler(req, res);
2393
+ return;
2394
+ }
2395
+ res.writeHead(404);
2396
+ res.end();
2397
+ };
2398
+ }
2399
+ function startWebServer(label2, port, handler) {
2400
+ const url = `http://localhost:${port}`;
2401
+ const server = createServer((req, res) => {
2402
+ handler(req, res, port);
2403
+ });
2404
+ server.listen(port, () => {
2405
+ console.log(chalk29.green(`${label2}: ${url}`));
2406
+ console.log(chalk29.dim("Press Ctrl+C to stop"));
2407
+ exec(`open ${url}`);
2408
+ });
2409
+ }
2358
2410
 
2359
2411
  // src/commands/backlog/web/getHtml.ts
2360
2412
  function getHtml() {
@@ -2379,11 +2431,7 @@ function getHtml() {
2379
2431
  </html>`;
2380
2432
  }
2381
2433
 
2382
- // src/commands/backlog/web/respondJson.ts
2383
- function respondJson(res, status2, data) {
2384
- res.writeHead(status2, { "Content-Type": "application/json" });
2385
- res.end(JSON.stringify(data));
2386
- }
2434
+ // src/commands/backlog/web/parseItemBody.ts
2387
2435
  function readBody(req) {
2388
2436
  return new Promise((resolve6, reject) => {
2389
2437
  let body = "";
@@ -2451,25 +2499,12 @@ async function updateItem(req, res, id) {
2451
2499
  }
2452
2500
 
2453
2501
  // src/commands/backlog/web/handleRequest.ts
2454
- var __dirname4 = dirname11(fileURLToPath3(import.meta.url));
2455
- var bundleCache;
2456
- function serveBundle(_req, res) {
2457
- if (!bundleCache) {
2458
- bundleCache = readFileSync12(
2459
- join10(__dirname4, "commands/backlog/web/bundle.js"),
2460
- "utf-8"
2461
- );
2462
- }
2463
- res.writeHead(200, { "Content-Type": "application/javascript" });
2464
- res.end(bundleCache);
2465
- }
2466
- function serveHtml(_req, res) {
2467
- res.writeHead(200, { "Content-Type": "text/html" });
2468
- res.end(getHtml());
2469
- }
2470
2502
  var routes = {
2471
- "GET /": serveHtml,
2472
- "GET /bundle.js": serveBundle,
2503
+ "GET /": createHtmlHandler(getHtml),
2504
+ "GET /bundle.js": createBundleHandler(
2505
+ import.meta.url,
2506
+ "commands/backlog/web/bundle.js"
2507
+ ),
2473
2508
  "GET /api/items": listItems,
2474
2509
  "POST /api/items": createItem
2475
2510
  };
@@ -2478,17 +2513,11 @@ var itemRoutes = {
2478
2513
  PUT: (req, res, id) => updateItem(req, res, id),
2479
2514
  DELETE: (_req, res, id) => deleteItem(res, id)
2480
2515
  };
2481
- function parseRoute(req, port) {
2482
- const url = new URL(req.url ?? "/", `http://localhost:${port}`);
2483
- return { method: req.method ?? "GET", pathname: url.pathname };
2484
- }
2516
+ var baseHandler = createRouteHandler(routes);
2485
2517
  async function handleRequest(req, res, port) {
2486
- const { method, pathname } = parseRoute(req, port);
2487
- const handler = routes[`${method} ${pathname}`];
2488
- if (handler) {
2489
- await handler(req, res);
2490
- return;
2491
- }
2518
+ const url = new URL(req.url ?? "/", `http://localhost:${port}`);
2519
+ const method = req.method ?? "GET";
2520
+ const pathname = url.pathname;
2492
2521
  const itemMatch = pathname.match(/^\/api\/items\/(\d+)$/);
2493
2522
  if (itemMatch) {
2494
2523
  const itemHandler = itemRoutes[method];
@@ -2497,22 +2526,16 @@ async function handleRequest(req, res, port) {
2497
2526
  return;
2498
2527
  }
2499
2528
  }
2500
- res.writeHead(404);
2501
- res.end();
2529
+ await baseHandler(req, res, port);
2502
2530
  }
2503
2531
 
2504
2532
  // src/commands/backlog/web/index.ts
2505
2533
  async function web(options2) {
2506
- const port = Number.parseInt(options2.port, 10);
2507
- const url = `http://localhost:${port}`;
2508
- const server = createServer((req, res) => {
2509
- handleRequest(req, res, port);
2510
- });
2511
- server.listen(port, () => {
2512
- console.log(chalk29.green(`Backlog web view: ${url}`));
2513
- console.log(chalk29.dim("Press Ctrl+C to stop"));
2514
- exec(`open ${url}`);
2515
- });
2534
+ startWebServer(
2535
+ "Backlog web view",
2536
+ Number.parseInt(options2.port, 10),
2537
+ handleRequest
2538
+ );
2516
2539
  }
2517
2540
 
2518
2541
  // src/commands/registerBacklog.ts
@@ -2613,9 +2636,9 @@ import { existsSync as existsSync16, readFileSync as readFileSync13, writeFileSy
2613
2636
  import { dirname as dirname12, resolve as resolve2 } from "path";
2614
2637
  import { fileURLToPath as fileURLToPath4 } from "url";
2615
2638
  var __filename2 = fileURLToPath4(import.meta.url);
2616
- var __dirname5 = dirname12(__filename2);
2639
+ var __dirname4 = dirname12(__filename2);
2617
2640
  function getCliReadsPath() {
2618
- return resolve2(__dirname5, "..", "assist.cli-reads");
2641
+ return resolve2(__dirname4, "..", "assist.cli-reads");
2619
2642
  }
2620
2643
  var cachedLines;
2621
2644
  function getCliReadsLines() {
@@ -2834,9 +2857,9 @@ import { execSync as execSync13 } from "child_process";
2834
2857
  import { dirname as dirname13, resolve as resolve4 } from "path";
2835
2858
  import { fileURLToPath as fileURLToPath5 } from "url";
2836
2859
  var __filename3 = fileURLToPath5(import.meta.url);
2837
- var __dirname6 = dirname13(__filename3);
2860
+ var __dirname5 = dirname13(__filename3);
2838
2861
  function getInstallDir() {
2839
- return resolve4(__dirname6, "..");
2862
+ return resolve4(__dirname5, "..");
2840
2863
  }
2841
2864
  function isGitRepo(dir) {
2842
2865
  try {
@@ -4614,6 +4637,230 @@ function registerNetframework(program2) {
4614
4637
  cmd.command("in-sln").description("Check whether a .csproj is referenced by any .sln file").argument("<csproj>", "Path to a .csproj file").action(inSln);
4615
4638
  }
4616
4639
 
4640
+ // src/commands/news/add/index.ts
4641
+ import chalk49 from "chalk";
4642
+ import enquirer5 from "enquirer";
4643
+ async function add2(url) {
4644
+ if (!url) {
4645
+ const response = await enquirer5.prompt({
4646
+ type: "input",
4647
+ name: "url",
4648
+ message: "RSS feed URL:",
4649
+ validate: (value) => {
4650
+ try {
4651
+ new URL(value);
4652
+ return true;
4653
+ } catch {
4654
+ return "Please enter a valid URL";
4655
+ }
4656
+ }
4657
+ });
4658
+ url = response.url;
4659
+ }
4660
+ const config = loadGlobalConfigRaw();
4661
+ const news = config.news ?? {};
4662
+ const feeds = news.feeds ?? [];
4663
+ if (feeds.includes(url)) {
4664
+ console.log(chalk49.yellow("Feed already exists in config"));
4665
+ return;
4666
+ }
4667
+ feeds.push(url);
4668
+ config.news = { ...news, feeds };
4669
+ saveGlobalConfig(config);
4670
+ console.log(chalk49.green(`Added feed: ${url}`));
4671
+ }
4672
+
4673
+ // src/commands/news/web/handleRequest.ts
4674
+ import chalk50 from "chalk";
4675
+
4676
+ // src/commands/news/web/shared.ts
4677
+ import { decodeHTML } from "entities";
4678
+ function extractText(xml, tag) {
4679
+ const cdataMatch = xml.match(
4680
+ new RegExp(`<${tag}>\\s*<!\\[CDATA\\[([\\s\\S]*?)\\]\\]>\\s*</${tag}>`)
4681
+ );
4682
+ if (cdataMatch) return cdataMatch[1].trim();
4683
+ const match = xml.match(new RegExp(`<${tag}[^>]*>([\\s\\S]*?)</${tag}>`));
4684
+ return match ? match[1].trim() : "";
4685
+ }
4686
+ function extractLink(itemXml) {
4687
+ const atomLink = itemXml.match(
4688
+ /<link[^>]*rel=["']alternate["'][^>]*href=["']([^"']+)["']/
4689
+ );
4690
+ if (atomLink) return atomLink[1];
4691
+ const atomLink2 = itemXml.match(/<link[^>]*href=["']([^"']+)["']/);
4692
+ if (atomLink2) return atomLink2[1];
4693
+ return extractText(itemXml, "link");
4694
+ }
4695
+ function parseDate(dateStr) {
4696
+ if (!dateStr) return (/* @__PURE__ */ new Date(0)).toISOString();
4697
+ try {
4698
+ return new Date(dateStr).toISOString();
4699
+ } catch {
4700
+ return (/* @__PURE__ */ new Date(0)).toISOString();
4701
+ }
4702
+ }
4703
+ function stripHtml(html) {
4704
+ const decoded = decodeHTML(html);
4705
+ const stripped = decoded.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
4706
+ return decodeHTML(stripped);
4707
+ }
4708
+ function matchAll(xml, regex) {
4709
+ const results = [];
4710
+ for (const m of xml.matchAll(regex)) {
4711
+ results.push(m[1]);
4712
+ }
4713
+ return results;
4714
+ }
4715
+ var MAX_EXCERPT = 500;
4716
+ function excerpt(xml, ...tags) {
4717
+ for (const tag of tags) {
4718
+ const raw = extractText(xml, tag);
4719
+ if (!raw) continue;
4720
+ const text = stripHtml(raw);
4721
+ if (text.length <= MAX_EXCERPT) return text;
4722
+ return `${text.slice(0, MAX_EXCERPT)}\u2026`;
4723
+ }
4724
+ return "";
4725
+ }
4726
+
4727
+ // src/commands/news/web/parseFeed.ts
4728
+ function parseRss(xml, feedOrigin) {
4729
+ const feedTitle = extractText(xml, "title");
4730
+ return matchAll(xml, /<item[\s>]([\s\S]*?)<\/item>/g).map((itemXml) => ({
4731
+ title: extractText(itemXml, "title"),
4732
+ link: extractLink(itemXml),
4733
+ pubDate: parseDate(
4734
+ extractText(itemXml, "pubDate") || extractText(itemXml, "dc:date")
4735
+ ),
4736
+ feedTitle,
4737
+ feedOrigin,
4738
+ excerpt: excerpt(itemXml, "description", "content:encoded")
4739
+ }));
4740
+ }
4741
+ function parseAtom(xml, feedOrigin) {
4742
+ const feedTitle = extractText(xml, "title");
4743
+ return matchAll(xml, /<entry[\s>]([\s\S]*?)<\/entry>/g).map((entryXml) => ({
4744
+ title: extractText(entryXml, "title"),
4745
+ link: extractLink(entryXml),
4746
+ pubDate: parseDate(
4747
+ extractText(entryXml, "published") || extractText(entryXml, "updated")
4748
+ ),
4749
+ feedTitle,
4750
+ feedOrigin,
4751
+ excerpt: excerpt(entryXml, "summary", "content")
4752
+ }));
4753
+ }
4754
+ function parseFeed(xml, feedOrigin) {
4755
+ if (xml.includes("<feed")) return parseAtom(xml, feedOrigin);
4756
+ return parseRss(xml, feedOrigin);
4757
+ }
4758
+
4759
+ // src/commands/news/web/fetchFeeds.ts
4760
+ async function fetchFeeds(urls, onProgress) {
4761
+ let done2 = 0;
4762
+ const results = await Promise.allSettled(
4763
+ urls.map(async (url) => {
4764
+ const origin = new URL(url).origin;
4765
+ const res = await fetch(url);
4766
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
4767
+ const items2 = parseFeed(await res.text(), origin);
4768
+ done2++;
4769
+ onProgress?.(done2, urls.length);
4770
+ return items2;
4771
+ })
4772
+ );
4773
+ const items = [];
4774
+ for (const result of results) {
4775
+ if (result.status === "fulfilled") {
4776
+ items.push(...result.value);
4777
+ }
4778
+ }
4779
+ items.sort(
4780
+ (a, b) => new Date(b.pubDate).getTime() - new Date(a.pubDate).getTime()
4781
+ );
4782
+ return items;
4783
+ }
4784
+
4785
+ // src/commands/news/web/getHtml.ts
4786
+ function getHtml2() {
4787
+ return `<!DOCTYPE html>
4788
+ <html lang="en" class="dark">
4789
+ <head>
4790
+ <meta charset="UTF-8">
4791
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
4792
+ <title>News</title>
4793
+ <script src="https://cdn.tailwindcss.com"></script>
4794
+ <script>tailwind.config={darkMode:'class'}</script>
4795
+ </head>
4796
+ <body class="font-[-apple-system,BlinkMacSystemFont,'Segoe_UI',Roboto,sans-serif] bg-gray-950 text-gray-200 leading-normal">
4797
+ <div class="max-w-3xl mx-auto px-4 py-6" id="app"></div>
4798
+ <script src="/bundle.js"></script>
4799
+ </body>
4800
+ </html>`;
4801
+ }
4802
+
4803
+ // src/commands/news/web/handleRequest.ts
4804
+ var cachedItems;
4805
+ var prefetchPromise;
4806
+ function prefetch() {
4807
+ const config = loadConfig();
4808
+ const total = config.news.feeds.length;
4809
+ if (total === 0) return;
4810
+ process.stdout.write(chalk50.dim(`Fetching ${total} feed(s)\u2026 `));
4811
+ prefetchPromise = fetchFeeds(config.news.feeds, (done2, t) => {
4812
+ const width = 20;
4813
+ const filled = Math.round(done2 / t * width);
4814
+ const bar = `${"\u2588".repeat(filled)}${"\u2591".repeat(width - filled)}`;
4815
+ process.stdout.write(
4816
+ `\r${chalk50.dim(`Fetching feeds ${bar} ${done2}/${t}`)}`
4817
+ );
4818
+ }).then((items) => {
4819
+ process.stdout.write(
4820
+ `\r${chalk50.green(`Fetched ${items.length} items from ${total} feed(s)`)}
4821
+ `
4822
+ );
4823
+ cachedItems = items;
4824
+ return items;
4825
+ });
4826
+ }
4827
+ async function listItems2(_req, res) {
4828
+ if (!cachedItems && prefetchPromise) {
4829
+ await prefetchPromise;
4830
+ }
4831
+ if (!cachedItems) {
4832
+ const config = loadConfig();
4833
+ cachedItems = await fetchFeeds(config.news.feeds);
4834
+ }
4835
+ respondJson(res, 200, cachedItems);
4836
+ }
4837
+ var routes2 = {
4838
+ "GET /": createHtmlHandler(getHtml2),
4839
+ "GET /bundle.js": createBundleHandler(
4840
+ import.meta.url,
4841
+ "commands/news/web/bundle.js"
4842
+ ),
4843
+ "GET /api/items": listItems2
4844
+ };
4845
+ var handleRequest2 = createRouteHandler(routes2);
4846
+
4847
+ // src/commands/news/web/index.ts
4848
+ async function web2(options2) {
4849
+ prefetch();
4850
+ startWebServer(
4851
+ "News web view",
4852
+ Number.parseInt(options2.port, 10),
4853
+ handleRequest2
4854
+ );
4855
+ }
4856
+
4857
+ // src/commands/registerNews.ts
4858
+ function registerNews(program2) {
4859
+ const newsCommand = program2.command("news").description("View latest news from configured RSS feeds").action(() => web2({ port: "3001" }));
4860
+ newsCommand.command("add").description("Add an RSS feed URL to the config").argument("<url>", "RSS feed URL").action(add2);
4861
+ newsCommand.command("web").description("Start a web view of the news feeds").option("-p, --port <number>", "Port to listen on", "3001").action(web2);
4862
+ }
4863
+
4617
4864
  // src/commands/prs/comment.ts
4618
4865
  import { spawnSync as spawnSync2 } from "child_process";
4619
4866
  import { unlinkSync as unlinkSync3, writeFileSync as writeFileSync17 } from "fs";
@@ -4838,6 +5085,7 @@ function fixed(commentId, sha) {
4838
5085
  const { org, repo } = getRepoInfo();
4839
5086
  const repoUrl = `https://github.com/${org}/${repo}`;
4840
5087
  const message = `Fixed in [${fullSha}](${repoUrl}/commit/${fullSha})`;
5088
+ execSync24("git push", { stdio: "inherit" });
4841
5089
  resolveCommentWithReply(commentId, message);
4842
5090
  } catch (error) {
4843
5091
  if (isGhNotInstalled(error)) {
@@ -4930,20 +5178,20 @@ function fetchLineComments(org, repo, prNumber, threadInfo) {
4930
5178
  }
4931
5179
 
4932
5180
  // src/commands/prs/listComments/printComments.ts
4933
- import chalk49 from "chalk";
5181
+ import chalk51 from "chalk";
4934
5182
  function formatForHuman(comment2) {
4935
5183
  if (comment2.type === "review") {
4936
- const stateColor = comment2.state === "APPROVED" ? chalk49.green : comment2.state === "CHANGES_REQUESTED" ? chalk49.red : chalk49.yellow;
5184
+ const stateColor = comment2.state === "APPROVED" ? chalk51.green : comment2.state === "CHANGES_REQUESTED" ? chalk51.red : chalk51.yellow;
4937
5185
  return [
4938
- `${chalk49.cyan("Review")} by ${chalk49.bold(comment2.user)} ${stateColor(`[${comment2.state}]`)}`,
5186
+ `${chalk51.cyan("Review")} by ${chalk51.bold(comment2.user)} ${stateColor(`[${comment2.state}]`)}`,
4939
5187
  comment2.body,
4940
5188
  ""
4941
5189
  ].join("\n");
4942
5190
  }
4943
5191
  const location = comment2.line ? `:${comment2.line}` : "";
4944
5192
  return [
4945
- `${chalk49.cyan("Line comment")} by ${chalk49.bold(comment2.user)} on ${chalk49.dim(`${comment2.path}${location}`)}`,
4946
- chalk49.dim(comment2.diff_hunk.split("\n").slice(-3).join("\n")),
5193
+ `${chalk51.cyan("Line comment")} by ${chalk51.bold(comment2.user)} on ${chalk51.dim(`${comment2.path}${location}`)}`,
5194
+ chalk51.dim(comment2.diff_hunk.split("\n").slice(-3).join("\n")),
4947
5195
  comment2.body,
4948
5196
  ""
4949
5197
  ].join("\n");
@@ -5030,16 +5278,16 @@ async function listComments() {
5030
5278
  import { execSync as execSync27 } from "child_process";
5031
5279
 
5032
5280
  // src/commands/prs/prs/displayPaginated/index.ts
5033
- import enquirer5 from "enquirer";
5281
+ import enquirer6 from "enquirer";
5034
5282
 
5035
5283
  // src/commands/prs/prs/displayPaginated/printPr.ts
5036
- import chalk50 from "chalk";
5284
+ import chalk52 from "chalk";
5037
5285
  var STATUS_MAP = {
5038
- MERGED: (pr) => pr.mergedAt ? { label: chalk50.magenta("merged"), date: pr.mergedAt } : null,
5039
- CLOSED: (pr) => pr.closedAt ? { label: chalk50.red("closed"), date: pr.closedAt } : null
5286
+ MERGED: (pr) => pr.mergedAt ? { label: chalk52.magenta("merged"), date: pr.mergedAt } : null,
5287
+ CLOSED: (pr) => pr.closedAt ? { label: chalk52.red("closed"), date: pr.closedAt } : null
5040
5288
  };
5041
5289
  function defaultStatus(pr) {
5042
- return { label: chalk50.green("opened"), date: pr.createdAt };
5290
+ return { label: chalk52.green("opened"), date: pr.createdAt };
5043
5291
  }
5044
5292
  function getStatus2(pr) {
5045
5293
  return STATUS_MAP[pr.state]?.(pr) ?? defaultStatus(pr);
@@ -5048,11 +5296,11 @@ function formatDate(dateStr) {
5048
5296
  return new Date(dateStr).toISOString().split("T")[0];
5049
5297
  }
5050
5298
  function formatPrHeader(pr, status2) {
5051
- return `${chalk50.cyan(`#${pr.number}`)} ${pr.title} ${chalk50.dim(`(${pr.author.login},`)} ${status2.label} ${chalk50.dim(`${formatDate(status2.date)})`)}`;
5299
+ return `${chalk52.cyan(`#${pr.number}`)} ${pr.title} ${chalk52.dim(`(${pr.author.login},`)} ${status2.label} ${chalk52.dim(`${formatDate(status2.date)})`)}`;
5052
5300
  }
5053
5301
  function logPrDetails(pr) {
5054
5302
  console.log(
5055
- chalk50.dim(` ${pr.changedFiles.toLocaleString()} files | ${pr.url}`)
5303
+ chalk52.dim(` ${pr.changedFiles.toLocaleString()} files | ${pr.url}`)
5056
5304
  );
5057
5305
  console.log();
5058
5306
  }
@@ -5100,7 +5348,7 @@ function parseAction(action) {
5100
5348
  }
5101
5349
  async function promptNavigation(currentPage, totalPages) {
5102
5350
  const choices = buildNavChoices(currentPage, totalPages);
5103
- const { action } = await enquirer5.prompt({
5351
+ const { action } = await enquirer6.prompt({
5104
5352
  type: "select",
5105
5353
  name: "action",
5106
5354
  message: "Navigate",
@@ -5222,7 +5470,7 @@ import { spawn as spawn3 } from "child_process";
5222
5470
  import * as path21 from "path";
5223
5471
 
5224
5472
  // src/commands/refactor/logViolations.ts
5225
- import chalk51 from "chalk";
5473
+ import chalk53 from "chalk";
5226
5474
  var DEFAULT_MAX_LINES = 100;
5227
5475
  function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
5228
5476
  if (violations.length === 0) {
@@ -5231,43 +5479,43 @@ function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
5231
5479
  }
5232
5480
  return;
5233
5481
  }
5234
- console.error(chalk51.red(`
5482
+ console.error(chalk53.red(`
5235
5483
  Refactor check failed:
5236
5484
  `));
5237
- console.error(chalk51.red(` The following files exceed ${maxLines} lines:
5485
+ console.error(chalk53.red(` The following files exceed ${maxLines} lines:
5238
5486
  `));
5239
5487
  for (const violation of violations) {
5240
- console.error(chalk51.red(` ${violation.file} (${violation.lines} lines)`));
5488
+ console.error(chalk53.red(` ${violation.file} (${violation.lines} lines)`));
5241
5489
  }
5242
5490
  console.error(
5243
- chalk51.yellow(
5491
+ chalk53.yellow(
5244
5492
  `
5245
5493
  Each file needs to be sensibly refactored, or if there is no sensible
5246
5494
  way to refactor it, ignore it with:
5247
5495
  `
5248
5496
  )
5249
5497
  );
5250
- console.error(chalk51.gray(` assist refactor ignore <file>
5498
+ console.error(chalk53.gray(` assist refactor ignore <file>
5251
5499
  `));
5252
5500
  if (process.env.CLAUDECODE) {
5253
- console.error(chalk51.cyan(`
5501
+ console.error(chalk53.cyan(`
5254
5502
  ## Extracting Code to New Files
5255
5503
  `));
5256
5504
  console.error(
5257
- chalk51.cyan(
5505
+ chalk53.cyan(
5258
5506
  ` When extracting logic from one file to another, consider where the extracted code belongs:
5259
5507
  `
5260
5508
  )
5261
5509
  );
5262
5510
  console.error(
5263
- chalk51.cyan(
5511
+ chalk53.cyan(
5264
5512
  ` 1. Keep related logic together: If the extracted code is tightly coupled to the
5265
5513
  original file's domain, create a new folder containing both the original and extracted files.
5266
5514
  `
5267
5515
  )
5268
5516
  );
5269
5517
  console.error(
5270
- chalk51.cyan(
5518
+ chalk53.cyan(
5271
5519
  ` 2. Share common utilities: If the extracted code can be reused across multiple
5272
5520
  domains, move it to a common/shared folder.
5273
5521
  `
@@ -5423,11 +5671,11 @@ async function check(pattern2, options2) {
5423
5671
 
5424
5672
  // src/commands/refactor/ignore.ts
5425
5673
  import fs16 from "fs";
5426
- import chalk52 from "chalk";
5674
+ import chalk54 from "chalk";
5427
5675
  var REFACTOR_YML_PATH2 = "refactor.yml";
5428
5676
  function ignore(file) {
5429
5677
  if (!fs16.existsSync(file)) {
5430
- console.error(chalk52.red(`Error: File does not exist: ${file}`));
5678
+ console.error(chalk54.red(`Error: File does not exist: ${file}`));
5431
5679
  process.exit(1);
5432
5680
  }
5433
5681
  const content = fs16.readFileSync(file, "utf-8");
@@ -5443,7 +5691,7 @@ function ignore(file) {
5443
5691
  fs16.writeFileSync(REFACTOR_YML_PATH2, entry);
5444
5692
  }
5445
5693
  console.log(
5446
- chalk52.green(
5694
+ chalk54.green(
5447
5695
  `Added ${file} to refactor ignore list (max ${maxLines} lines)`
5448
5696
  )
5449
5697
  );
@@ -5451,7 +5699,7 @@ function ignore(file) {
5451
5699
 
5452
5700
  // src/commands/refactor/restructure/index.ts
5453
5701
  import path30 from "path";
5454
- import chalk55 from "chalk";
5702
+ import chalk57 from "chalk";
5455
5703
 
5456
5704
  // src/commands/refactor/restructure/buildImportGraph/index.ts
5457
5705
  import path22 from "path";
@@ -5694,50 +5942,50 @@ function computeRewrites(moves, edges, allProjectFiles) {
5694
5942
 
5695
5943
  // src/commands/refactor/restructure/displayPlan.ts
5696
5944
  import path26 from "path";
5697
- import chalk53 from "chalk";
5945
+ import chalk55 from "chalk";
5698
5946
  function relPath(filePath) {
5699
5947
  return path26.relative(process.cwd(), filePath);
5700
5948
  }
5701
5949
  function displayMoves(plan) {
5702
5950
  if (plan.moves.length === 0) return;
5703
- console.log(chalk53.bold("\nFile moves:"));
5951
+ console.log(chalk55.bold("\nFile moves:"));
5704
5952
  for (const move of plan.moves) {
5705
5953
  console.log(
5706
- ` ${chalk53.red(relPath(move.from))} \u2192 ${chalk53.green(relPath(move.to))}`
5954
+ ` ${chalk55.red(relPath(move.from))} \u2192 ${chalk55.green(relPath(move.to))}`
5707
5955
  );
5708
- console.log(chalk53.dim(` ${move.reason}`));
5956
+ console.log(chalk55.dim(` ${move.reason}`));
5709
5957
  }
5710
5958
  }
5711
5959
  function displayRewrites(rewrites) {
5712
5960
  if (rewrites.length === 0) return;
5713
5961
  const affectedFiles = new Set(rewrites.map((r) => r.file));
5714
- console.log(chalk53.bold(`
5962
+ console.log(chalk55.bold(`
5715
5963
  Import rewrites (${affectedFiles.size} files):`));
5716
5964
  for (const file of affectedFiles) {
5717
- console.log(` ${chalk53.cyan(relPath(file))}:`);
5965
+ console.log(` ${chalk55.cyan(relPath(file))}:`);
5718
5966
  for (const { oldSpecifier, newSpecifier } of rewrites.filter(
5719
5967
  (r) => r.file === file
5720
5968
  )) {
5721
5969
  console.log(
5722
- ` ${chalk53.red(`"${oldSpecifier}"`)} \u2192 ${chalk53.green(`"${newSpecifier}"`)}`
5970
+ ` ${chalk55.red(`"${oldSpecifier}"`)} \u2192 ${chalk55.green(`"${newSpecifier}"`)}`
5723
5971
  );
5724
5972
  }
5725
5973
  }
5726
5974
  }
5727
5975
  function displayPlan(plan) {
5728
5976
  if (plan.warnings.length > 0) {
5729
- console.log(chalk53.yellow("\nWarnings:"));
5730
- for (const w of plan.warnings) console.log(chalk53.yellow(` ${w}`));
5977
+ console.log(chalk55.yellow("\nWarnings:"));
5978
+ for (const w of plan.warnings) console.log(chalk55.yellow(` ${w}`));
5731
5979
  }
5732
5980
  if (plan.newDirectories.length > 0) {
5733
- console.log(chalk53.bold("\nNew directories:"));
5981
+ console.log(chalk55.bold("\nNew directories:"));
5734
5982
  for (const dir of plan.newDirectories)
5735
- console.log(chalk53.green(` ${dir}/`));
5983
+ console.log(chalk55.green(` ${dir}/`));
5736
5984
  }
5737
5985
  displayMoves(plan);
5738
5986
  displayRewrites(plan.rewrites);
5739
5987
  console.log(
5740
- chalk53.dim(
5988
+ chalk55.dim(
5741
5989
  `
5742
5990
  Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rewritten`
5743
5991
  )
@@ -5747,18 +5995,18 @@ Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rew
5747
5995
  // src/commands/refactor/restructure/executePlan.ts
5748
5996
  import fs18 from "fs";
5749
5997
  import path27 from "path";
5750
- import chalk54 from "chalk";
5998
+ import chalk56 from "chalk";
5751
5999
  function executePlan(plan) {
5752
6000
  const updatedContents = applyRewrites(plan.rewrites);
5753
6001
  for (const [file, content] of updatedContents) {
5754
6002
  fs18.writeFileSync(file, content, "utf-8");
5755
6003
  console.log(
5756
- chalk54.cyan(` Rewrote imports in ${path27.relative(process.cwd(), file)}`)
6004
+ chalk56.cyan(` Rewrote imports in ${path27.relative(process.cwd(), file)}`)
5757
6005
  );
5758
6006
  }
5759
6007
  for (const dir of plan.newDirectories) {
5760
6008
  fs18.mkdirSync(dir, { recursive: true });
5761
- console.log(chalk54.green(` Created ${path27.relative(process.cwd(), dir)}/`));
6009
+ console.log(chalk56.green(` Created ${path27.relative(process.cwd(), dir)}/`));
5762
6010
  }
5763
6011
  for (const move of plan.moves) {
5764
6012
  const targetDir = path27.dirname(move.to);
@@ -5767,7 +6015,7 @@ function executePlan(plan) {
5767
6015
  }
5768
6016
  fs18.renameSync(move.from, move.to);
5769
6017
  console.log(
5770
- chalk54.white(
6018
+ chalk56.white(
5771
6019
  ` Moved ${path27.relative(process.cwd(), move.from)} \u2192 ${path27.relative(process.cwd(), move.to)}`
5772
6020
  )
5773
6021
  );
@@ -5782,7 +6030,7 @@ function removeEmptyDirectories(dirs) {
5782
6030
  if (entries.length === 0) {
5783
6031
  fs18.rmdirSync(dir);
5784
6032
  console.log(
5785
- chalk54.dim(
6033
+ chalk56.dim(
5786
6034
  ` Removed empty directory ${path27.relative(process.cwd(), dir)}`
5787
6035
  )
5788
6036
  );
@@ -5913,22 +6161,22 @@ async function restructure(pattern2, options2 = {}) {
5913
6161
  const targetPattern = pattern2 ?? "src";
5914
6162
  const files = findSourceFiles2(targetPattern);
5915
6163
  if (files.length === 0) {
5916
- console.log(chalk55.yellow("No files found matching pattern"));
6164
+ console.log(chalk57.yellow("No files found matching pattern"));
5917
6165
  return;
5918
6166
  }
5919
6167
  const tsConfigPath = path30.resolve("tsconfig.json");
5920
6168
  const plan = buildPlan(files, tsConfigPath);
5921
6169
  if (plan.moves.length === 0) {
5922
- console.log(chalk55.green("No restructuring needed"));
6170
+ console.log(chalk57.green("No restructuring needed"));
5923
6171
  return;
5924
6172
  }
5925
6173
  displayPlan(plan);
5926
6174
  if (options2.apply) {
5927
- console.log(chalk55.bold("\nApplying changes..."));
6175
+ console.log(chalk57.bold("\nApplying changes..."));
5928
6176
  executePlan(plan);
5929
- console.log(chalk55.green("\nRestructuring complete"));
6177
+ console.log(chalk57.green("\nRestructuring complete"));
5930
6178
  } else {
5931
- console.log(chalk55.dim("\nDry run. Use --apply to execute."));
6179
+ console.log(chalk57.dim("\nDry run. Use --apply to execute."));
5932
6180
  }
5933
6181
  }
5934
6182
 
@@ -6471,14 +6719,14 @@ import {
6471
6719
  import { dirname as dirname17, join as join26 } from "path";
6472
6720
 
6473
6721
  // src/commands/transcript/summarise/processStagedFile/validateStagedContent.ts
6474
- import chalk56 from "chalk";
6722
+ import chalk58 from "chalk";
6475
6723
  var FULL_TRANSCRIPT_REGEX = /^\[Full Transcript\]\(([^)]+)\)/;
6476
6724
  function validateStagedContent(filename, content) {
6477
6725
  const firstLine = content.split("\n")[0];
6478
6726
  const match = firstLine.match(FULL_TRANSCRIPT_REGEX);
6479
6727
  if (!match) {
6480
6728
  console.error(
6481
- chalk56.red(
6729
+ chalk58.red(
6482
6730
  `Staged file ${filename} missing [Full Transcript](<path>) link on first line.`
6483
6731
  )
6484
6732
  );
@@ -6487,7 +6735,7 @@ function validateStagedContent(filename, content) {
6487
6735
  const contentAfterLink = content.slice(firstLine.length).trim();
6488
6736
  if (!contentAfterLink) {
6489
6737
  console.error(
6490
- chalk56.red(
6738
+ chalk58.red(
6491
6739
  `Staged file ${filename} has no summary content after the transcript link.`
6492
6740
  )
6493
6741
  );
@@ -6624,7 +6872,7 @@ import { join as join29 } from "path";
6624
6872
  import { homedir as homedir7 } from "os";
6625
6873
  import { dirname as dirname19, join as join28 } from "path";
6626
6874
  import { fileURLToPath as fileURLToPath6 } from "url";
6627
- var __dirname7 = dirname19(fileURLToPath6(import.meta.url));
6875
+ var __dirname6 = dirname19(fileURLToPath6(import.meta.url));
6628
6876
  var VOICE_DIR = join28(homedir7(), ".assist", "voice");
6629
6877
  var voicePaths = {
6630
6878
  dir: VOICE_DIR,
@@ -6634,7 +6882,7 @@ var voicePaths = {
6634
6882
  lock: join28(VOICE_DIR, "voice.lock")
6635
6883
  };
6636
6884
  function getPythonDir() {
6637
- return join28(__dirname7, "commands", "voice", "python");
6885
+ return join28(__dirname6, "commands", "voice", "python");
6638
6886
  }
6639
6887
  function getVenvPython() {
6640
6888
  return process.platform === "win32" ? join28(voicePaths.venv, "Scripts", "python.exe") : join28(voicePaths.venv, "bin", "python");
@@ -6880,7 +7128,7 @@ function registerVoice(program2) {
6880
7128
 
6881
7129
  // src/commands/roam/auth.ts
6882
7130
  import { randomBytes } from "crypto";
6883
- import chalk57 from "chalk";
7131
+ import chalk59 from "chalk";
6884
7132
 
6885
7133
  // src/lib/openBrowser.ts
6886
7134
  import { execSync as execSync31 } from "child_process";
@@ -7015,7 +7263,7 @@ async function exchangeToken(params) {
7015
7263
  }
7016
7264
 
7017
7265
  // src/commands/roam/promptCredentials.ts
7018
- import enquirer6 from "enquirer";
7266
+ import enquirer7 from "enquirer";
7019
7267
  function censor(value) {
7020
7268
  const visible = value.slice(-4);
7021
7269
  return `${"*".repeat(value.length - 4)}${visible}`;
@@ -7024,7 +7272,7 @@ function label(name, existing) {
7024
7272
  return existing ? `${name} (${censor(existing)})` : name;
7025
7273
  }
7026
7274
  async function promptField(name, existing) {
7027
- const { value } = await enquirer6.prompt({
7275
+ const { value } = await enquirer7.prompt({
7028
7276
  type: "input",
7029
7277
  name: "value",
7030
7278
  message: `${label(name, existing)}:`,
@@ -7055,13 +7303,13 @@ async function auth() {
7055
7303
  saveGlobalConfig(config);
7056
7304
  const state = randomBytes(16).toString("hex");
7057
7305
  console.log(
7058
- chalk57.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
7306
+ chalk59.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
7059
7307
  );
7060
- console.log(chalk57.white("http://localhost:14523/callback\n"));
7061
- console.log(chalk57.blue("Opening browser for authorization..."));
7062
- console.log(chalk57.dim("Waiting for authorization callback..."));
7308
+ console.log(chalk59.white("http://localhost:14523/callback\n"));
7309
+ console.log(chalk59.blue("Opening browser for authorization..."));
7310
+ console.log(chalk59.dim("Waiting for authorization callback..."));
7063
7311
  const { code, redirectUri } = await authorizeInBrowser(clientId, state);
7064
- console.log(chalk57.dim("Exchanging code for tokens..."));
7312
+ console.log(chalk59.dim("Exchanging code for tokens..."));
7065
7313
  const tokens = await exchangeToken({
7066
7314
  code,
7067
7315
  clientId,
@@ -7077,7 +7325,7 @@ async function auth() {
7077
7325
  };
7078
7326
  saveGlobalConfig(config);
7079
7327
  console.log(
7080
- chalk57.green("Roam credentials and tokens saved to ~/.assist.yml")
7328
+ chalk59.green("Roam credentials and tokens saved to ~/.assist.yml")
7081
7329
  );
7082
7330
  }
7083
7331
 
@@ -7191,7 +7439,7 @@ Run \`assist run ${name} $ARGUMENTS 2>&1\`.
7191
7439
  writeFileSync24(filePath, content);
7192
7440
  console.log(`Created command file: ${filePath}`);
7193
7441
  }
7194
- function add2() {
7442
+ function add3() {
7195
7443
  const { name, command, args } = requireParsedArgs();
7196
7444
  saveNewRunConfig(name, command, args);
7197
7445
  createCommandFile(name);
@@ -7265,14 +7513,14 @@ function run2(name, args) {
7265
7513
  }
7266
7514
 
7267
7515
  // src/commands/statusLine.ts
7268
- import chalk58 from "chalk";
7516
+ import chalk60 from "chalk";
7269
7517
  function formatNumber(num) {
7270
7518
  return num.toLocaleString("en-US");
7271
7519
  }
7272
7520
  function colorizePercent(pct) {
7273
7521
  const label2 = `${pct}%`;
7274
- if (pct > 80) return chalk58.red(label2);
7275
- if (pct > 40) return chalk58.yellow(label2);
7522
+ if (pct > 80) return chalk60.red(label2);
7523
+ if (pct > 40) return chalk60.yellow(label2);
7276
7524
  return label2;
7277
7525
  }
7278
7526
  async function statusLine() {
@@ -7298,7 +7546,7 @@ import { fileURLToPath as fileURLToPath7 } from "url";
7298
7546
  // src/commands/sync/syncClaudeMd.ts
7299
7547
  import * as fs21 from "fs";
7300
7548
  import * as path31 from "path";
7301
- import chalk59 from "chalk";
7549
+ import chalk61 from "chalk";
7302
7550
  async function syncClaudeMd(claudeDir, targetBase) {
7303
7551
  const source = path31.join(claudeDir, "CLAUDE.md");
7304
7552
  const target = path31.join(targetBase, "CLAUDE.md");
@@ -7307,12 +7555,12 @@ async function syncClaudeMd(claudeDir, targetBase) {
7307
7555
  const targetContent = fs21.readFileSync(target, "utf-8");
7308
7556
  if (sourceContent !== targetContent) {
7309
7557
  console.log(
7310
- chalk59.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
7558
+ chalk61.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
7311
7559
  );
7312
7560
  console.log();
7313
7561
  printDiff(targetContent, sourceContent);
7314
7562
  const confirm = await promptConfirm(
7315
- chalk59.red("Overwrite existing CLAUDE.md?"),
7563
+ chalk61.red("Overwrite existing CLAUDE.md?"),
7316
7564
  false
7317
7565
  );
7318
7566
  if (!confirm) {
@@ -7328,7 +7576,7 @@ async function syncClaudeMd(claudeDir, targetBase) {
7328
7576
  // src/commands/sync/syncSettings.ts
7329
7577
  import * as fs22 from "fs";
7330
7578
  import * as path32 from "path";
7331
- import chalk60 from "chalk";
7579
+ import chalk62 from "chalk";
7332
7580
  async function syncSettings(claudeDir, targetBase, options2) {
7333
7581
  const source = path32.join(claudeDir, "settings.json");
7334
7582
  const target = path32.join(targetBase, "settings.json");
@@ -7344,14 +7592,14 @@ async function syncSettings(claudeDir, targetBase, options2) {
7344
7592
  if (mergedContent !== normalizedTarget) {
7345
7593
  if (!options2?.yes) {
7346
7594
  console.log(
7347
- chalk60.yellow(
7595
+ chalk62.yellow(
7348
7596
  "\n\u26A0\uFE0F Warning: settings.json differs from existing file"
7349
7597
  )
7350
7598
  );
7351
7599
  console.log();
7352
7600
  printDiff(targetContent, mergedContent);
7353
7601
  const confirm = await promptConfirm(
7354
- chalk60.red("Overwrite existing settings.json?"),
7602
+ chalk62.red("Overwrite existing settings.json?"),
7355
7603
  false
7356
7604
  );
7357
7605
  if (!confirm) {
@@ -7367,9 +7615,9 @@ async function syncSettings(claudeDir, targetBase, options2) {
7367
7615
 
7368
7616
  // src/commands/sync.ts
7369
7617
  var __filename4 = fileURLToPath7(import.meta.url);
7370
- var __dirname8 = path33.dirname(__filename4);
7618
+ var __dirname7 = path33.dirname(__filename4);
7371
7619
  async function sync(options2) {
7372
- const claudeDir = path33.join(__dirname8, "..", "claude");
7620
+ const claudeDir = path33.join(__dirname7, "..", "claude");
7373
7621
  const targetBase = path33.join(os.homedir(), ".claude");
7374
7622
  syncCommands(claudeDir, targetBase);
7375
7623
  await syncSettings(claudeDir, targetBase, { yes: options2?.yes });
@@ -7432,7 +7680,7 @@ var program = new Command();
7432
7680
  program.name("assist").description("CLI application").version(package_default.version);
7433
7681
  program.command("sync").description("Copy command files to ~/.claude/commands").option("-y, --yes", "Overwrite settings.json without prompting").action((options2) => sync(options2));
7434
7682
  program.command("init").description("Initialize VS Code and verify configurations").action(init4);
7435
- program.command("commit").description("Create a git commit with validation").argument("<args...>", "status | <files...> <message>").action(commit);
7683
+ program.command("commit").description("Create a git commit with validation").argument("<args...>", "status | <message> [files...]").action(commit);
7436
7684
  var configCommand = program.command("config").description("View and modify assist.yml configuration");
7437
7685
  configCommand.command("set <key> <value>").description("Set a config value (e.g. commit.push true)").action(configSet);
7438
7686
  configCommand.command("get <key>").description("Get a config value").action(configGet);
@@ -7444,7 +7692,7 @@ runCommand.command("list").description("List configured run commands").action(li
7444
7692
  runCommand.command("add").description("Add a new run configuration to assist.yml").argument("<name>", "Name for the run configuration").argument("<command>", "Command to execute").argument("[args...]", "Static args to pass to the command").addHelpText(
7445
7693
  "after",
7446
7694
  '\nPositional params can be added to the config manually:\n params:\n - name: env # assist run deploy prod \u2192 appends "prod"\n required: true\n - name: tag\n default: latest'
7447
- ).allowUnknownOption().allowExcessArguments().action(() => add2());
7695
+ ).allowUnknownOption().allowExcessArguments().action(() => add3());
7448
7696
  registerNew(program);
7449
7697
  var lintCommand = program.command("lint").description("Run lint checks for conventions not enforced by biomejs").action(lint);
7450
7698
  lintCommand.command("init").description("Initialize Biome with standard linter config").action(init);
@@ -7466,6 +7714,7 @@ registerDevlog(program);
7466
7714
  registerDeploy(program);
7467
7715
  registerComplexity(program);
7468
7716
  registerNetframework(program);
7717
+ registerNews(program);
7469
7718
  registerTranscript(program);
7470
7719
  registerVoice(program);
7471
7720
  program.parse();