@staff0rd/assist 0.106.1 → 0.108.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.106.1",
9
+ version: "0.108.0",
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",
@@ -143,6 +144,9 @@ var assistConfigSchema = z.strictObject({
143
144
  restructure: z.strictObject({
144
145
  ignore: z.array(z.string()).default([])
145
146
  }).optional(),
147
+ jira: z.strictObject({
148
+ acField: z.string().default("customfield_11937")
149
+ }).optional(),
146
150
  roam: z.strictObject({
147
151
  clientId: z.string(),
148
152
  clientSecret: z.string(),
@@ -153,6 +157,9 @@ var assistConfigSchema = z.strictObject({
153
157
  run: z.array(runConfigSchema).optional(),
154
158
  transcript: transcriptConfigSchema.optional(),
155
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: [] }),
156
163
  voice: z.strictObject({
157
164
  wakeWords: z.array(z.string()).default(DEFAULT_WAKE_WORDS),
158
165
  mic: z.string().optional(),
@@ -2225,7 +2232,7 @@ async function add() {
2225
2232
  const type = await promptType();
2226
2233
  const name = await promptName();
2227
2234
  const description = await promptDescription();
2228
- const acceptanceCriteria = await promptAcceptanceCriteria();
2235
+ const acceptanceCriteria2 = await promptAcceptanceCriteria();
2229
2236
  const items = loadBacklog();
2230
2237
  const id = getNextId(items);
2231
2238
  items.push({
@@ -2233,7 +2240,7 @@ async function add() {
2233
2240
  type,
2234
2241
  name,
2235
2242
  description,
2236
- acceptanceCriteria,
2243
+ acceptanceCriteria: acceptanceCriteria2,
2237
2244
  status: "todo"
2238
2245
  });
2239
2246
  saveBacklog(items);
@@ -2343,15 +2350,63 @@ async function start(id) {
2343
2350
  }
2344
2351
  }
2345
2352
 
2346
- // src/commands/backlog/web/index.ts
2353
+ // src/shared/web.ts
2347
2354
  import { exec } from "child_process";
2348
- import { createServer } from "http";
2349
- import chalk29 from "chalk";
2350
-
2351
- // src/commands/backlog/web/handleRequest.ts
2352
2355
  import { readFileSync as readFileSync12 } from "fs";
2356
+ import {
2357
+ createServer
2358
+ } from "http";
2353
2359
  import { dirname as dirname11, join as join10 } from "path";
2354
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
+ }
2355
2410
 
2356
2411
  // src/commands/backlog/web/getHtml.ts
2357
2412
  function getHtml() {
@@ -2376,11 +2431,7 @@ function getHtml() {
2376
2431
  </html>`;
2377
2432
  }
2378
2433
 
2379
- // src/commands/backlog/web/respondJson.ts
2380
- function respondJson(res, status2, data) {
2381
- res.writeHead(status2, { "Content-Type": "application/json" });
2382
- res.end(JSON.stringify(data));
2383
- }
2434
+ // src/commands/backlog/web/parseItemBody.ts
2384
2435
  function readBody(req) {
2385
2436
  return new Promise((resolve6, reject) => {
2386
2437
  let body = "";
@@ -2448,25 +2499,12 @@ async function updateItem(req, res, id) {
2448
2499
  }
2449
2500
 
2450
2501
  // src/commands/backlog/web/handleRequest.ts
2451
- var __dirname4 = dirname11(fileURLToPath3(import.meta.url));
2452
- var bundleCache;
2453
- function serveBundle(_req, res) {
2454
- if (!bundleCache) {
2455
- bundleCache = readFileSync12(
2456
- join10(__dirname4, "commands/backlog/web/bundle.js"),
2457
- "utf-8"
2458
- );
2459
- }
2460
- res.writeHead(200, { "Content-Type": "application/javascript" });
2461
- res.end(bundleCache);
2462
- }
2463
- function serveHtml(_req, res) {
2464
- res.writeHead(200, { "Content-Type": "text/html" });
2465
- res.end(getHtml());
2466
- }
2467
2502
  var routes = {
2468
- "GET /": serveHtml,
2469
- "GET /bundle.js": serveBundle,
2503
+ "GET /": createHtmlHandler(getHtml),
2504
+ "GET /bundle.js": createBundleHandler(
2505
+ import.meta.url,
2506
+ "commands/backlog/web/bundle.js"
2507
+ ),
2470
2508
  "GET /api/items": listItems,
2471
2509
  "POST /api/items": createItem
2472
2510
  };
@@ -2475,17 +2513,11 @@ var itemRoutes = {
2475
2513
  PUT: (req, res, id) => updateItem(req, res, id),
2476
2514
  DELETE: (_req, res, id) => deleteItem(res, id)
2477
2515
  };
2478
- function parseRoute(req, port) {
2479
- const url = new URL(req.url ?? "/", `http://localhost:${port}`);
2480
- return { method: req.method ?? "GET", pathname: url.pathname };
2481
- }
2516
+ var baseHandler = createRouteHandler(routes);
2482
2517
  async function handleRequest(req, res, port) {
2483
- const { method, pathname } = parseRoute(req, port);
2484
- const handler = routes[`${method} ${pathname}`];
2485
- if (handler) {
2486
- await handler(req, res);
2487
- return;
2488
- }
2518
+ const url = new URL(req.url ?? "/", `http://localhost:${port}`);
2519
+ const method = req.method ?? "GET";
2520
+ const pathname = url.pathname;
2489
2521
  const itemMatch = pathname.match(/^\/api\/items\/(\d+)$/);
2490
2522
  if (itemMatch) {
2491
2523
  const itemHandler = itemRoutes[method];
@@ -2494,22 +2526,16 @@ async function handleRequest(req, res, port) {
2494
2526
  return;
2495
2527
  }
2496
2528
  }
2497
- res.writeHead(404);
2498
- res.end();
2529
+ await baseHandler(req, res, port);
2499
2530
  }
2500
2531
 
2501
2532
  // src/commands/backlog/web/index.ts
2502
2533
  async function web(options2) {
2503
- const port = Number.parseInt(options2.port, 10);
2504
- const url = `http://localhost:${port}`;
2505
- const server = createServer((req, res) => {
2506
- handleRequest(req, res, port);
2507
- });
2508
- server.listen(port, () => {
2509
- console.log(chalk29.green(`Backlog web view: ${url}`));
2510
- console.log(chalk29.dim("Press Ctrl+C to stop"));
2511
- exec(`open ${url}`);
2512
- });
2534
+ startWebServer(
2535
+ "Backlog web view",
2536
+ Number.parseInt(options2.port, 10),
2537
+ handleRequest
2538
+ );
2513
2539
  }
2514
2540
 
2515
2541
  // src/commands/registerBacklog.ts
@@ -2610,9 +2636,9 @@ import { existsSync as existsSync16, readFileSync as readFileSync13, writeFileSy
2610
2636
  import { dirname as dirname12, resolve as resolve2 } from "path";
2611
2637
  import { fileURLToPath as fileURLToPath4 } from "url";
2612
2638
  var __filename2 = fileURLToPath4(import.meta.url);
2613
- var __dirname5 = dirname12(__filename2);
2639
+ var __dirname4 = dirname12(__filename2);
2614
2640
  function getCliReadsPath() {
2615
- return resolve2(__dirname5, "..", "assist.cli-reads");
2641
+ return resolve2(__dirname4, "..", "assist.cli-reads");
2616
2642
  }
2617
2643
  var cachedLines;
2618
2644
  function getCliReadsLines() {
@@ -2831,9 +2857,9 @@ import { execSync as execSync13 } from "child_process";
2831
2857
  import { dirname as dirname13, resolve as resolve4 } from "path";
2832
2858
  import { fileURLToPath as fileURLToPath5 } from "url";
2833
2859
  var __filename3 = fileURLToPath5(import.meta.url);
2834
- var __dirname6 = dirname13(__filename3);
2860
+ var __dirname5 = dirname13(__filename3);
2835
2861
  function getInstallDir() {
2836
- return resolve4(__dirname6, "..");
2862
+ return resolve4(__dirname5, "..");
2837
2863
  }
2838
2864
  function isGitRepo(dir) {
2839
2865
  try {
@@ -4210,12 +4236,200 @@ function registerDevlog(program2) {
4210
4236
  ).option("--all", "Show all non-archived repos regardless of push date").action(repos);
4211
4237
  }
4212
4238
 
4239
+ // src/commands/jira/acceptanceCriteria.ts
4240
+ import { execSync as execSync20 } from "child_process";
4241
+ import chalk45 from "chalk";
4242
+
4243
+ // src/commands/jira/adfToText.ts
4244
+ function renderInline(node) {
4245
+ const text = node.text ?? "";
4246
+ if (node.marks?.some((m) => m.type === "code")) return `\`${text}\``;
4247
+ return text;
4248
+ }
4249
+ function renderChildren(node, indent) {
4250
+ return renderNodes(node.content ?? [], indent);
4251
+ }
4252
+ function renderOrderedList(node, indent) {
4253
+ let counter = 0;
4254
+ return (node.content ?? []).map((item) => {
4255
+ counter++;
4256
+ return renderListItem(item, indent, `${counter}.`);
4257
+ }).join("\n");
4258
+ }
4259
+ function renderBulletList(node, indent) {
4260
+ return (node.content ?? []).map((item) => renderListItem(item, indent, "-")).join("\n");
4261
+ }
4262
+ function renderHeading(node, indent) {
4263
+ const level = node.attrs?.level ?? 1;
4264
+ return `${"#".repeat(level)} ${renderChildren(node, indent)}`;
4265
+ }
4266
+ var renderers = {
4267
+ text: (node) => renderInline(node),
4268
+ paragraph: renderChildren,
4269
+ orderedList: renderOrderedList,
4270
+ bulletList: renderBulletList,
4271
+ listItem: (node, indent) => renderListItem(node, indent, "-"),
4272
+ heading: renderHeading,
4273
+ doc: renderChildren
4274
+ };
4275
+ function renderNode(node, indent) {
4276
+ const renderer = renderers[node.type];
4277
+ if (renderer) return renderer(node, indent);
4278
+ return node.content ? renderChildren(node, indent) : "";
4279
+ }
4280
+ function renderNodes(nodes, indent) {
4281
+ return nodes.map((node) => renderNode(node, indent)).join("");
4282
+ }
4283
+ function isListNode(node) {
4284
+ return node.type === "orderedList" || node.type === "bulletList";
4285
+ }
4286
+ function renderListChild(child, indent, pad, marker, isFirst) {
4287
+ if (isListNode(child)) return renderNodes([child], indent + 1);
4288
+ if (child.type !== "paragraph") return renderNode(child, indent);
4289
+ const text = renderChildren(child, indent);
4290
+ return isFirst ? `${pad}${marker} ${text}` : `${pad} ${text}`;
4291
+ }
4292
+ function renderListItem(node, indent, marker) {
4293
+ const pad = " ".repeat(indent);
4294
+ return (node.content ?? []).map((child, i) => renderListChild(child, indent, pad, marker, i === 0)).join("\n");
4295
+ }
4296
+ function adfToText(doc) {
4297
+ return renderNodes([doc], 0);
4298
+ }
4299
+
4300
+ // src/commands/jira/acceptanceCriteria.ts
4301
+ var DEFAULT_AC_FIELD = "customfield_11937";
4302
+ function acceptanceCriteria(issueKey) {
4303
+ const config = loadConfig();
4304
+ const field = config.jira?.acField ?? DEFAULT_AC_FIELD;
4305
+ let result;
4306
+ try {
4307
+ result = execSync20(
4308
+ `acli jira workitem view ${issueKey} -f ${field} --json`,
4309
+ { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
4310
+ );
4311
+ } catch (error) {
4312
+ if (error instanceof Error && "stderr" in error) {
4313
+ const stderr = error.stderr;
4314
+ if (stderr.includes("unauthorized")) {
4315
+ console.error(
4316
+ chalk45.red("Jira authentication expired."),
4317
+ "Run",
4318
+ chalk45.cyan("assist jira auth"),
4319
+ "to re-authenticate."
4320
+ );
4321
+ process.exit(1);
4322
+ }
4323
+ }
4324
+ console.error(chalk45.red(`Failed to fetch ${issueKey}.`));
4325
+ process.exit(1);
4326
+ }
4327
+ const parsed = JSON.parse(result);
4328
+ const acValue = parsed?.fields?.[field];
4329
+ if (!acValue) {
4330
+ console.log(chalk45.yellow(`No acceptance criteria found on ${issueKey}.`));
4331
+ return;
4332
+ }
4333
+ if (typeof acValue === "string") {
4334
+ console.log(acValue);
4335
+ return;
4336
+ }
4337
+ if (acValue.type === "doc") {
4338
+ console.log(adfToText(acValue));
4339
+ return;
4340
+ }
4341
+ console.log(JSON.stringify(acValue, null, 2));
4342
+ }
4343
+
4344
+ // src/commands/jira/jiraAuth.ts
4345
+ import { execSync as execSync21 } from "child_process";
4346
+ import Enquirer from "enquirer";
4347
+
4348
+ // src/shared/loadJson.ts
4349
+ import { existsSync as existsSync20, mkdirSync as mkdirSync5, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "fs";
4350
+ import { homedir as homedir6 } from "os";
4351
+ import { join as join16 } from "path";
4352
+ function getStoreDir() {
4353
+ return join16(homedir6(), ".assist");
4354
+ }
4355
+ function getStorePath(filename) {
4356
+ return join16(getStoreDir(), filename);
4357
+ }
4358
+ function loadJson(filename) {
4359
+ const path35 = getStorePath(filename);
4360
+ if (existsSync20(path35)) {
4361
+ try {
4362
+ return JSON.parse(readFileSync18(path35, "utf-8"));
4363
+ } catch {
4364
+ return {};
4365
+ }
4366
+ }
4367
+ return {};
4368
+ }
4369
+ function saveJson(filename, data) {
4370
+ const dir = getStoreDir();
4371
+ if (!existsSync20(dir)) {
4372
+ mkdirSync5(dir, { recursive: true });
4373
+ }
4374
+ writeFileSync16(getStorePath(filename), JSON.stringify(data, null, 2));
4375
+ }
4376
+
4377
+ // src/commands/jira/jiraAuth.ts
4378
+ var CONFIG_FILE = "jira.json";
4379
+ async function promptCredentials(config) {
4380
+ const { Input, Password } = Enquirer;
4381
+ const site = await new Input({
4382
+ name: "site",
4383
+ message: "Jira site (e.g., mycompany.atlassian.net):",
4384
+ initial: config.site
4385
+ }).run();
4386
+ const email = await new Input({
4387
+ name: "email",
4388
+ message: "Email:",
4389
+ initial: config.email
4390
+ }).run();
4391
+ const token = await new Password({
4392
+ name: "token",
4393
+ message: "API token (https://id.atlassian.com/manage-profile/security/api-tokens):"
4394
+ }).run();
4395
+ return { site, email, token };
4396
+ }
4397
+ async function jiraAuth() {
4398
+ const config = loadJson(CONFIG_FILE);
4399
+ try {
4400
+ const { site, email, token } = await promptCredentials(config);
4401
+ if (!site || !email || !token) {
4402
+ console.error("All fields are required.");
4403
+ process.exit(1);
4404
+ }
4405
+ execSync21(`acli jira auth login --site ${site} --email "${email}" --token`, {
4406
+ encoding: "utf-8",
4407
+ input: token,
4408
+ stdio: ["pipe", "inherit", "inherit"]
4409
+ });
4410
+ saveJson(CONFIG_FILE, { site, email });
4411
+ console.log("Successfully authenticated with Jira.");
4412
+ } catch (error) {
4413
+ if (error instanceof Error) {
4414
+ console.error("Error authenticating with Jira:", error.message);
4415
+ }
4416
+ process.exit(1);
4417
+ }
4418
+ }
4419
+
4420
+ // src/commands/registerJira.ts
4421
+ function registerJira(program2) {
4422
+ const jiraCommand = program2.command("jira").description("Jira utilities");
4423
+ jiraCommand.command("auth").description("Authenticate with Jira via API token").action(() => jiraAuth());
4424
+ jiraCommand.command("ac <issue-key>").description("Print acceptance criteria for a Jira issue").action((issueKey) => acceptanceCriteria(issueKey));
4425
+ }
4426
+
4213
4427
  // src/commands/netframework/buildTree.ts
4214
- import { readFileSync as readFileSync18 } from "fs";
4428
+ import { readFileSync as readFileSync19 } from "fs";
4215
4429
  import path17 from "path";
4216
4430
  var PROJECT_REF_RE = /<ProjectReference\s+Include="([^"]+)"/g;
4217
4431
  function getProjectRefs(csprojPath) {
4218
- const content = readFileSync18(csprojPath, "utf-8");
4432
+ const content = readFileSync19(csprojPath, "utf-8");
4219
4433
  const refs = [];
4220
4434
  for (const match of content.matchAll(PROJECT_REF_RE)) {
4221
4435
  refs.push(match[1].replace(/\\/g, "/"));
@@ -4232,7 +4446,7 @@ function buildTree(csprojPath, repoRoot, visited = /* @__PURE__ */ new Set()) {
4232
4446
  for (const ref of getProjectRefs(abs)) {
4233
4447
  const childAbs = path17.resolve(dir, ref);
4234
4448
  try {
4235
- readFileSync18(childAbs);
4449
+ readFileSync19(childAbs);
4236
4450
  node.children.push(buildTree(childAbs, repoRoot, visited));
4237
4451
  } catch {
4238
4452
  node.children.push({
@@ -4257,7 +4471,7 @@ function collectAllDeps(node) {
4257
4471
  }
4258
4472
 
4259
4473
  // src/commands/netframework/findContainingSolutions.ts
4260
- import { readdirSync as readdirSync2, readFileSync as readFileSync19, statSync } from "fs";
4474
+ import { readdirSync as readdirSync2, readFileSync as readFileSync20, statSync } from "fs";
4261
4475
  import path18 from "path";
4262
4476
  function findSlnFiles(dir, maxDepth, depth = 0) {
4263
4477
  if (depth > maxDepth) return [];
@@ -4292,7 +4506,7 @@ function findContainingSolutions(csprojPath, repoRoot) {
4292
4506
  const pattern2 = new RegExp(`[\\\\"/]${escapeRegex(csprojBasename)}"`);
4293
4507
  for (const sln of slnFiles) {
4294
4508
  try {
4295
- const content = readFileSync19(sln, "utf-8");
4509
+ const content = readFileSync20(sln, "utf-8");
4296
4510
  if (pattern2.test(content)) {
4297
4511
  matches.push(path18.relative(repoRoot, sln));
4298
4512
  }
@@ -4306,30 +4520,30 @@ function escapeRegex(s) {
4306
4520
  }
4307
4521
 
4308
4522
  // src/commands/netframework/printTree.ts
4309
- import chalk45 from "chalk";
4523
+ import chalk46 from "chalk";
4310
4524
  function printNodes(nodes, prefix2) {
4311
4525
  for (let i = 0; i < nodes.length; i++) {
4312
4526
  const isLast = i === nodes.length - 1;
4313
4527
  const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
4314
4528
  const childPrefix = isLast ? " " : "\u2502 ";
4315
4529
  const isMissing = nodes[i].relativePath.startsWith("[MISSING]");
4316
- const label2 = isMissing ? chalk45.red(nodes[i].relativePath) : nodes[i].relativePath;
4530
+ const label2 = isMissing ? chalk46.red(nodes[i].relativePath) : nodes[i].relativePath;
4317
4531
  console.log(`${prefix2}${connector}${label2}`);
4318
4532
  printNodes(nodes[i].children, prefix2 + childPrefix);
4319
4533
  }
4320
4534
  }
4321
4535
  function printTree(tree, totalCount, solutions) {
4322
- console.log(chalk45.bold("\nProject Dependency Tree"));
4323
- console.log(chalk45.cyan(tree.relativePath));
4536
+ console.log(chalk46.bold("\nProject Dependency Tree"));
4537
+ console.log(chalk46.cyan(tree.relativePath));
4324
4538
  printNodes(tree.children, "");
4325
- console.log(chalk45.dim(`
4539
+ console.log(chalk46.dim(`
4326
4540
  ${totalCount} projects total (including root)`));
4327
- console.log(chalk45.bold("\nSolution Membership"));
4541
+ console.log(chalk46.bold("\nSolution Membership"));
4328
4542
  if (solutions.length === 0) {
4329
- console.log(chalk45.yellow(" Not found in any .sln"));
4543
+ console.log(chalk46.yellow(" Not found in any .sln"));
4330
4544
  } else {
4331
4545
  for (const sln of solutions) {
4332
- console.log(` ${chalk45.green(sln)}`);
4546
+ console.log(` ${chalk46.green(sln)}`);
4333
4547
  }
4334
4548
  }
4335
4549
  console.log();
@@ -4356,17 +4570,17 @@ function printJson(tree, totalCount, solutions) {
4356
4570
  }
4357
4571
 
4358
4572
  // src/commands/netframework/resolveCsproj.ts
4359
- import { existsSync as existsSync21 } from "fs";
4573
+ import { existsSync as existsSync22 } from "fs";
4360
4574
  import path20 from "path";
4361
- import chalk46 from "chalk";
4575
+ import chalk47 from "chalk";
4362
4576
 
4363
4577
  // src/commands/netframework/findRepoRoot.ts
4364
- import { existsSync as existsSync20 } from "fs";
4578
+ import { existsSync as existsSync21 } from "fs";
4365
4579
  import path19 from "path";
4366
4580
  function findRepoRoot(dir) {
4367
4581
  let current = dir;
4368
4582
  while (current !== path19.dirname(current)) {
4369
- if (existsSync20(path19.join(current, ".git"))) {
4583
+ if (existsSync21(path19.join(current, ".git"))) {
4370
4584
  return current;
4371
4585
  }
4372
4586
  current = path19.dirname(current);
@@ -4377,13 +4591,13 @@ function findRepoRoot(dir) {
4377
4591
  // src/commands/netframework/resolveCsproj.ts
4378
4592
  function resolveCsproj(csprojPath) {
4379
4593
  const resolved = path20.resolve(csprojPath);
4380
- if (!existsSync21(resolved)) {
4381
- console.error(chalk46.red(`File not found: ${resolved}`));
4594
+ if (!existsSync22(resolved)) {
4595
+ console.error(chalk47.red(`File not found: ${resolved}`));
4382
4596
  process.exit(1);
4383
4597
  }
4384
4598
  const repoRoot = findRepoRoot(path20.dirname(resolved));
4385
4599
  if (!repoRoot) {
4386
- console.error(chalk46.red("Could not find git repository root"));
4600
+ console.error(chalk47.red("Could not find git repository root"));
4387
4601
  process.exit(1);
4388
4602
  }
4389
4603
  return { resolved, repoRoot };
@@ -4403,12 +4617,12 @@ async function deps(csprojPath, options2) {
4403
4617
  }
4404
4618
 
4405
4619
  // src/commands/netframework/inSln.ts
4406
- import chalk47 from "chalk";
4620
+ import chalk48 from "chalk";
4407
4621
  async function inSln(csprojPath) {
4408
4622
  const { resolved, repoRoot } = resolveCsproj(csprojPath);
4409
4623
  const solutions = findContainingSolutions(resolved, repoRoot);
4410
4624
  if (solutions.length === 0) {
4411
- console.log(chalk47.yellow("Not found in any .sln file"));
4625
+ console.log(chalk48.yellow("Not found in any .sln file"));
4412
4626
  process.exit(1);
4413
4627
  }
4414
4628
  for (const sln of solutions) {
@@ -4423,14 +4637,238 @@ function registerNetframework(program2) {
4423
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);
4424
4638
  }
4425
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
+
4426
4864
  // src/commands/prs/comment.ts
4427
4865
  import { spawnSync as spawnSync2 } from "child_process";
4428
- import { unlinkSync as unlinkSync3, writeFileSync as writeFileSync16 } from "fs";
4866
+ import { unlinkSync as unlinkSync3, writeFileSync as writeFileSync17 } from "fs";
4429
4867
  import { tmpdir as tmpdir2 } from "os";
4430
- import { join as join16 } from "path";
4868
+ import { join as join17 } from "path";
4431
4869
 
4432
4870
  // src/commands/prs/shared.ts
4433
- import { execSync as execSync20 } from "child_process";
4871
+ import { execSync as execSync22 } from "child_process";
4434
4872
  function isGhNotInstalled(error) {
4435
4873
  if (error instanceof Error) {
4436
4874
  const msg = error.message.toLowerCase();
@@ -4446,14 +4884,14 @@ function isNotFound(error) {
4446
4884
  }
4447
4885
  function getRepoInfo() {
4448
4886
  const repoInfo = JSON.parse(
4449
- execSync20("gh repo view --json owner,name", { encoding: "utf-8" })
4887
+ execSync22("gh repo view --json owner,name", { encoding: "utf-8" })
4450
4888
  );
4451
4889
  return { org: repoInfo.owner.login, repo: repoInfo.name };
4452
4890
  }
4453
4891
  function getCurrentPrNumber() {
4454
4892
  try {
4455
4893
  const prInfo = JSON.parse(
4456
- execSync20("gh pr view --json number", { encoding: "utf-8" })
4894
+ execSync22("gh pr view --json number", { encoding: "utf-8" })
4457
4895
  );
4458
4896
  return prInfo.number;
4459
4897
  } catch (error) {
@@ -4467,7 +4905,7 @@ function getCurrentPrNumber() {
4467
4905
  function getCurrentPrNodeId() {
4468
4906
  try {
4469
4907
  const prInfo = JSON.parse(
4470
- execSync20("gh pr view --json id", { encoding: "utf-8" })
4908
+ execSync22("gh pr view --json id", { encoding: "utf-8" })
4471
4909
  );
4472
4910
  return prInfo.id;
4473
4911
  } catch (error) {
@@ -4499,8 +4937,8 @@ function comment(path35, line, body) {
4499
4937
  validateLine(line);
4500
4938
  try {
4501
4939
  const prId = getCurrentPrNodeId();
4502
- const queryFile = join16(tmpdir2(), `gh-query-${Date.now()}.graphql`);
4503
- writeFileSync16(queryFile, MUTATION);
4940
+ const queryFile = join17(tmpdir2(), `gh-query-${Date.now()}.graphql`);
4941
+ writeFileSync17(queryFile, MUTATION);
4504
4942
  try {
4505
4943
  const result = spawnSync2(
4506
4944
  "gh",
@@ -4538,32 +4976,32 @@ function comment(path35, line, body) {
4538
4976
  }
4539
4977
 
4540
4978
  // src/commands/prs/fixed.ts
4541
- import { execSync as execSync22 } from "child_process";
4979
+ import { execSync as execSync24 } from "child_process";
4542
4980
 
4543
4981
  // src/commands/prs/resolveCommentWithReply.ts
4544
- import { execSync as execSync21 } from "child_process";
4545
- import { unlinkSync as unlinkSync5, writeFileSync as writeFileSync17 } from "fs";
4982
+ import { execSync as execSync23 } from "child_process";
4983
+ import { unlinkSync as unlinkSync5, writeFileSync as writeFileSync18 } from "fs";
4546
4984
  import { tmpdir as tmpdir3 } from "os";
4547
- import { join as join18 } from "path";
4985
+ import { join as join19 } from "path";
4548
4986
 
4549
4987
  // src/commands/prs/loadCommentsCache.ts
4550
- import { existsSync as existsSync22, readFileSync as readFileSync20, unlinkSync as unlinkSync4 } from "fs";
4551
- import { join as join17 } from "path";
4988
+ import { existsSync as existsSync23, readFileSync as readFileSync21, unlinkSync as unlinkSync4 } from "fs";
4989
+ import { join as join18 } from "path";
4552
4990
  import { parse as parse2 } from "yaml";
4553
4991
  function getCachePath(prNumber) {
4554
- return join17(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`);
4992
+ return join18(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`);
4555
4993
  }
4556
4994
  function loadCommentsCache(prNumber) {
4557
4995
  const cachePath = getCachePath(prNumber);
4558
- if (!existsSync22(cachePath)) {
4996
+ if (!existsSync23(cachePath)) {
4559
4997
  return null;
4560
4998
  }
4561
- const content = readFileSync20(cachePath, "utf-8");
4999
+ const content = readFileSync21(cachePath, "utf-8");
4562
5000
  return parse2(content);
4563
5001
  }
4564
5002
  function deleteCommentsCache(prNumber) {
4565
5003
  const cachePath = getCachePath(prNumber);
4566
- if (existsSync22(cachePath)) {
5004
+ if (existsSync23(cachePath)) {
4567
5005
  unlinkSync4(cachePath);
4568
5006
  console.log("No more unresolved line comments. Cache dropped.");
4569
5007
  }
@@ -4571,17 +5009,17 @@ function deleteCommentsCache(prNumber) {
4571
5009
 
4572
5010
  // src/commands/prs/resolveCommentWithReply.ts
4573
5011
  function replyToComment(org, repo, prNumber, commentId, message) {
4574
- execSync21(
5012
+ execSync23(
4575
5013
  `gh api repos/${org}/${repo}/pulls/${prNumber}/comments -f body="${message.replace(/"/g, '\\"')}" -F in_reply_to=${commentId}`,
4576
5014
  { stdio: ["inherit", "pipe", "inherit"] }
4577
5015
  );
4578
5016
  }
4579
5017
  function resolveThread(threadId) {
4580
5018
  const mutation = `mutation($threadId: ID!) { resolveReviewThread(input: {threadId: $threadId}) { thread { isResolved } } }`;
4581
- const queryFile = join18(tmpdir3(), `gh-mutation-${Date.now()}.graphql`);
4582
- writeFileSync17(queryFile, mutation);
5019
+ const queryFile = join19(tmpdir3(), `gh-mutation-${Date.now()}.graphql`);
5020
+ writeFileSync18(queryFile, mutation);
4583
5021
  try {
4584
- execSync21(
5022
+ execSync23(
4585
5023
  `gh api graphql -F query=@${queryFile} -f threadId="${threadId}"`,
4586
5024
  { stdio: ["inherit", "pipe", "inherit"] }
4587
5025
  );
@@ -4633,7 +5071,7 @@ function resolveCommentWithReply(commentId, message) {
4633
5071
  // src/commands/prs/fixed.ts
4634
5072
  function verifySha(sha) {
4635
5073
  try {
4636
- return execSync22(`git rev-parse --verify ${sha}`, {
5074
+ return execSync24(`git rev-parse --verify ${sha}`, {
4637
5075
  encoding: "utf-8"
4638
5076
  }).trim();
4639
5077
  } catch {
@@ -4659,21 +5097,21 @@ function fixed(commentId, sha) {
4659
5097
  }
4660
5098
 
4661
5099
  // src/commands/prs/listComments/index.ts
4662
- import { existsSync as existsSync23, mkdirSync as mkdirSync5, writeFileSync as writeFileSync19 } from "fs";
4663
- import { join as join20 } from "path";
5100
+ import { existsSync as existsSync24, mkdirSync as mkdirSync6, writeFileSync as writeFileSync20 } from "fs";
5101
+ import { join as join21 } from "path";
4664
5102
  import { stringify } from "yaml";
4665
5103
 
4666
5104
  // src/commands/prs/fetchThreadIds.ts
4667
- import { execSync as execSync23 } from "child_process";
4668
- import { unlinkSync as unlinkSync6, writeFileSync as writeFileSync18 } from "fs";
5105
+ import { execSync as execSync25 } from "child_process";
5106
+ import { unlinkSync as unlinkSync6, writeFileSync as writeFileSync19 } from "fs";
4669
5107
  import { tmpdir as tmpdir4 } from "os";
4670
- import { join as join19 } from "path";
5108
+ import { join as join20 } from "path";
4671
5109
  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 } } } } } } }`;
4672
5110
  function fetchThreadIds(org, repo, prNumber) {
4673
- const queryFile = join19(tmpdir4(), `gh-query-${Date.now()}.graphql`);
4674
- writeFileSync18(queryFile, THREAD_QUERY);
5111
+ const queryFile = join20(tmpdir4(), `gh-query-${Date.now()}.graphql`);
5112
+ writeFileSync19(queryFile, THREAD_QUERY);
4675
5113
  try {
4676
- const result = execSync23(
5114
+ const result = execSync25(
4677
5115
  `gh api graphql -F query=@${queryFile} -F owner="${org}" -F repo="${repo}" -F prNumber=${prNumber}`,
4678
5116
  { encoding: "utf-8" }
4679
5117
  );
@@ -4695,9 +5133,9 @@ function fetchThreadIds(org, repo, prNumber) {
4695
5133
  }
4696
5134
 
4697
5135
  // src/commands/prs/listComments/fetchReviewComments.ts
4698
- import { execSync as execSync24 } from "child_process";
5136
+ import { execSync as execSync26 } from "child_process";
4699
5137
  function fetchJson(endpoint) {
4700
- const result = execSync24(`gh api --paginate ${endpoint}`, {
5138
+ const result = execSync26(`gh api --paginate ${endpoint}`, {
4701
5139
  encoding: "utf-8"
4702
5140
  });
4703
5141
  if (!result.trim()) return [];
@@ -4739,20 +5177,20 @@ function fetchLineComments(org, repo, prNumber, threadInfo) {
4739
5177
  }
4740
5178
 
4741
5179
  // src/commands/prs/listComments/printComments.ts
4742
- import chalk48 from "chalk";
5180
+ import chalk51 from "chalk";
4743
5181
  function formatForHuman(comment2) {
4744
5182
  if (comment2.type === "review") {
4745
- const stateColor = comment2.state === "APPROVED" ? chalk48.green : comment2.state === "CHANGES_REQUESTED" ? chalk48.red : chalk48.yellow;
5183
+ const stateColor = comment2.state === "APPROVED" ? chalk51.green : comment2.state === "CHANGES_REQUESTED" ? chalk51.red : chalk51.yellow;
4746
5184
  return [
4747
- `${chalk48.cyan("Review")} by ${chalk48.bold(comment2.user)} ${stateColor(`[${comment2.state}]`)}`,
5185
+ `${chalk51.cyan("Review")} by ${chalk51.bold(comment2.user)} ${stateColor(`[${comment2.state}]`)}`,
4748
5186
  comment2.body,
4749
5187
  ""
4750
5188
  ].join("\n");
4751
5189
  }
4752
5190
  const location = comment2.line ? `:${comment2.line}` : "";
4753
5191
  return [
4754
- `${chalk48.cyan("Line comment")} by ${chalk48.bold(comment2.user)} on ${chalk48.dim(`${comment2.path}${location}`)}`,
4755
- chalk48.dim(comment2.diff_hunk.split("\n").slice(-3).join("\n")),
5192
+ `${chalk51.cyan("Line comment")} by ${chalk51.bold(comment2.user)} on ${chalk51.dim(`${comment2.path}${location}`)}`,
5193
+ chalk51.dim(comment2.diff_hunk.split("\n").slice(-3).join("\n")),
4756
5194
  comment2.body,
4757
5195
  ""
4758
5196
  ].join("\n");
@@ -4784,17 +5222,17 @@ function printComments(result) {
4784
5222
 
4785
5223
  // src/commands/prs/listComments/index.ts
4786
5224
  function writeCommentsCache(prNumber, comments) {
4787
- const assistDir = join20(process.cwd(), ".assist");
4788
- if (!existsSync23(assistDir)) {
4789
- mkdirSync5(assistDir, { recursive: true });
5225
+ const assistDir = join21(process.cwd(), ".assist");
5226
+ if (!existsSync24(assistDir)) {
5227
+ mkdirSync6(assistDir, { recursive: true });
4790
5228
  }
4791
5229
  const cacheData = {
4792
5230
  prNumber,
4793
5231
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
4794
5232
  comments
4795
5233
  };
4796
- const cachePath = join20(assistDir, `pr-${prNumber}-comments.yaml`);
4797
- writeFileSync19(cachePath, stringify(cacheData));
5234
+ const cachePath = join21(assistDir, `pr-${prNumber}-comments.yaml`);
5235
+ writeFileSync20(cachePath, stringify(cacheData));
4798
5236
  }
4799
5237
  function handleKnownErrors(error) {
4800
5238
  if (isGhNotInstalled(error)) {
@@ -4826,7 +5264,7 @@ async function listComments() {
4826
5264
  ];
4827
5265
  updateCache(prNumber, allComments);
4828
5266
  const hasLineComments = allComments.some((c) => c.type === "line");
4829
- const cachePath = hasLineComments ? join20(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`) : null;
5267
+ const cachePath = hasLineComments ? join21(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`) : null;
4830
5268
  return { comments: allComments, cachePath };
4831
5269
  } catch (error) {
4832
5270
  const handled = handleKnownErrors(error);
@@ -4836,19 +5274,19 @@ async function listComments() {
4836
5274
  }
4837
5275
 
4838
5276
  // src/commands/prs/prs/index.ts
4839
- import { execSync as execSync25 } from "child_process";
5277
+ import { execSync as execSync27 } from "child_process";
4840
5278
 
4841
5279
  // src/commands/prs/prs/displayPaginated/index.ts
4842
- import enquirer5 from "enquirer";
5280
+ import enquirer6 from "enquirer";
4843
5281
 
4844
5282
  // src/commands/prs/prs/displayPaginated/printPr.ts
4845
- import chalk49 from "chalk";
5283
+ import chalk52 from "chalk";
4846
5284
  var STATUS_MAP = {
4847
- MERGED: (pr) => pr.mergedAt ? { label: chalk49.magenta("merged"), date: pr.mergedAt } : null,
4848
- CLOSED: (pr) => pr.closedAt ? { label: chalk49.red("closed"), date: pr.closedAt } : null
5285
+ MERGED: (pr) => pr.mergedAt ? { label: chalk52.magenta("merged"), date: pr.mergedAt } : null,
5286
+ CLOSED: (pr) => pr.closedAt ? { label: chalk52.red("closed"), date: pr.closedAt } : null
4849
5287
  };
4850
5288
  function defaultStatus(pr) {
4851
- return { label: chalk49.green("opened"), date: pr.createdAt };
5289
+ return { label: chalk52.green("opened"), date: pr.createdAt };
4852
5290
  }
4853
5291
  function getStatus2(pr) {
4854
5292
  return STATUS_MAP[pr.state]?.(pr) ?? defaultStatus(pr);
@@ -4857,11 +5295,11 @@ function formatDate(dateStr) {
4857
5295
  return new Date(dateStr).toISOString().split("T")[0];
4858
5296
  }
4859
5297
  function formatPrHeader(pr, status2) {
4860
- return `${chalk49.cyan(`#${pr.number}`)} ${pr.title} ${chalk49.dim(`(${pr.author.login},`)} ${status2.label} ${chalk49.dim(`${formatDate(status2.date)})`)}`;
5298
+ return `${chalk52.cyan(`#${pr.number}`)} ${pr.title} ${chalk52.dim(`(${pr.author.login},`)} ${status2.label} ${chalk52.dim(`${formatDate(status2.date)})`)}`;
4861
5299
  }
4862
5300
  function logPrDetails(pr) {
4863
5301
  console.log(
4864
- chalk49.dim(` ${pr.changedFiles.toLocaleString()} files | ${pr.url}`)
5302
+ chalk52.dim(` ${pr.changedFiles.toLocaleString()} files | ${pr.url}`)
4865
5303
  );
4866
5304
  console.log();
4867
5305
  }
@@ -4909,7 +5347,7 @@ function parseAction(action) {
4909
5347
  }
4910
5348
  async function promptNavigation(currentPage, totalPages) {
4911
5349
  const choices = buildNavChoices(currentPage, totalPages);
4912
- const { action } = await enquirer5.prompt({
5350
+ const { action } = await enquirer6.prompt({
4913
5351
  type: "select",
4914
5352
  name: "action",
4915
5353
  message: "Navigate",
@@ -4942,7 +5380,7 @@ async function displayPaginated(pullRequests) {
4942
5380
  async function prs(options2) {
4943
5381
  const state = options2.open ? "open" : options2.closed ? "closed" : "all";
4944
5382
  try {
4945
- const result = execSync25(
5383
+ const result = execSync27(
4946
5384
  `gh pr list --state ${state} --json number,title,url,author,createdAt,mergedAt,closedAt,state,changedFiles --limit 100`,
4947
5385
  { encoding: "utf-8" }
4948
5386
  );
@@ -4965,7 +5403,7 @@ async function prs(options2) {
4965
5403
  }
4966
5404
 
4967
5405
  // src/commands/prs/wontfix.ts
4968
- import { execSync as execSync26 } from "child_process";
5406
+ import { execSync as execSync28 } from "child_process";
4969
5407
  function validateReason(reason) {
4970
5408
  const lowerReason = reason.toLowerCase();
4971
5409
  if (lowerReason.includes("claude") || lowerReason.includes("opus")) {
@@ -4982,7 +5420,7 @@ function validateShaReferences(reason) {
4982
5420
  const invalidShas = [];
4983
5421
  for (const sha of shas) {
4984
5422
  try {
4985
- execSync26(`git cat-file -t ${sha}`, { stdio: "pipe" });
5423
+ execSync28(`git cat-file -t ${sha}`, { stdio: "pipe" });
4986
5424
  } catch {
4987
5425
  invalidShas.push(sha);
4988
5426
  }
@@ -5031,7 +5469,7 @@ import { spawn as spawn3 } from "child_process";
5031
5469
  import * as path21 from "path";
5032
5470
 
5033
5471
  // src/commands/refactor/logViolations.ts
5034
- import chalk50 from "chalk";
5472
+ import chalk53 from "chalk";
5035
5473
  var DEFAULT_MAX_LINES = 100;
5036
5474
  function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
5037
5475
  if (violations.length === 0) {
@@ -5040,43 +5478,43 @@ function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
5040
5478
  }
5041
5479
  return;
5042
5480
  }
5043
- console.error(chalk50.red(`
5481
+ console.error(chalk53.red(`
5044
5482
  Refactor check failed:
5045
5483
  `));
5046
- console.error(chalk50.red(` The following files exceed ${maxLines} lines:
5484
+ console.error(chalk53.red(` The following files exceed ${maxLines} lines:
5047
5485
  `));
5048
5486
  for (const violation of violations) {
5049
- console.error(chalk50.red(` ${violation.file} (${violation.lines} lines)`));
5487
+ console.error(chalk53.red(` ${violation.file} (${violation.lines} lines)`));
5050
5488
  }
5051
5489
  console.error(
5052
- chalk50.yellow(
5490
+ chalk53.yellow(
5053
5491
  `
5054
5492
  Each file needs to be sensibly refactored, or if there is no sensible
5055
5493
  way to refactor it, ignore it with:
5056
5494
  `
5057
5495
  )
5058
5496
  );
5059
- console.error(chalk50.gray(` assist refactor ignore <file>
5497
+ console.error(chalk53.gray(` assist refactor ignore <file>
5060
5498
  `));
5061
5499
  if (process.env.CLAUDECODE) {
5062
- console.error(chalk50.cyan(`
5500
+ console.error(chalk53.cyan(`
5063
5501
  ## Extracting Code to New Files
5064
5502
  `));
5065
5503
  console.error(
5066
- chalk50.cyan(
5504
+ chalk53.cyan(
5067
5505
  ` When extracting logic from one file to another, consider where the extracted code belongs:
5068
5506
  `
5069
5507
  )
5070
5508
  );
5071
5509
  console.error(
5072
- chalk50.cyan(
5510
+ chalk53.cyan(
5073
5511
  ` 1. Keep related logic together: If the extracted code is tightly coupled to the
5074
5512
  original file's domain, create a new folder containing both the original and extracted files.
5075
5513
  `
5076
5514
  )
5077
5515
  );
5078
5516
  console.error(
5079
- chalk50.cyan(
5517
+ chalk53.cyan(
5080
5518
  ` 2. Share common utilities: If the extracted code can be reused across multiple
5081
5519
  domains, move it to a common/shared folder.
5082
5520
  `
@@ -5086,7 +5524,7 @@ Refactor check failed:
5086
5524
  }
5087
5525
 
5088
5526
  // src/commands/refactor/check/getViolations/index.ts
5089
- import { execSync as execSync27 } from "child_process";
5527
+ import { execSync as execSync29 } from "child_process";
5090
5528
  import fs15 from "fs";
5091
5529
  import { minimatch as minimatch4 } from "minimatch";
5092
5530
 
@@ -5136,7 +5574,7 @@ function getGitFiles(options2) {
5136
5574
  }
5137
5575
  const files = /* @__PURE__ */ new Set();
5138
5576
  if (options2.staged || options2.modified) {
5139
- const staged = execSync27("git diff --cached --name-only", {
5577
+ const staged = execSync29("git diff --cached --name-only", {
5140
5578
  encoding: "utf-8"
5141
5579
  });
5142
5580
  for (const file of staged.trim().split("\n").filter(Boolean)) {
@@ -5144,7 +5582,7 @@ function getGitFiles(options2) {
5144
5582
  }
5145
5583
  }
5146
5584
  if (options2.unstaged || options2.modified) {
5147
- const unstaged = execSync27("git diff --name-only", { encoding: "utf-8" });
5585
+ const unstaged = execSync29("git diff --name-only", { encoding: "utf-8" });
5148
5586
  for (const file of unstaged.trim().split("\n").filter(Boolean)) {
5149
5587
  files.add(file);
5150
5588
  }
@@ -5232,11 +5670,11 @@ async function check(pattern2, options2) {
5232
5670
 
5233
5671
  // src/commands/refactor/ignore.ts
5234
5672
  import fs16 from "fs";
5235
- import chalk51 from "chalk";
5673
+ import chalk54 from "chalk";
5236
5674
  var REFACTOR_YML_PATH2 = "refactor.yml";
5237
5675
  function ignore(file) {
5238
5676
  if (!fs16.existsSync(file)) {
5239
- console.error(chalk51.red(`Error: File does not exist: ${file}`));
5677
+ console.error(chalk54.red(`Error: File does not exist: ${file}`));
5240
5678
  process.exit(1);
5241
5679
  }
5242
5680
  const content = fs16.readFileSync(file, "utf-8");
@@ -5252,7 +5690,7 @@ function ignore(file) {
5252
5690
  fs16.writeFileSync(REFACTOR_YML_PATH2, entry);
5253
5691
  }
5254
5692
  console.log(
5255
- chalk51.green(
5693
+ chalk54.green(
5256
5694
  `Added ${file} to refactor ignore list (max ${maxLines} lines)`
5257
5695
  )
5258
5696
  );
@@ -5260,7 +5698,7 @@ function ignore(file) {
5260
5698
 
5261
5699
  // src/commands/refactor/restructure/index.ts
5262
5700
  import path30 from "path";
5263
- import chalk54 from "chalk";
5701
+ import chalk57 from "chalk";
5264
5702
 
5265
5703
  // src/commands/refactor/restructure/buildImportGraph/index.ts
5266
5704
  import path22 from "path";
@@ -5503,50 +5941,50 @@ function computeRewrites(moves, edges, allProjectFiles) {
5503
5941
 
5504
5942
  // src/commands/refactor/restructure/displayPlan.ts
5505
5943
  import path26 from "path";
5506
- import chalk52 from "chalk";
5944
+ import chalk55 from "chalk";
5507
5945
  function relPath(filePath) {
5508
5946
  return path26.relative(process.cwd(), filePath);
5509
5947
  }
5510
5948
  function displayMoves(plan) {
5511
5949
  if (plan.moves.length === 0) return;
5512
- console.log(chalk52.bold("\nFile moves:"));
5950
+ console.log(chalk55.bold("\nFile moves:"));
5513
5951
  for (const move of plan.moves) {
5514
5952
  console.log(
5515
- ` ${chalk52.red(relPath(move.from))} \u2192 ${chalk52.green(relPath(move.to))}`
5953
+ ` ${chalk55.red(relPath(move.from))} \u2192 ${chalk55.green(relPath(move.to))}`
5516
5954
  );
5517
- console.log(chalk52.dim(` ${move.reason}`));
5955
+ console.log(chalk55.dim(` ${move.reason}`));
5518
5956
  }
5519
5957
  }
5520
5958
  function displayRewrites(rewrites) {
5521
5959
  if (rewrites.length === 0) return;
5522
5960
  const affectedFiles = new Set(rewrites.map((r) => r.file));
5523
- console.log(chalk52.bold(`
5961
+ console.log(chalk55.bold(`
5524
5962
  Import rewrites (${affectedFiles.size} files):`));
5525
5963
  for (const file of affectedFiles) {
5526
- console.log(` ${chalk52.cyan(relPath(file))}:`);
5964
+ console.log(` ${chalk55.cyan(relPath(file))}:`);
5527
5965
  for (const { oldSpecifier, newSpecifier } of rewrites.filter(
5528
5966
  (r) => r.file === file
5529
5967
  )) {
5530
5968
  console.log(
5531
- ` ${chalk52.red(`"${oldSpecifier}"`)} \u2192 ${chalk52.green(`"${newSpecifier}"`)}`
5969
+ ` ${chalk55.red(`"${oldSpecifier}"`)} \u2192 ${chalk55.green(`"${newSpecifier}"`)}`
5532
5970
  );
5533
5971
  }
5534
5972
  }
5535
5973
  }
5536
5974
  function displayPlan(plan) {
5537
5975
  if (plan.warnings.length > 0) {
5538
- console.log(chalk52.yellow("\nWarnings:"));
5539
- for (const w of plan.warnings) console.log(chalk52.yellow(` ${w}`));
5976
+ console.log(chalk55.yellow("\nWarnings:"));
5977
+ for (const w of plan.warnings) console.log(chalk55.yellow(` ${w}`));
5540
5978
  }
5541
5979
  if (plan.newDirectories.length > 0) {
5542
- console.log(chalk52.bold("\nNew directories:"));
5980
+ console.log(chalk55.bold("\nNew directories:"));
5543
5981
  for (const dir of plan.newDirectories)
5544
- console.log(chalk52.green(` ${dir}/`));
5982
+ console.log(chalk55.green(` ${dir}/`));
5545
5983
  }
5546
5984
  displayMoves(plan);
5547
5985
  displayRewrites(plan.rewrites);
5548
5986
  console.log(
5549
- chalk52.dim(
5987
+ chalk55.dim(
5550
5988
  `
5551
5989
  Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rewritten`
5552
5990
  )
@@ -5556,18 +5994,18 @@ Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rew
5556
5994
  // src/commands/refactor/restructure/executePlan.ts
5557
5995
  import fs18 from "fs";
5558
5996
  import path27 from "path";
5559
- import chalk53 from "chalk";
5997
+ import chalk56 from "chalk";
5560
5998
  function executePlan(plan) {
5561
5999
  const updatedContents = applyRewrites(plan.rewrites);
5562
6000
  for (const [file, content] of updatedContents) {
5563
6001
  fs18.writeFileSync(file, content, "utf-8");
5564
6002
  console.log(
5565
- chalk53.cyan(` Rewrote imports in ${path27.relative(process.cwd(), file)}`)
6003
+ chalk56.cyan(` Rewrote imports in ${path27.relative(process.cwd(), file)}`)
5566
6004
  );
5567
6005
  }
5568
6006
  for (const dir of plan.newDirectories) {
5569
6007
  fs18.mkdirSync(dir, { recursive: true });
5570
- console.log(chalk53.green(` Created ${path27.relative(process.cwd(), dir)}/`));
6008
+ console.log(chalk56.green(` Created ${path27.relative(process.cwd(), dir)}/`));
5571
6009
  }
5572
6010
  for (const move of plan.moves) {
5573
6011
  const targetDir = path27.dirname(move.to);
@@ -5576,7 +6014,7 @@ function executePlan(plan) {
5576
6014
  }
5577
6015
  fs18.renameSync(move.from, move.to);
5578
6016
  console.log(
5579
- chalk53.white(
6017
+ chalk56.white(
5580
6018
  ` Moved ${path27.relative(process.cwd(), move.from)} \u2192 ${path27.relative(process.cwd(), move.to)}`
5581
6019
  )
5582
6020
  );
@@ -5591,7 +6029,7 @@ function removeEmptyDirectories(dirs) {
5591
6029
  if (entries.length === 0) {
5592
6030
  fs18.rmdirSync(dir);
5593
6031
  console.log(
5594
- chalk53.dim(
6032
+ chalk56.dim(
5595
6033
  ` Removed empty directory ${path27.relative(process.cwd(), dir)}`
5596
6034
  )
5597
6035
  );
@@ -5722,22 +6160,22 @@ async function restructure(pattern2, options2 = {}) {
5722
6160
  const targetPattern = pattern2 ?? "src";
5723
6161
  const files = findSourceFiles2(targetPattern);
5724
6162
  if (files.length === 0) {
5725
- console.log(chalk54.yellow("No files found matching pattern"));
6163
+ console.log(chalk57.yellow("No files found matching pattern"));
5726
6164
  return;
5727
6165
  }
5728
6166
  const tsConfigPath = path30.resolve("tsconfig.json");
5729
6167
  const plan = buildPlan(files, tsConfigPath);
5730
6168
  if (plan.moves.length === 0) {
5731
- console.log(chalk54.green("No restructuring needed"));
6169
+ console.log(chalk57.green("No restructuring needed"));
5732
6170
  return;
5733
6171
  }
5734
6172
  displayPlan(plan);
5735
6173
  if (options2.apply) {
5736
- console.log(chalk54.bold("\nApplying changes..."));
6174
+ console.log(chalk57.bold("\nApplying changes..."));
5737
6175
  executePlan(plan);
5738
- console.log(chalk54.green("\nRestructuring complete"));
6176
+ console.log(chalk57.green("\nRestructuring complete"));
5739
6177
  } else {
5740
- console.log(chalk54.dim("\nDry run. Use --apply to execute."));
6178
+ console.log(chalk57.dim("\nDry run. Use --apply to execute."));
5741
6179
  }
5742
6180
  }
5743
6181
 
@@ -5760,8 +6198,8 @@ function registerRefactor(program2) {
5760
6198
  }
5761
6199
 
5762
6200
  // src/commands/transcript/shared.ts
5763
- import { existsSync as existsSync24, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
5764
- import { basename as basename4, join as join21, relative } from "path";
6201
+ import { existsSync as existsSync25, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
6202
+ import { basename as basename4, join as join22, relative } from "path";
5765
6203
  import * as readline2 from "readline";
5766
6204
  var DATE_PREFIX_REGEX = /^\d{4}-\d{2}-\d{2}/;
5767
6205
  function getDatePrefix(daysOffset = 0) {
@@ -5776,10 +6214,10 @@ function isValidDatePrefix(filename) {
5776
6214
  return DATE_PREFIX_REGEX.test(filename);
5777
6215
  }
5778
6216
  function collectFiles(dir, extension) {
5779
- if (!existsSync24(dir)) return [];
6217
+ if (!existsSync25(dir)) return [];
5780
6218
  const results = [];
5781
6219
  for (const entry of readdirSync3(dir)) {
5782
- const fullPath = join21(dir, entry);
6220
+ const fullPath = join22(dir, entry);
5783
6221
  if (statSync2(fullPath).isDirectory()) {
5784
6222
  results.push(...collectFiles(fullPath, extension));
5785
6223
  } else if (entry.endsWith(extension)) {
@@ -5873,14 +6311,14 @@ async function configure() {
5873
6311
  }
5874
6312
 
5875
6313
  // src/commands/transcript/format/index.ts
5876
- import { existsSync as existsSync26 } from "fs";
6314
+ import { existsSync as existsSync27 } from "fs";
5877
6315
 
5878
6316
  // src/commands/transcript/format/fixInvalidDatePrefixes/index.ts
5879
- import { dirname as dirname15, join as join23 } from "path";
6317
+ import { dirname as dirname15, join as join24 } from "path";
5880
6318
 
5881
6319
  // src/commands/transcript/format/fixInvalidDatePrefixes/promptForDateFix.ts
5882
6320
  import { renameSync } from "fs";
5883
- import { join as join22 } from "path";
6321
+ import { join as join23 } from "path";
5884
6322
  async function resolveDate(rl, choice) {
5885
6323
  if (choice === "1") return getDatePrefix(0);
5886
6324
  if (choice === "2") return getDatePrefix(-1);
@@ -5895,7 +6333,7 @@ async function resolveDate(rl, choice) {
5895
6333
  }
5896
6334
  function renameWithPrefix(vttDir, vttFile, prefix2) {
5897
6335
  const newFilename = `${prefix2}.${vttFile}`;
5898
- renameSync(join22(vttDir, vttFile), join22(vttDir, newFilename));
6336
+ renameSync(join23(vttDir, vttFile), join23(vttDir, newFilename));
5899
6337
  console.log(`Renamed to: ${newFilename}`);
5900
6338
  return newFilename;
5901
6339
  }
@@ -5929,12 +6367,12 @@ async function fixInvalidDatePrefixes(vttFiles) {
5929
6367
  const vttFileDir = dirname15(vttFile.absolutePath);
5930
6368
  const newFilename = await promptForDateFix(vttFile.filename, vttFileDir);
5931
6369
  if (newFilename) {
5932
- const newRelativePath = join23(
6370
+ const newRelativePath = join24(
5933
6371
  dirname15(vttFile.relativePath),
5934
6372
  newFilename
5935
6373
  );
5936
6374
  vttFiles[i] = {
5937
- absolutePath: join23(vttFileDir, newFilename),
6375
+ absolutePath: join24(vttFileDir, newFilename),
5938
6376
  relativePath: newRelativePath,
5939
6377
  filename: newFilename
5940
6378
  };
@@ -5947,8 +6385,8 @@ async function fixInvalidDatePrefixes(vttFiles) {
5947
6385
  }
5948
6386
 
5949
6387
  // src/commands/transcript/format/processVttFile/index.ts
5950
- import { existsSync as existsSync25, mkdirSync as mkdirSync6, readFileSync as readFileSync21, writeFileSync as writeFileSync20 } from "fs";
5951
- import { basename as basename5, dirname as dirname16, join as join24 } from "path";
6388
+ import { existsSync as existsSync26, mkdirSync as mkdirSync7, readFileSync as readFileSync22, writeFileSync as writeFileSync21 } from "fs";
6389
+ import { basename as basename5, dirname as dirname16, join as join25 } from "path";
5952
6390
 
5953
6391
  // src/commands/transcript/cleanText.ts
5954
6392
  function cleanText(text) {
@@ -6158,22 +6596,22 @@ function toMdFilename(vttFilename) {
6158
6596
  return `${basename5(vttFilename, ".vtt").replace(/\s*Transcription\s*/g, " ").trim()}.md`;
6159
6597
  }
6160
6598
  function resolveOutputDir(relativeDir, transcriptsDir) {
6161
- return relativeDir === "." ? transcriptsDir : join24(transcriptsDir, relativeDir);
6599
+ return relativeDir === "." ? transcriptsDir : join25(transcriptsDir, relativeDir);
6162
6600
  }
6163
6601
  function buildOutputPaths(vttFile, transcriptsDir) {
6164
6602
  const mdFile = toMdFilename(vttFile.filename);
6165
6603
  const relativeDir = dirname16(vttFile.relativePath);
6166
6604
  const outputDir = resolveOutputDir(relativeDir, transcriptsDir);
6167
- const outputPath = join24(outputDir, mdFile);
6605
+ const outputPath = join25(outputDir, mdFile);
6168
6606
  return { outputDir, outputPath, mdFile, relativeDir };
6169
6607
  }
6170
6608
  function logSkipped(relativeDir, mdFile) {
6171
- console.log(`Skipping (already exists): ${join24(relativeDir, mdFile)}`);
6609
+ console.log(`Skipping (already exists): ${join25(relativeDir, mdFile)}`);
6172
6610
  return "skipped";
6173
6611
  }
6174
6612
  function ensureDirectory(dir, label2) {
6175
- if (!existsSync25(dir)) {
6176
- mkdirSync6(dir, { recursive: true });
6613
+ if (!existsSync26(dir)) {
6614
+ mkdirSync7(dir, { recursive: true });
6177
6615
  console.log(`Created ${label2}: ${dir}`);
6178
6616
  }
6179
6617
  }
@@ -6195,10 +6633,10 @@ function logReduction(cueCount, messageCount) {
6195
6633
  }
6196
6634
  function readAndParseCues(inputPath) {
6197
6635
  console.log(`Reading: ${inputPath}`);
6198
- return processCues(readFileSync21(inputPath, "utf-8"));
6636
+ return processCues(readFileSync22(inputPath, "utf-8"));
6199
6637
  }
6200
6638
  function writeFormatted(outputPath, content) {
6201
- writeFileSync20(outputPath, content, "utf-8");
6639
+ writeFileSync21(outputPath, content, "utf-8");
6202
6640
  console.log(`Written: ${outputPath}`);
6203
6641
  }
6204
6642
  function convertVttToMarkdown(inputPath, outputPath) {
@@ -6208,7 +6646,7 @@ function convertVttToMarkdown(inputPath, outputPath) {
6208
6646
  logReduction(cues.length, chatMessages.length);
6209
6647
  }
6210
6648
  function tryProcessVtt(vttFile, paths) {
6211
- if (existsSync25(paths.outputPath))
6649
+ if (existsSync26(paths.outputPath))
6212
6650
  return logSkipped(paths.relativeDir, paths.mdFile);
6213
6651
  convertVttToMarkdown(vttFile.absolutePath, paths.outputPath);
6214
6652
  return "processed";
@@ -6234,7 +6672,7 @@ function processAllFiles(vttFiles, transcriptsDir) {
6234
6672
  logSummary(counts);
6235
6673
  }
6236
6674
  function requireVttDir(vttDir) {
6237
- if (!existsSync26(vttDir)) {
6675
+ if (!existsSync27(vttDir)) {
6238
6676
  console.error(`VTT directory not found: ${vttDir}`);
6239
6677
  process.exit(1);
6240
6678
  }
@@ -6266,28 +6704,28 @@ async function format() {
6266
6704
  }
6267
6705
 
6268
6706
  // src/commands/transcript/summarise/index.ts
6269
- import { existsSync as existsSync28 } from "fs";
6270
- import { basename as basename6, dirname as dirname18, join as join26, relative as relative2 } from "path";
6707
+ import { existsSync as existsSync29 } from "fs";
6708
+ import { basename as basename6, dirname as dirname18, join as join27, relative as relative2 } from "path";
6271
6709
 
6272
6710
  // src/commands/transcript/summarise/processStagedFile/index.ts
6273
6711
  import {
6274
- existsSync as existsSync27,
6275
- mkdirSync as mkdirSync7,
6276
- readFileSync as readFileSync22,
6712
+ existsSync as existsSync28,
6713
+ mkdirSync as mkdirSync8,
6714
+ readFileSync as readFileSync23,
6277
6715
  renameSync as renameSync2,
6278
6716
  rmSync
6279
6717
  } from "fs";
6280
- import { dirname as dirname17, join as join25 } from "path";
6718
+ import { dirname as dirname17, join as join26 } from "path";
6281
6719
 
6282
6720
  // src/commands/transcript/summarise/processStagedFile/validateStagedContent.ts
6283
- import chalk55 from "chalk";
6721
+ import chalk58 from "chalk";
6284
6722
  var FULL_TRANSCRIPT_REGEX = /^\[Full Transcript\]\(([^)]+)\)/;
6285
6723
  function validateStagedContent(filename, content) {
6286
6724
  const firstLine = content.split("\n")[0];
6287
6725
  const match = firstLine.match(FULL_TRANSCRIPT_REGEX);
6288
6726
  if (!match) {
6289
6727
  console.error(
6290
- chalk55.red(
6728
+ chalk58.red(
6291
6729
  `Staged file ${filename} missing [Full Transcript](<path>) link on first line.`
6292
6730
  )
6293
6731
  );
@@ -6296,7 +6734,7 @@ function validateStagedContent(filename, content) {
6296
6734
  const contentAfterLink = content.slice(firstLine.length).trim();
6297
6735
  if (!contentAfterLink) {
6298
6736
  console.error(
6299
- chalk55.red(
6737
+ chalk58.red(
6300
6738
  `Staged file ${filename} has no summary content after the transcript link.`
6301
6739
  )
6302
6740
  );
@@ -6306,9 +6744,9 @@ function validateStagedContent(filename, content) {
6306
6744
  }
6307
6745
 
6308
6746
  // src/commands/transcript/summarise/processStagedFile/index.ts
6309
- var STAGING_DIR = join25(process.cwd(), ".assist", "transcript");
6747
+ var STAGING_DIR = join26(process.cwd(), ".assist", "transcript");
6310
6748
  function processStagedFile() {
6311
- if (!existsSync27(STAGING_DIR)) {
6749
+ if (!existsSync28(STAGING_DIR)) {
6312
6750
  return false;
6313
6751
  }
6314
6752
  const stagedFiles = findMdFilesRecursive(STAGING_DIR);
@@ -6317,7 +6755,7 @@ function processStagedFile() {
6317
6755
  }
6318
6756
  const { transcriptsDir, summaryDir } = getTranscriptConfig();
6319
6757
  const stagedFile = stagedFiles[0];
6320
- const content = readFileSync22(stagedFile.absolutePath, "utf-8");
6758
+ const content = readFileSync23(stagedFile.absolutePath, "utf-8");
6321
6759
  validateStagedContent(stagedFile.filename, content);
6322
6760
  const stagedBaseName = getTranscriptBaseName(stagedFile.filename);
6323
6761
  const transcriptFiles = findMdFilesRecursive(transcriptsDir);
@@ -6330,10 +6768,10 @@ function processStagedFile() {
6330
6768
  );
6331
6769
  process.exit(1);
6332
6770
  }
6333
- const destPath = join25(summaryDir, matchingTranscript.relativePath);
6771
+ const destPath = join26(summaryDir, matchingTranscript.relativePath);
6334
6772
  const destDir = dirname17(destPath);
6335
- if (!existsSync27(destDir)) {
6336
- mkdirSync7(destDir, { recursive: true });
6773
+ if (!existsSync28(destDir)) {
6774
+ mkdirSync8(destDir, { recursive: true });
6337
6775
  }
6338
6776
  renameSync2(stagedFile.absolutePath, destPath);
6339
6777
  const remaining = findMdFilesRecursive(STAGING_DIR);
@@ -6346,7 +6784,7 @@ function processStagedFile() {
6346
6784
  // src/commands/transcript/summarise/index.ts
6347
6785
  function buildRelativeKey(relativePath, baseName) {
6348
6786
  const relDir = dirname18(relativePath);
6349
- return relDir === "." ? baseName : join26(relDir, baseName);
6787
+ return relDir === "." ? baseName : join27(relDir, baseName);
6350
6788
  }
6351
6789
  function buildSummaryIndex(summaryDir) {
6352
6790
  const summaryFiles = findMdFilesRecursive(summaryDir);
@@ -6359,7 +6797,7 @@ function buildSummaryIndex(summaryDir) {
6359
6797
  function summarise2() {
6360
6798
  processStagedFile();
6361
6799
  const { transcriptsDir, summaryDir } = getTranscriptConfig();
6362
- if (!existsSync28(transcriptsDir)) {
6800
+ if (!existsSync29(transcriptsDir)) {
6363
6801
  console.log("No transcripts directory found.");
6364
6802
  return;
6365
6803
  }
@@ -6380,8 +6818,8 @@ function summarise2() {
6380
6818
  }
6381
6819
  const next2 = missing[0];
6382
6820
  const outputFilename = `${getTranscriptBaseName(next2.filename)}.md`;
6383
- const outputPath = join26(STAGING_DIR, outputFilename);
6384
- const summaryFileDir = join26(summaryDir, dirname18(next2.relativePath));
6821
+ const outputPath = join27(STAGING_DIR, outputFilename);
6822
+ const summaryFileDir = join27(summaryDir, dirname18(next2.relativePath));
6385
6823
  const relativeTranscriptPath = encodeURI(
6386
6824
  relative2(summaryFileDir, next2.absolutePath).replace(/\\/g, "/")
6387
6825
  );
@@ -6427,50 +6865,50 @@ function registerVerify(program2) {
6427
6865
 
6428
6866
  // src/commands/voice/devices.ts
6429
6867
  import { spawnSync as spawnSync3 } from "child_process";
6430
- import { join as join28 } from "path";
6868
+ import { join as join29 } from "path";
6431
6869
 
6432
6870
  // src/commands/voice/shared.ts
6433
- import { homedir as homedir6 } from "os";
6434
- import { dirname as dirname19, join as join27 } from "path";
6871
+ import { homedir as homedir7 } from "os";
6872
+ import { dirname as dirname19, join as join28 } from "path";
6435
6873
  import { fileURLToPath as fileURLToPath6 } from "url";
6436
- var __dirname7 = dirname19(fileURLToPath6(import.meta.url));
6437
- var VOICE_DIR = join27(homedir6(), ".assist", "voice");
6874
+ var __dirname6 = dirname19(fileURLToPath6(import.meta.url));
6875
+ var VOICE_DIR = join28(homedir7(), ".assist", "voice");
6438
6876
  var voicePaths = {
6439
6877
  dir: VOICE_DIR,
6440
- pid: join27(VOICE_DIR, "voice.pid"),
6441
- log: join27(VOICE_DIR, "voice.log"),
6442
- venv: join27(VOICE_DIR, ".venv"),
6443
- lock: join27(VOICE_DIR, "voice.lock")
6878
+ pid: join28(VOICE_DIR, "voice.pid"),
6879
+ log: join28(VOICE_DIR, "voice.log"),
6880
+ venv: join28(VOICE_DIR, ".venv"),
6881
+ lock: join28(VOICE_DIR, "voice.lock")
6444
6882
  };
6445
6883
  function getPythonDir() {
6446
- return join27(__dirname7, "commands", "voice", "python");
6884
+ return join28(__dirname6, "commands", "voice", "python");
6447
6885
  }
6448
6886
  function getVenvPython() {
6449
- return process.platform === "win32" ? join27(voicePaths.venv, "Scripts", "python.exe") : join27(voicePaths.venv, "bin", "python");
6887
+ return process.platform === "win32" ? join28(voicePaths.venv, "Scripts", "python.exe") : join28(voicePaths.venv, "bin", "python");
6450
6888
  }
6451
6889
  function getLockDir() {
6452
6890
  const config = loadConfig();
6453
6891
  return config.voice?.lockDir ?? VOICE_DIR;
6454
6892
  }
6455
6893
  function getLockFile() {
6456
- return join27(getLockDir(), "voice.lock");
6894
+ return join28(getLockDir(), "voice.lock");
6457
6895
  }
6458
6896
 
6459
6897
  // src/commands/voice/devices.ts
6460
6898
  function devices() {
6461
- const script = join28(getPythonDir(), "list_devices.py");
6899
+ const script = join29(getPythonDir(), "list_devices.py");
6462
6900
  spawnSync3(getVenvPython(), [script], { stdio: "inherit" });
6463
6901
  }
6464
6902
 
6465
6903
  // src/commands/voice/logs.ts
6466
- import { existsSync as existsSync29, readFileSync as readFileSync23 } from "fs";
6904
+ import { existsSync as existsSync30, readFileSync as readFileSync24 } from "fs";
6467
6905
  function logs(options2) {
6468
- if (!existsSync29(voicePaths.log)) {
6906
+ if (!existsSync30(voicePaths.log)) {
6469
6907
  console.log("No voice log file found");
6470
6908
  return;
6471
6909
  }
6472
6910
  const count = Number.parseInt(options2.lines ?? "150", 10);
6473
- const content = readFileSync23(voicePaths.log, "utf-8").trim();
6911
+ const content = readFileSync24(voicePaths.log, "utf-8").trim();
6474
6912
  if (!content) {
6475
6913
  console.log("Voice log is empty");
6476
6914
  return;
@@ -6492,13 +6930,13 @@ function logs(options2) {
6492
6930
 
6493
6931
  // src/commands/voice/setup.ts
6494
6932
  import { spawnSync as spawnSync4 } from "child_process";
6495
- import { mkdirSync as mkdirSync9 } from "fs";
6496
- import { join as join30 } from "path";
6933
+ import { mkdirSync as mkdirSync10 } from "fs";
6934
+ import { join as join31 } from "path";
6497
6935
 
6498
6936
  // src/commands/voice/checkLockFile.ts
6499
- import { execSync as execSync28 } from "child_process";
6500
- import { existsSync as existsSync30, mkdirSync as mkdirSync8, readFileSync as readFileSync24, writeFileSync as writeFileSync21 } from "fs";
6501
- import { join as join29 } from "path";
6937
+ import { execSync as execSync30 } from "child_process";
6938
+ import { existsSync as existsSync31, mkdirSync as mkdirSync9, readFileSync as readFileSync25, writeFileSync as writeFileSync22 } from "fs";
6939
+ import { join as join30 } from "path";
6502
6940
  function isProcessAlive(pid) {
6503
6941
  try {
6504
6942
  process.kill(pid, 0);
@@ -6509,9 +6947,9 @@ function isProcessAlive(pid) {
6509
6947
  }
6510
6948
  function checkLockFile() {
6511
6949
  const lockFile = getLockFile();
6512
- if (!existsSync30(lockFile)) return;
6950
+ if (!existsSync31(lockFile)) return;
6513
6951
  try {
6514
- const lock = JSON.parse(readFileSync24(lockFile, "utf-8"));
6952
+ const lock = JSON.parse(readFileSync25(lockFile, "utf-8"));
6515
6953
  if (lock.pid && isProcessAlive(lock.pid)) {
6516
6954
  console.error(
6517
6955
  `Voice daemon already running (PID ${lock.pid}, env: ${lock.env}). Stop it first with: assist voice stop`
@@ -6522,10 +6960,10 @@ function checkLockFile() {
6522
6960
  }
6523
6961
  }
6524
6962
  function bootstrapVenv() {
6525
- if (existsSync30(getVenvPython())) return;
6963
+ if (existsSync31(getVenvPython())) return;
6526
6964
  console.log("Setting up Python environment...");
6527
6965
  const pythonDir = getPythonDir();
6528
- execSync28(
6966
+ execSync30(
6529
6967
  `uv sync --project "${pythonDir}" --extra runtime --no-install-project`,
6530
6968
  {
6531
6969
  stdio: "inherit",
@@ -6535,8 +6973,8 @@ function bootstrapVenv() {
6535
6973
  }
6536
6974
  function writeLockFile(pid) {
6537
6975
  const lockFile = getLockFile();
6538
- mkdirSync8(join29(lockFile, ".."), { recursive: true });
6539
- writeFileSync21(
6976
+ mkdirSync9(join30(lockFile, ".."), { recursive: true });
6977
+ writeFileSync22(
6540
6978
  lockFile,
6541
6979
  JSON.stringify({
6542
6980
  pid,
@@ -6548,10 +6986,10 @@ function writeLockFile(pid) {
6548
6986
 
6549
6987
  // src/commands/voice/setup.ts
6550
6988
  function setup() {
6551
- mkdirSync9(voicePaths.dir, { recursive: true });
6989
+ mkdirSync10(voicePaths.dir, { recursive: true });
6552
6990
  bootstrapVenv();
6553
6991
  console.log("\nDownloading models...\n");
6554
- const script = join30(getPythonDir(), "setup_models.py");
6992
+ const script = join31(getPythonDir(), "setup_models.py");
6555
6993
  const result = spawnSync4(getVenvPython(), [script], {
6556
6994
  stdio: "inherit",
6557
6995
  env: { ...process.env, VOICE_LOG_FILE: voicePaths.log }
@@ -6564,8 +7002,8 @@ function setup() {
6564
7002
 
6565
7003
  // src/commands/voice/start.ts
6566
7004
  import { spawn as spawn4 } from "child_process";
6567
- import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync22 } from "fs";
6568
- import { join as join31 } from "path";
7005
+ import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync23 } from "fs";
7006
+ import { join as join32 } from "path";
6569
7007
 
6570
7008
  // src/commands/voice/buildDaemonEnv.ts
6571
7009
  function buildDaemonEnv(options2) {
@@ -6593,17 +7031,17 @@ function spawnBackground(python, script, env) {
6593
7031
  console.error("Failed to start voice daemon");
6594
7032
  process.exit(1);
6595
7033
  }
6596
- writeFileSync22(voicePaths.pid, String(pid));
7034
+ writeFileSync23(voicePaths.pid, String(pid));
6597
7035
  writeLockFile(pid);
6598
7036
  console.log(`Voice daemon started (PID ${pid})`);
6599
7037
  }
6600
7038
  function start2(options2) {
6601
- mkdirSync10(voicePaths.dir, { recursive: true });
7039
+ mkdirSync11(voicePaths.dir, { recursive: true });
6602
7040
  checkLockFile();
6603
7041
  bootstrapVenv();
6604
7042
  const debug = options2.debug || options2.foreground || process.platform === "win32";
6605
7043
  const env = buildDaemonEnv({ debug });
6606
- const script = join31(getPythonDir(), "voice_daemon.py");
7044
+ const script = join32(getPythonDir(), "voice_daemon.py");
6607
7045
  const python = getVenvPython();
6608
7046
  if (options2.foreground) {
6609
7047
  spawnForeground(python, script, env);
@@ -6613,7 +7051,7 @@ function start2(options2) {
6613
7051
  }
6614
7052
 
6615
7053
  // src/commands/voice/status.ts
6616
- import { existsSync as existsSync31, readFileSync as readFileSync25 } from "fs";
7054
+ import { existsSync as existsSync32, readFileSync as readFileSync26 } from "fs";
6617
7055
  function isProcessAlive2(pid) {
6618
7056
  try {
6619
7057
  process.kill(pid, 0);
@@ -6623,16 +7061,16 @@ function isProcessAlive2(pid) {
6623
7061
  }
6624
7062
  }
6625
7063
  function readRecentLogs(count) {
6626
- if (!existsSync31(voicePaths.log)) return [];
6627
- const lines = readFileSync25(voicePaths.log, "utf-8").trim().split("\n");
7064
+ if (!existsSync32(voicePaths.log)) return [];
7065
+ const lines = readFileSync26(voicePaths.log, "utf-8").trim().split("\n");
6628
7066
  return lines.slice(-count);
6629
7067
  }
6630
7068
  function status() {
6631
- if (!existsSync31(voicePaths.pid)) {
7069
+ if (!existsSync32(voicePaths.pid)) {
6632
7070
  console.log("Voice daemon: not running (no PID file)");
6633
7071
  return;
6634
7072
  }
6635
- const pid = Number.parseInt(readFileSync25(voicePaths.pid, "utf-8").trim(), 10);
7073
+ const pid = Number.parseInt(readFileSync26(voicePaths.pid, "utf-8").trim(), 10);
6636
7074
  const alive = isProcessAlive2(pid);
6637
7075
  console.log(`Voice daemon: ${alive ? "running" : "dead"} (PID ${pid})`);
6638
7076
  const recent = readRecentLogs(5);
@@ -6651,13 +7089,13 @@ function status() {
6651
7089
  }
6652
7090
 
6653
7091
  // src/commands/voice/stop.ts
6654
- import { existsSync as existsSync32, readFileSync as readFileSync26, unlinkSync as unlinkSync7 } from "fs";
7092
+ import { existsSync as existsSync33, readFileSync as readFileSync27, unlinkSync as unlinkSync7 } from "fs";
6655
7093
  function stop() {
6656
- if (!existsSync32(voicePaths.pid)) {
7094
+ if (!existsSync33(voicePaths.pid)) {
6657
7095
  console.log("Voice daemon is not running (no PID file)");
6658
7096
  return;
6659
7097
  }
6660
- const pid = Number.parseInt(readFileSync26(voicePaths.pid, "utf-8").trim(), 10);
7098
+ const pid = Number.parseInt(readFileSync27(voicePaths.pid, "utf-8").trim(), 10);
6661
7099
  try {
6662
7100
  process.kill(pid, "SIGTERM");
6663
7101
  console.log(`Sent SIGTERM to voice daemon (PID ${pid})`);
@@ -6670,7 +7108,7 @@ function stop() {
6670
7108
  }
6671
7109
  try {
6672
7110
  const lockFile = getLockFile();
6673
- if (existsSync32(lockFile)) unlinkSync7(lockFile);
7111
+ if (existsSync33(lockFile)) unlinkSync7(lockFile);
6674
7112
  } catch {
6675
7113
  }
6676
7114
  console.log("Voice daemon stopped");
@@ -6689,14 +7127,14 @@ function registerVoice(program2) {
6689
7127
 
6690
7128
  // src/commands/roam/auth.ts
6691
7129
  import { randomBytes } from "crypto";
6692
- import chalk56 from "chalk";
7130
+ import chalk59 from "chalk";
6693
7131
 
6694
7132
  // src/lib/openBrowser.ts
6695
- import { execSync as execSync29 } from "child_process";
7133
+ import { execSync as execSync31 } from "child_process";
6696
7134
  function tryExec(commands) {
6697
7135
  for (const cmd of commands) {
6698
7136
  try {
6699
- execSync29(cmd);
7137
+ execSync31(cmd);
6700
7138
  return true;
6701
7139
  } catch {
6702
7140
  }
@@ -6824,7 +7262,7 @@ async function exchangeToken(params) {
6824
7262
  }
6825
7263
 
6826
7264
  // src/commands/roam/promptCredentials.ts
6827
- import enquirer6 from "enquirer";
7265
+ import enquirer7 from "enquirer";
6828
7266
  function censor(value) {
6829
7267
  const visible = value.slice(-4);
6830
7268
  return `${"*".repeat(value.length - 4)}${visible}`;
@@ -6833,7 +7271,7 @@ function label(name, existing) {
6833
7271
  return existing ? `${name} (${censor(existing)})` : name;
6834
7272
  }
6835
7273
  async function promptField(name, existing) {
6836
- const { value } = await enquirer6.prompt({
7274
+ const { value } = await enquirer7.prompt({
6837
7275
  type: "input",
6838
7276
  name: "value",
6839
7277
  message: `${label(name, existing)}:`,
@@ -6841,7 +7279,7 @@ async function promptField(name, existing) {
6841
7279
  });
6842
7280
  return value.trim() || existing || "";
6843
7281
  }
6844
- async function promptCredentials(existing) {
7282
+ async function promptCredentials2(existing) {
6845
7283
  const clientId = await promptField("Client ID", existing?.clientId);
6846
7284
  const clientSecret = await promptField(
6847
7285
  "Client Secret",
@@ -6856,7 +7294,7 @@ async function promptCredentials(existing) {
6856
7294
  // src/commands/roam/auth.ts
6857
7295
  async function auth() {
6858
7296
  const config = loadGlobalConfigRaw();
6859
- const { clientId, clientSecret } = await promptCredentials(
7297
+ const { clientId, clientSecret } = await promptCredentials2(
6860
7298
  config.roam
6861
7299
  );
6862
7300
  const existingRoam = config.roam ?? {};
@@ -6864,13 +7302,13 @@ async function auth() {
6864
7302
  saveGlobalConfig(config);
6865
7303
  const state = randomBytes(16).toString("hex");
6866
7304
  console.log(
6867
- chalk56.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
7305
+ chalk59.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
6868
7306
  );
6869
- console.log(chalk56.white("http://localhost:14523/callback\n"));
6870
- console.log(chalk56.blue("Opening browser for authorization..."));
6871
- console.log(chalk56.dim("Waiting for authorization callback..."));
7307
+ console.log(chalk59.white("http://localhost:14523/callback\n"));
7308
+ console.log(chalk59.blue("Opening browser for authorization..."));
7309
+ console.log(chalk59.dim("Waiting for authorization callback..."));
6872
7310
  const { code, redirectUri } = await authorizeInBrowser(clientId, state);
6873
- console.log(chalk56.dim("Exchanging code for tokens..."));
7311
+ console.log(chalk59.dim("Exchanging code for tokens..."));
6874
7312
  const tokens = await exchangeToken({
6875
7313
  code,
6876
7314
  clientId,
@@ -6886,7 +7324,7 @@ async function auth() {
6886
7324
  };
6887
7325
  saveGlobalConfig(config);
6888
7326
  console.log(
6889
- chalk56.green("Roam credentials and tokens saved to ~/.assist.yml")
7327
+ chalk59.green("Roam credentials and tokens saved to ~/.assist.yml")
6890
7328
  );
6891
7329
  }
6892
7330
 
@@ -6933,8 +7371,8 @@ function resolveParams(params, cliArgs) {
6933
7371
  }
6934
7372
 
6935
7373
  // src/commands/run/add.ts
6936
- import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync23 } from "fs";
6937
- import { join as join32 } from "path";
7374
+ import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync24 } from "fs";
7375
+ import { join as join33 } from "path";
6938
7376
  function findAddIndex() {
6939
7377
  const addIndex = process.argv.indexOf("add");
6940
7378
  if (addIndex === -1 || addIndex + 2 >= process.argv.length) return -1;
@@ -6988,19 +7426,19 @@ function saveNewRunConfig(name, command, args) {
6988
7426
  saveConfig(config);
6989
7427
  }
6990
7428
  function createCommandFile(name) {
6991
- const dir = join32(".claude", "commands");
6992
- mkdirSync11(dir, { recursive: true });
7429
+ const dir = join33(".claude", "commands");
7430
+ mkdirSync12(dir, { recursive: true });
6993
7431
  const content = `---
6994
7432
  description: Run ${name}
6995
7433
  ---
6996
7434
 
6997
7435
  Run \`assist run ${name} $ARGUMENTS 2>&1\`.
6998
7436
  `;
6999
- const filePath = join32(dir, `${name}.md`);
7000
- writeFileSync23(filePath, content);
7437
+ const filePath = join33(dir, `${name}.md`);
7438
+ writeFileSync24(filePath, content);
7001
7439
  console.log(`Created command file: ${filePath}`);
7002
7440
  }
7003
- function add2() {
7441
+ function add3() {
7004
7442
  const { name, command, args } = requireParsedArgs();
7005
7443
  saveNewRunConfig(name, command, args);
7006
7444
  createCommandFile(name);
@@ -7074,14 +7512,14 @@ function run2(name, args) {
7074
7512
  }
7075
7513
 
7076
7514
  // src/commands/statusLine.ts
7077
- import chalk57 from "chalk";
7515
+ import chalk60 from "chalk";
7078
7516
  function formatNumber(num) {
7079
7517
  return num.toLocaleString("en-US");
7080
7518
  }
7081
7519
  function colorizePercent(pct) {
7082
7520
  const label2 = `${pct}%`;
7083
- if (pct > 80) return chalk57.red(label2);
7084
- if (pct > 40) return chalk57.yellow(label2);
7521
+ if (pct > 80) return chalk60.red(label2);
7522
+ if (pct > 40) return chalk60.yellow(label2);
7085
7523
  return label2;
7086
7524
  }
7087
7525
  async function statusLine() {
@@ -7107,7 +7545,7 @@ import { fileURLToPath as fileURLToPath7 } from "url";
7107
7545
  // src/commands/sync/syncClaudeMd.ts
7108
7546
  import * as fs21 from "fs";
7109
7547
  import * as path31 from "path";
7110
- import chalk58 from "chalk";
7548
+ import chalk61 from "chalk";
7111
7549
  async function syncClaudeMd(claudeDir, targetBase) {
7112
7550
  const source = path31.join(claudeDir, "CLAUDE.md");
7113
7551
  const target = path31.join(targetBase, "CLAUDE.md");
@@ -7116,12 +7554,12 @@ async function syncClaudeMd(claudeDir, targetBase) {
7116
7554
  const targetContent = fs21.readFileSync(target, "utf-8");
7117
7555
  if (sourceContent !== targetContent) {
7118
7556
  console.log(
7119
- chalk58.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
7557
+ chalk61.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
7120
7558
  );
7121
7559
  console.log();
7122
7560
  printDiff(targetContent, sourceContent);
7123
7561
  const confirm = await promptConfirm(
7124
- chalk58.red("Overwrite existing CLAUDE.md?"),
7562
+ chalk61.red("Overwrite existing CLAUDE.md?"),
7125
7563
  false
7126
7564
  );
7127
7565
  if (!confirm) {
@@ -7137,7 +7575,7 @@ async function syncClaudeMd(claudeDir, targetBase) {
7137
7575
  // src/commands/sync/syncSettings.ts
7138
7576
  import * as fs22 from "fs";
7139
7577
  import * as path32 from "path";
7140
- import chalk59 from "chalk";
7578
+ import chalk62 from "chalk";
7141
7579
  async function syncSettings(claudeDir, targetBase, options2) {
7142
7580
  const source = path32.join(claudeDir, "settings.json");
7143
7581
  const target = path32.join(targetBase, "settings.json");
@@ -7153,14 +7591,14 @@ async function syncSettings(claudeDir, targetBase, options2) {
7153
7591
  if (mergedContent !== normalizedTarget) {
7154
7592
  if (!options2?.yes) {
7155
7593
  console.log(
7156
- chalk59.yellow(
7594
+ chalk62.yellow(
7157
7595
  "\n\u26A0\uFE0F Warning: settings.json differs from existing file"
7158
7596
  )
7159
7597
  );
7160
7598
  console.log();
7161
7599
  printDiff(targetContent, mergedContent);
7162
7600
  const confirm = await promptConfirm(
7163
- chalk59.red("Overwrite existing settings.json?"),
7601
+ chalk62.red("Overwrite existing settings.json?"),
7164
7602
  false
7165
7603
  );
7166
7604
  if (!confirm) {
@@ -7176,9 +7614,9 @@ async function syncSettings(claudeDir, targetBase, options2) {
7176
7614
 
7177
7615
  // src/commands/sync.ts
7178
7616
  var __filename4 = fileURLToPath7(import.meta.url);
7179
- var __dirname8 = path33.dirname(__filename4);
7617
+ var __dirname7 = path33.dirname(__filename4);
7180
7618
  async function sync(options2) {
7181
- const claudeDir = path33.join(__dirname8, "..", "claude");
7619
+ const claudeDir = path33.join(__dirname7, "..", "claude");
7182
7620
  const targetBase = path33.join(os.homedir(), ".claude");
7183
7621
  syncCommands(claudeDir, targetBase);
7184
7622
  await syncSettings(claudeDir, targetBase, { yes: options2?.yes });
@@ -7197,7 +7635,7 @@ function syncCommands(claudeDir, targetBase) {
7197
7635
  }
7198
7636
 
7199
7637
  // src/commands/update.ts
7200
- import { execSync as execSync30 } from "child_process";
7638
+ import { execSync as execSync32 } from "child_process";
7201
7639
  import * as path34 from "path";
7202
7640
  function isGlobalNpmInstall(dir) {
7203
7641
  try {
@@ -7205,7 +7643,7 @@ function isGlobalNpmInstall(dir) {
7205
7643
  if (resolved.split(path34.sep).includes("node_modules")) {
7206
7644
  return true;
7207
7645
  }
7208
- const globalPrefix = execSync30("npm prefix -g", { stdio: "pipe" }).toString().trim();
7646
+ const globalPrefix = execSync32("npm prefix -g", { stdio: "pipe" }).toString().trim();
7209
7647
  return resolved.toLowerCase().startsWith(path34.resolve(globalPrefix).toLowerCase());
7210
7648
  } catch {
7211
7649
  return false;
@@ -7216,18 +7654,18 @@ async function update() {
7216
7654
  console.log(`Assist is installed at: ${installDir}`);
7217
7655
  if (isGitRepo(installDir)) {
7218
7656
  console.log("Detected git repo installation, pulling latest...");
7219
- execSync30("git pull", { cwd: installDir, stdio: "inherit" });
7657
+ execSync32("git pull", { cwd: installDir, stdio: "inherit" });
7220
7658
  console.log("Installing dependencies...");
7221
- execSync30("npm i", { cwd: installDir, stdio: "inherit" });
7659
+ execSync32("npm i", { cwd: installDir, stdio: "inherit" });
7222
7660
  console.log("Building...");
7223
- execSync30("npm run build", { cwd: installDir, stdio: "inherit" });
7661
+ execSync32("npm run build", { cwd: installDir, stdio: "inherit" });
7224
7662
  console.log("Syncing commands...");
7225
- execSync30("assist sync", { stdio: "inherit" });
7663
+ execSync32("assist sync", { stdio: "inherit" });
7226
7664
  } else if (isGlobalNpmInstall(installDir)) {
7227
7665
  console.log("Detected global npm installation, updating...");
7228
- execSync30("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
7666
+ execSync32("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
7229
7667
  console.log("Syncing commands...");
7230
- execSync30("assist sync", { stdio: "inherit" });
7668
+ execSync32("assist sync", { stdio: "inherit" });
7231
7669
  } else {
7232
7670
  console.error(
7233
7671
  "Could not determine installation method. Expected a git repo or global npm install."
@@ -7241,7 +7679,7 @@ var program = new Command();
7241
7679
  program.name("assist").description("CLI application").version(package_default.version);
7242
7680
  program.command("sync").description("Copy command files to ~/.claude/commands").option("-y, --yes", "Overwrite settings.json without prompting").action((options2) => sync(options2));
7243
7681
  program.command("init").description("Initialize VS Code and verify configurations").action(init4);
7244
- program.command("commit").description("Create a git commit with validation").argument("<args...>", "status | <files...> <message>").action(commit);
7682
+ program.command("commit").description("Create a git commit with validation").argument("<args...>", "status | <message> [files...]").action(commit);
7245
7683
  var configCommand = program.command("config").description("View and modify assist.yml configuration");
7246
7684
  configCommand.command("set <key> <value>").description("Set a config value (e.g. commit.push true)").action(configSet);
7247
7685
  configCommand.command("get <key>").description("Get a config value").action(configGet);
@@ -7253,7 +7691,7 @@ runCommand.command("list").description("List configured run commands").action(li
7253
7691
  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(
7254
7692
  "after",
7255
7693
  '\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'
7256
- ).allowUnknownOption().allowExcessArguments().action(() => add2());
7694
+ ).allowUnknownOption().allowExcessArguments().action(() => add3());
7257
7695
  registerNew(program);
7258
7696
  var lintCommand = program.command("lint").description("Run lint checks for conventions not enforced by biomejs").action(lint);
7259
7697
  lintCommand.command("init").description("Initialize Biome with standard linter config").action(init);
@@ -7265,6 +7703,7 @@ program.command("notify").description(
7265
7703
  ).action(notify);
7266
7704
  program.command("update").description("Update assist to the latest version and sync commands").action(update);
7267
7705
  registerCliHook(program);
7706
+ registerJira(program);
7268
7707
  registerPrs(program);
7269
7708
  registerRoam(program);
7270
7709
  registerBacklog(program);
@@ -7274,6 +7713,7 @@ registerDevlog(program);
7274
7713
  registerDeploy(program);
7275
7714
  registerComplexity(program);
7276
7715
  registerNetframework(program);
7716
+ registerNews(program);
7277
7717
  registerTranscript(program);
7278
7718
  registerVoice(program);
7279
7719
  program.parse();