@staff0rd/assist 0.643.5 → 0.644.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command } from "commander";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@staff0rd/assist",
9
- version: "0.643.5",
9
+ version: "0.644.1",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -12991,16 +12991,33 @@ async function runGhImage(filePath, cwd) {
12991
12991
  async function readRequestBuffer(req, limit) {
12992
12992
  const chunks = [];
12993
12993
  let size = 0;
12994
+ let overLimit = false;
12994
12995
  for await (const chunk of req) {
12995
12996
  const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
12996
12997
  size += buf.length;
12998
+ if (overLimit) continue;
12997
12999
  if (size > limit) {
12998
- req.destroy();
12999
- return null;
13000
+ overLimit = true;
13001
+ chunks.length = 0;
13002
+ continue;
13000
13003
  }
13001
13004
  chunks.push(buf);
13002
13005
  }
13003
- return Buffer.concat(chunks);
13006
+ return overLimit ? null : Buffer.concat(chunks);
13007
+ }
13008
+
13009
+ // src/commands/sessions/web/uploadSizeLimit.ts
13010
+ var MAX_IMAGE_BYTES = 25 * 1024 * 1024;
13011
+ var MAX_VIDEO_BYTES = 10 * 1024 * 1024;
13012
+ function uploadSizeLimit(contentType) {
13013
+ const mime = contentType.split(";")[0].trim().toLowerCase();
13014
+ return mime.startsWith("video/") ? {
13015
+ maxBytes: MAX_VIDEO_BYTES,
13016
+ tooLargeMessage: "Video too large (max 10MB)."
13017
+ } : {
13018
+ maxBytes: MAX_IMAGE_BYTES,
13019
+ tooLargeMessage: "Image too large (max 25MB)."
13020
+ };
13004
13021
  }
13005
13022
 
13006
13023
  // src/commands/sessions/web/writeTempImage.ts
@@ -13047,16 +13064,16 @@ async function writeTempImage(name, contentType, body) {
13047
13064
  }
13048
13065
 
13049
13066
  // src/commands/sessions/web/uploadPrImage.ts
13050
- var MAX_BYTES = 25 * 1024 * 1024;
13051
13067
  async function uploadPrImage(req, res) {
13052
13068
  const cwd = getCwdParam(req, res);
13053
13069
  if (!cwd) return;
13054
13070
  const url = new URL(req.url ?? "/", "http://localhost");
13055
13071
  const name = url.searchParams.get("name") ?? "";
13056
13072
  const contentType = req.headers["content-type"] ?? "";
13057
- const body = await readRequestBuffer(req, MAX_BYTES);
13073
+ const { maxBytes, tooLargeMessage } = uploadSizeLimit(contentType);
13074
+ const body = await readRequestBuffer(req, maxBytes);
13058
13075
  if (!body) {
13059
- respondJson(res, 413, { error: "Image too large (max 25MB)." });
13076
+ respondJson(res, 413, { error: tooLargeMessage });
13060
13077
  return;
13061
13078
  }
13062
13079
  if (body.length === 0) {
@@ -22792,7 +22809,7 @@ function registerCreateIssue(issueCommand) {
22792
22809
  []
22793
22810
  ).addHelpText(
22794
22811
  "after",
22795
- "\nThere is no What/Why/How template: an issue reports a problem, and the target repo's own issue template is unknowable from here. Write the body as the repo's maintainers would expect.\nIn an assist web session the title and body are previewed for approve/reject first (with inline comments); nothing is created until it is approved.\nThe reviewer may also drop or paste screenshots into the pane; on approval these are appended to the issue body under a ## Screenshots section automatically, and on rejection they are discarded \u2014 so never author that section yourself.\n--type, --parent, --project, --status and --label are all resolved before the preview, so an unknown name, an unreadable parent, a missing project scope or --status without --project creates nothing.\nA bare --parent number is read against --repo, or the current repo; a parent in another repository is allowed.\nThe project scope is needed for --project: gh auth refresh -h github.com -s project"
22812
+ "\nThere is no What/Why/How template: an issue reports a problem, and the target repo's own issue template is unknowable from here. Write the body as the repo's maintainers would expect.\nIn an assist web session the title and body are previewed for approve/reject first (with inline comments); nothing is created until it is approved.\nThe reviewer may also drop or paste screenshots or video into the pane; on approval these are appended to the issue body under a ## Screenshots section automatically, and on rejection they are discarded \u2014 so never author that section yourself.\n--type, --parent, --project, --status and --label are all resolved before the preview, so an unknown name, an unreadable parent, a missing project scope or --status without --project creates nothing.\nA bare --parent number is read against --repo, or the current repo; a parent in another repository is allowed.\nThe project scope is needed for --project: gh auth refresh -h github.com -s project"
22796
22813
  ).action(createIssue);
22797
22814
  }
22798
22815
 
@@ -25228,25 +25245,25 @@ function isVisibleText(t) {
25228
25245
  return /[a-zA-Z]{3,}/.test(t);
25229
25246
  }
25230
25247
  var isHashtag = (t) => /^#[A-Za-z0-9_]+$/.test(t);
25231
- function collectRscText(v, resolve25, sink2, seen) {
25248
+ function collectRscText(v, resolve25, sink3, seen) {
25232
25249
  if (v == null) return;
25233
25250
  if (typeof v === "string") {
25234
25251
  if (isRscRef(v)) {
25235
25252
  if (!seen.has(v)) {
25236
25253
  seen.add(v);
25237
- collectRscText(resolve25(v), resolve25, sink2, seen);
25254
+ collectRscText(resolve25(v), resolve25, sink3, seen);
25238
25255
  }
25239
- } else if (isHashtag(v)) sink2.hashtags.push(v);
25240
- else if (isVisibleText(v)) sink2.text.push(v);
25256
+ } else if (isHashtag(v)) sink3.hashtags.push(v);
25257
+ else if (isVisibleText(v)) sink3.text.push(v);
25241
25258
  return;
25242
25259
  }
25243
25260
  if (Array.isArray(v)) {
25244
- for (const x of v) collectRscText(x, resolve25, sink2, seen);
25261
+ for (const x of v) collectRscText(x, resolve25, sink3, seen);
25245
25262
  return;
25246
25263
  }
25247
25264
  if (typeof v === "object") {
25248
25265
  for (const val of Object.values(v)) {
25249
- collectRscText(val, resolve25, sink2, seen);
25266
+ collectRscText(val, resolve25, sink3, seen);
25250
25267
  }
25251
25268
  }
25252
25269
  }
@@ -25285,9 +25302,9 @@ function buildMentionMap(rows, resolve25) {
25285
25302
  if (!url || o.children == null) return;
25286
25303
  const slug = slugFromProfileUrl(url);
25287
25304
  if (!slug || map.has(slug)) return;
25288
- const sink2 = { text: [], hashtags: [] };
25289
- collectRscText(o.children, resolve25, sink2, /* @__PURE__ */ new Set());
25290
- const name = sink2.text.join(" ").replace(/\s+/g, " ").trim();
25305
+ const sink3 = { text: [], hashtags: [] };
25306
+ collectRscText(o.children, resolve25, sink3, /* @__PURE__ */ new Set());
25307
+ const name = sink3.text.join(" ").replace(/\s+/g, " ").trim();
25291
25308
  map.set(slug, name ? { slug, name, url } : { slug, url });
25292
25309
  });
25293
25310
  return map;
@@ -25388,8 +25405,8 @@ function walkPostRow(v, resolve25, raw) {
25388
25405
  if (a) raw.related.push(a[0]);
25389
25406
  }
25390
25407
  if (isCommentary(o)) {
25391
- const sink2 = { text: raw.text, hashtags: raw.hashtags };
25392
- collectRscText(o.children, resolve25, sink2, /* @__PURE__ */ new Set());
25408
+ const sink3 = { text: raw.text, hashtags: raw.hashtags };
25409
+ collectRscText(o.children, resolve25, sink3, /* @__PURE__ */ new Set());
25393
25410
  }
25394
25411
  for (const val of Object.values(o)) walkPostRow(val, resolve25, raw);
25395
25412
  }
@@ -27212,8 +27229,8 @@ on rejection it exits non-zero with the reason. The reviewer may also attach
27212
27229
  inline comments to specific spans of the preview; on rejection these are printed
27213
27230
  as numbered quoted-span + note pairs on stderr. Address every comment (and the
27214
27231
  reason), then run the command again to re-preview the revised PR. Repeat until it
27215
- is approved. The reviewer may also drop or paste screenshots into the pane; on
27216
- approval these are appended to the PR body under a ## Screenshots section
27232
+ is approved. The reviewer may also drop or paste screenshots or video into the
27233
+ pane; on approval these are appended to the PR body under a ## Screenshots section
27217
27234
  automatically (they are discarded on rejection), so you never author that section
27218
27235
  yourself. Just compose the sections and run the command.`;
27219
27236
  function raiseHelpText(promptJira, draft) {
@@ -27460,12 +27477,12 @@ import chalk190 from "chalk";
27460
27477
 
27461
27478
  // src/shared/createConnectionAuth.ts
27462
27479
  import chalk185 from "chalk";
27463
- function listConnections(connections, format) {
27480
+ function listConnections(connections, format2) {
27464
27481
  if (connections.length === 0) {
27465
27482
  console.log("No connections configured.");
27466
27483
  } else {
27467
27484
  for (const c of connections) {
27468
- console.log(format(c));
27485
+ console.log(format2(c));
27469
27486
  }
27470
27487
  }
27471
27488
  }
@@ -30488,6 +30505,77 @@ function renderEntry(entry, frame) {
30488
30505
  return `${SPINNER_FRAMES2[frame]} ${entry.text}`;
30489
30506
  }
30490
30507
 
30508
+ // src/commands/review/startReviewLog.ts
30509
+ import { format } from "util";
30510
+
30511
+ // src/commands/review/createReviewLogSink.ts
30512
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync22 } from "fs";
30513
+ import { join as join73 } from "path";
30514
+
30515
+ // src/shared/stripAnsi.ts
30516
+ var ANSI = new RegExp(
30517
+ `${String.fromCharCode(27)}\\[[0-9;?]*[ -/]*[@-~]`,
30518
+ "g"
30519
+ );
30520
+ function stripAnsi(text18) {
30521
+ return text18.replace(ANSI, "");
30522
+ }
30523
+
30524
+ // src/commands/review/createReviewLogSink.ts
30525
+ var LOG_FILE = "review.log";
30526
+ function createReviewLogSink() {
30527
+ let logPath2;
30528
+ let buffered = [];
30529
+ return {
30530
+ append(line) {
30531
+ const stripped = stripAnsi(line);
30532
+ if (!logPath2) {
30533
+ buffered.push(stripped);
30534
+ return;
30535
+ }
30536
+ appendFileSync2(logPath2, `${stripped}
30537
+ `);
30538
+ },
30539
+ attach(reviewDir) {
30540
+ mkdirSync22(reviewDir, { recursive: true });
30541
+ logPath2 = join73(reviewDir, LOG_FILE);
30542
+ const lines2 = [
30543
+ "",
30544
+ `=== ${(/* @__PURE__ */ new Date()).toISOString()} ===`,
30545
+ `$ ${process.argv.slice(1).join(" ")}`,
30546
+ ...buffered
30547
+ ];
30548
+ buffered = [];
30549
+ appendFileSync2(logPath2, `${lines2.join("\n")}
30550
+ `);
30551
+ }
30552
+ };
30553
+ }
30554
+
30555
+ // src/commands/review/startReviewLog.ts
30556
+ var sink2 = createReviewLogSink();
30557
+ var patched = false;
30558
+ function appendReviewLog(line) {
30559
+ sink2.append(line);
30560
+ }
30561
+ function attachReviewLog(reviewDir) {
30562
+ sink2.attach(reviewDir);
30563
+ }
30564
+ function patch(method) {
30565
+ const original = console[method].bind(console);
30566
+ console[method] = (...args) => {
30567
+ original(...args);
30568
+ appendReviewLog(format(...args));
30569
+ };
30570
+ }
30571
+ function startReviewLog() {
30572
+ if (patched) return;
30573
+ patched = true;
30574
+ patch("log");
30575
+ patch("error");
30576
+ patch("warn");
30577
+ }
30578
+
30491
30579
  // src/commands/review/MultiSpinner.ts
30492
30580
  var TICK_MS2 = 80;
30493
30581
  var MultiSpinner = class {
@@ -30532,6 +30620,7 @@ var MultiSpinner = class {
30532
30620
  entry.state = state;
30533
30621
  if (text18 !== void 0) entry.text = text18;
30534
30622
  entry.elapsedStart = void 0;
30623
+ appendReviewLog(renderEntry(entry, 0));
30535
30624
  this.render();
30536
30625
  this.maybeFinish();
30537
30626
  }
@@ -31442,6 +31531,7 @@ function runPostSynthesis(synthesisPath, prInfo, options2) {
31442
31531
  async function reviewPr(repoRoot2, options2) {
31443
31532
  const context = gatherChangedContext();
31444
31533
  const paths = setupReviewDir(repoRoot2, context, options2.force ?? false);
31534
+ attachReviewLog(paths.reviewDir);
31445
31535
  const synthesisOk = await runReviewPipeline(paths, {
31446
31536
  verbose: options2.verbose ?? false
31447
31537
  });
@@ -31485,6 +31575,7 @@ function validateCheckoutOnly(options2) {
31485
31575
  }
31486
31576
  async function review(options2 = {}) {
31487
31577
  validateOptions(options2);
31578
+ startReviewLog();
31488
31579
  const invokedIn = resolveRepoRoot();
31489
31580
  if (options2.checkoutOnly && options2.number)
31490
31581
  return checkoutOnlySession(options2.number);
@@ -32133,7 +32224,7 @@ function parseSlackThreadRef(value) {
32133
32224
  }
32134
32225
 
32135
32226
  // src/commands/slack/postSlackMessage.ts
32136
- import { mkdirSync as mkdirSync22, writeFileSync as writeFileSync42 } from "fs";
32227
+ import { mkdirSync as mkdirSync23, writeFileSync as writeFileSync42 } from "fs";
32137
32228
 
32138
32229
  // src/commands/slack/reviewProposedSlackMessage.ts
32139
32230
  import { randomUUID as randomUUID17 } from "crypto";
@@ -32158,11 +32249,11 @@ async function reviewProposedSlackMessage(target, body, workingPath) {
32158
32249
  }
32159
32250
 
32160
32251
  // src/commands/slack/slackWorkingFile.ts
32161
- import { join as join73 } from "path";
32252
+ import { join as join74 } from "path";
32162
32253
  function slackWorkingFile(channel) {
32163
32254
  const slug = channel.replace(/^[#@]/, "").toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "channel";
32164
- const dir = join73(getStoreDir(), "slack");
32165
- return { dir, bodyPath: join73(dir, `${slug}.md`) };
32255
+ const dir = join74(getStoreDir(), "slack");
32256
+ return { dir, bodyPath: join74(dir, `${slug}.md`) };
32166
32257
  }
32167
32258
 
32168
32259
  // src/commands/slack/postSlackMessage.ts
@@ -32183,7 +32274,7 @@ async function postSlackMessage(channelArg, options2) {
32183
32274
  }
32184
32275
  const threadTs = options2.thread;
32185
32276
  const { dir, bodyPath } = slackWorkingFile(channel);
32186
- mkdirSync22(dir, { recursive: true });
32277
+ mkdirSync23(dir, { recursive: true });
32187
32278
  writeFileSync42(bodyPath, `${body}
32188
32279
  `);
32189
32280
  await reviewProposedSlackMessage({ channel, threadTs }, body, bodyPath);
@@ -33178,21 +33269,21 @@ var FORMATS = ["md", "vtt"];
33178
33269
  function isCleanFormat(value) {
33179
33270
  return FORMATS.includes(value);
33180
33271
  }
33181
- function serialise(cues, format, timestamps) {
33182
- return format === "vtt" ? formatVtt(cues) : formatChatLog(cuesToChatMessages(cues), { timestamps });
33272
+ function serialise(cues, format2, timestamps) {
33273
+ return format2 === "vtt" ? formatVtt(cues) : formatChatLog(cuesToChatMessages(cues), { timestamps });
33183
33274
  }
33184
33275
  function clean(file, options2 = {}) {
33185
- const format = options2.format ?? "md";
33186
- if (!isCleanFormat(format)) {
33276
+ const format2 = options2.format ?? "md";
33277
+ if (!isCleanFormat(format2)) {
33187
33278
  console.error(
33188
- `Error: --format must be one of: ${FORMATS.join(", ")} (got: ${format})`
33279
+ `Error: --format must be one of: ${FORMATS.join(", ")} (got: ${format2})`
33189
33280
  );
33190
33281
  process.exit(1);
33191
33282
  }
33192
33283
  const timestamps = options2.timestamps ?? false;
33193
- if (timestamps && format !== "md") {
33284
+ if (timestamps && format2 !== "md") {
33194
33285
  console.error(
33195
- `Error: --timestamps applies only to --format md (got: ${format})`
33286
+ `Error: --timestamps applies only to --format md (got: ${format2})`
33196
33287
  );
33197
33288
  process.exit(1);
33198
33289
  }
@@ -33205,7 +33296,7 @@ function clean(file, options2 = {}) {
33205
33296
  console.error(`Error: no cues found in: ${file}`);
33206
33297
  process.exit(1);
33207
33298
  }
33208
- console.log(serialise(cues, format, timestamps));
33299
+ console.log(serialise(cues, format2, timestamps));
33209
33300
  }
33210
33301
 
33211
33302
  // src/commands/transcript/shared.ts
@@ -33280,20 +33371,20 @@ async function configure() {
33280
33371
 
33281
33372
  // src/commands/transcript/list.ts
33282
33373
  import { existsSync as existsSync72, readdirSync as readdirSync20, statSync as statSync11 } from "fs";
33283
- import { join as join84 } from "path";
33374
+ import { join as join85 } from "path";
33284
33375
  function list4() {
33285
33376
  const { vttDir } = getTranscriptConfig();
33286
33377
  if (!existsSync72(vttDir)) return;
33287
33378
  for (const entry of readdirSync20(vttDir)) {
33288
33379
  if (!entry.endsWith(".vtt")) continue;
33289
- if (statSync11(join84(vttDir, entry)).isDirectory()) continue;
33380
+ if (statSync11(join85(vttDir, entry)).isDirectory()) continue;
33290
33381
  console.log(entry);
33291
33382
  }
33292
33383
  }
33293
33384
 
33294
33385
  // src/commands/transcript/move.ts
33295
- import { existsSync as existsSync73, mkdirSync as mkdirSync28, renameSync as renameSync2, writeFileSync as writeFileSync46 } from "fs";
33296
- import { basename as basename22, join as join85 } from "path";
33386
+ import { existsSync as existsSync73, mkdirSync as mkdirSync29, renameSync as renameSync2, writeFileSync as writeFileSync46 } from "fs";
33387
+ import { basename as basename22, join as join86 } from "path";
33297
33388
 
33298
33389
  // src/commands/transcript/convertVttToMarkdown.ts
33299
33390
  function convertVttToMarkdown(inputPath) {
@@ -33303,9 +33394,9 @@ function convertVttToMarkdown(inputPath) {
33303
33394
  // src/commands/transcript/move.ts
33304
33395
  var DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
33305
33396
  function archiveRawVtt(vttDir, sourcePath, filename) {
33306
- const processedDir = join85(vttDir, "processed");
33307
- mkdirSync28(processedDir, { recursive: true });
33308
- renameSync2(sourcePath, join85(processedDir, filename));
33397
+ const processedDir = join86(vttDir, "processed");
33398
+ mkdirSync29(processedDir, { recursive: true });
33399
+ renameSync2(sourcePath, join86(processedDir, filename));
33309
33400
  }
33310
33401
  function move(file, options2) {
33311
33402
  const { date, client } = options2;
@@ -33315,19 +33406,19 @@ function move(file, options2) {
33315
33406
  }
33316
33407
  const { vttDir, transcriptsDir, summaryDir } = getTranscriptConfig();
33317
33408
  const filename = basename22(file);
33318
- const sourcePath = join85(vttDir, filename);
33409
+ const sourcePath = join86(vttDir, filename);
33319
33410
  if (!existsSync73(sourcePath)) {
33320
33411
  console.error(`Error: VTT file not found: ${sourcePath}`);
33321
33412
  process.exit(1);
33322
33413
  }
33323
33414
  const base = basename22(filename, ".vtt").replace(/ Transcription$/, "");
33324
33415
  const outputName = `${date} ${base}.md`;
33325
- const formattedDir = join85(transcriptsDir, client);
33326
- mkdirSync28(formattedDir, { recursive: true });
33327
- const formattedPath = join85(formattedDir, outputName);
33416
+ const formattedDir = join86(transcriptsDir, client);
33417
+ mkdirSync29(formattedDir, { recursive: true });
33418
+ const formattedPath = join86(formattedDir, outputName);
33328
33419
  writeFileSync46(formattedPath, convertVttToMarkdown(sourcePath), "utf8");
33329
33420
  archiveRawVtt(vttDir, sourcePath, filename);
33330
- const summaryPath = join85(summaryDir, client, outputName);
33421
+ const summaryPath = join86(summaryDir, client, outputName);
33331
33422
  console.log(`Formatted transcript: ${formattedPath}`);
33332
33423
  console.log(`Summary target: ${summaryPath}`);
33333
33424
  }
@@ -33641,38 +33732,38 @@ function registerVerify(program2) {
33641
33732
 
33642
33733
  // src/commands/voice/devices.ts
33643
33734
  import { spawnSync as spawnSync9 } from "child_process";
33644
- import { join as join87 } from "path";
33735
+ import { join as join88 } from "path";
33645
33736
 
33646
33737
  // src/commands/voice/shared.ts
33647
33738
  import { homedir as homedir25 } from "os";
33648
- import { dirname as dirname36, join as join86 } from "path";
33739
+ import { dirname as dirname36, join as join87 } from "path";
33649
33740
  import { fileURLToPath as fileURLToPath9 } from "url";
33650
33741
  var __dirname6 = dirname36(fileURLToPath9(import.meta.url));
33651
- var VOICE_DIR = join86(homedir25(), ".assist", "voice");
33742
+ var VOICE_DIR = join87(homedir25(), ".assist", "voice");
33652
33743
  var voicePaths = {
33653
33744
  dir: VOICE_DIR,
33654
- pid: join86(VOICE_DIR, "voice.pid"),
33655
- log: join86(VOICE_DIR, "voice.log"),
33656
- venv: join86(VOICE_DIR, ".venv"),
33657
- lock: join86(VOICE_DIR, "voice.lock")
33745
+ pid: join87(VOICE_DIR, "voice.pid"),
33746
+ log: join87(VOICE_DIR, "voice.log"),
33747
+ venv: join87(VOICE_DIR, ".venv"),
33748
+ lock: join87(VOICE_DIR, "voice.lock")
33658
33749
  };
33659
33750
  function getPythonDir() {
33660
- return join86(__dirname6, "commands", "voice", "python");
33751
+ return join87(__dirname6, "commands", "voice", "python");
33661
33752
  }
33662
33753
  function getVenvPython() {
33663
- return process.platform === "win32" ? join86(voicePaths.venv, "Scripts", "python.exe") : join86(voicePaths.venv, "bin", "python");
33754
+ return process.platform === "win32" ? join87(voicePaths.venv, "Scripts", "python.exe") : join87(voicePaths.venv, "bin", "python");
33664
33755
  }
33665
33756
  function getLockDir() {
33666
33757
  const config = loadConfig();
33667
33758
  return config.voice?.lockDir ?? VOICE_DIR;
33668
33759
  }
33669
33760
  function getLockFile() {
33670
- return join86(getLockDir(), "voice.lock");
33761
+ return join87(getLockDir(), "voice.lock");
33671
33762
  }
33672
33763
 
33673
33764
  // src/commands/voice/devices.ts
33674
33765
  function devices() {
33675
- const script = join87(getPythonDir(), "list_devices.py");
33766
+ const script = join88(getPythonDir(), "list_devices.py");
33676
33767
  spawnSync9(getVenvPython(), [script], { stdio: "inherit" });
33677
33768
  }
33678
33769
 
@@ -33706,13 +33797,13 @@ function logs(options2) {
33706
33797
 
33707
33798
  // src/commands/voice/setup.ts
33708
33799
  import { spawnSync as spawnSync10 } from "child_process";
33709
- import { mkdirSync as mkdirSync30 } from "fs";
33710
- import { join as join89 } from "path";
33800
+ import { mkdirSync as mkdirSync31 } from "fs";
33801
+ import { join as join90 } from "path";
33711
33802
 
33712
33803
  // src/commands/voice/checkLockFile.ts
33713
33804
  import { execSync as execSync60 } from "child_process";
33714
- import { existsSync as existsSync76, mkdirSync as mkdirSync29, readFileSync as readFileSync58, writeFileSync as writeFileSync48 } from "fs";
33715
- import { join as join88 } from "path";
33805
+ import { existsSync as existsSync76, mkdirSync as mkdirSync30, readFileSync as readFileSync58, writeFileSync as writeFileSync48 } from "fs";
33806
+ import { join as join89 } from "path";
33716
33807
  function isProcessAlive2(pid) {
33717
33808
  try {
33718
33809
  process.kill(pid, 0);
@@ -33749,7 +33840,7 @@ function bootstrapVenv() {
33749
33840
  }
33750
33841
  function writeLockFile(pid) {
33751
33842
  const lockFile = getLockFile();
33752
- mkdirSync29(join88(lockFile, ".."), { recursive: true });
33843
+ mkdirSync30(join89(lockFile, ".."), { recursive: true });
33753
33844
  writeFileSync48(
33754
33845
  lockFile,
33755
33846
  JSON.stringify({
@@ -33762,10 +33853,10 @@ function writeLockFile(pid) {
33762
33853
 
33763
33854
  // src/commands/voice/setup.ts
33764
33855
  function setup() {
33765
- mkdirSync30(voicePaths.dir, { recursive: true });
33856
+ mkdirSync31(voicePaths.dir, { recursive: true });
33766
33857
  bootstrapVenv();
33767
33858
  console.log("\nDownloading models...\n");
33768
- const script = join89(getPythonDir(), "setup_models.py");
33859
+ const script = join90(getPythonDir(), "setup_models.py");
33769
33860
  const result = spawnSync10(getVenvPython(), [script], {
33770
33861
  stdio: "inherit",
33771
33862
  env: { ...process.env, VOICE_LOG_FILE: voicePaths.log }
@@ -33778,8 +33869,8 @@ function setup() {
33778
33869
 
33779
33870
  // src/commands/voice/start.ts
33780
33871
  import { spawn as spawn8 } from "child_process";
33781
- import { mkdirSync as mkdirSync31, writeFileSync as writeFileSync49 } from "fs";
33782
- import { join as join90 } from "path";
33872
+ import { mkdirSync as mkdirSync32, writeFileSync as writeFileSync49 } from "fs";
33873
+ import { join as join91 } from "path";
33783
33874
 
33784
33875
  // src/commands/voice/buildDaemonEnv.ts
33785
33876
  function buildDaemonEnv(options2) {
@@ -33812,12 +33903,12 @@ function spawnBackground(python, script, env) {
33812
33903
  console.log(`Voice daemon started (PID ${pid})`);
33813
33904
  }
33814
33905
  function start2(options2) {
33815
- mkdirSync31(voicePaths.dir, { recursive: true });
33906
+ mkdirSync32(voicePaths.dir, { recursive: true });
33816
33907
  checkLockFile();
33817
33908
  bootstrapVenv();
33818
33909
  const debug = options2.debug || options2.foreground || process.platform === "win32";
33819
33910
  const env = buildDaemonEnv({ debug });
33820
- const script = join90(getPythonDir(), "voice_daemon.py");
33911
+ const script = join91(getPythonDir(), "voice_daemon.py");
33821
33912
  const python = getVenvPython();
33822
33913
  if (options2.foreground) {
33823
33914
  spawnForeground(python, script, env);
@@ -33948,11 +34039,11 @@ function changedPaths(from, cwd) {
33948
34039
  }
33949
34040
 
33950
34041
  // src/commands/watch/readBuiltVersion.ts
33951
- import { join as join91 } from "path";
34042
+ import { join as join92 } from "path";
33952
34043
  function readBuiltVersion(cwd) {
33953
34044
  try {
33954
34045
  const root = runGit3(["rev-parse", "--show-toplevel"], cwd);
33955
- return readPackageJson(join91(root, "package.json")).version ?? "unknown";
34046
+ return readPackageJson(join92(root, "package.json")).version ?? "unknown";
33956
34047
  } catch {
33957
34048
  return "unknown";
33958
34049
  }
@@ -34313,13 +34404,13 @@ import { existsSync as existsSync81 } from "fs";
34313
34404
  // src/commands/run/resolveCommand.ts
34314
34405
  import { execFileSync as execFileSync18 } from "child_process";
34315
34406
  import { existsSync as existsSync80 } from "fs";
34316
- import { dirname as dirname37, join as join92, resolve as resolve20 } from "path";
34407
+ import { dirname as dirname37, join as join93, resolve as resolve20 } from "path";
34317
34408
  function resolveCommand2(command) {
34318
34409
  if (process.platform !== "win32" || command !== "bash") return command;
34319
34410
  try {
34320
34411
  const gitPath = execFileSync18("where", ["git"], { encoding: "utf8" }).trim().split("\r\n")[0];
34321
34412
  const gitRoot = resolve20(dirname37(gitPath), "..");
34322
- const gitBash = join92(gitRoot, "bin", "bash.exe");
34413
+ const gitBash = join93(gitRoot, "bin", "bash.exe");
34323
34414
  if (existsSync80(gitBash)) return gitBash;
34324
34415
  } catch {
34325
34416
  return command;
@@ -34747,7 +34838,7 @@ async function auth() {
34747
34838
  // src/commands/roam/postRoamActivity.ts
34748
34839
  import { execFileSync as execFileSync20 } from "child_process";
34749
34840
  import { readdirSync as readdirSync21, readFileSync as readFileSync61, statSync as statSync12 } from "fs";
34750
- import { join as join93 } from "path";
34841
+ import { join as join94 } from "path";
34751
34842
  function findPortFile(roamDir) {
34752
34843
  let entries;
34753
34844
  try {
@@ -34756,7 +34847,7 @@ function findPortFile(roamDir) {
34756
34847
  return void 0;
34757
34848
  }
34758
34849
  const candidates = entries.filter((name) => /^roam-local-api(-[^.]+)?\.port$/.test(name)).map((name) => {
34759
- const path91 = join93(roamDir, name);
34850
+ const path91 = join94(roamDir, name);
34760
34851
  try {
34761
34852
  return { path: path91, mtimeMs: statSync12(path91).mtimeMs };
34762
34853
  } catch {
@@ -34773,7 +34864,7 @@ var PID_BY_APP = {
34773
34864
  function postRoamActivity(app, event) {
34774
34865
  const appData = process.env.APPDATA;
34775
34866
  if (!appData) return;
34776
- const portFile = findPortFile(join93(appData, "Roam"));
34867
+ const portFile = findPortFile(join94(appData, "Roam"));
34777
34868
  if (!portFile) return;
34778
34869
  let port;
34779
34870
  try {
@@ -34908,8 +34999,8 @@ async function run3(name, args) {
34908
34999
  }
34909
35000
 
34910
35001
  // src/commands/run/add.ts
34911
- import { mkdirSync as mkdirSync32, writeFileSync as writeFileSync50 } from "fs";
34912
- import { join as join94 } from "path";
35002
+ import { mkdirSync as mkdirSync33, writeFileSync as writeFileSync50 } from "fs";
35003
+ import { join as join95 } from "path";
34913
35004
 
34914
35005
  // src/commands/run/extractOption.ts
34915
35006
  function extractOption(args, flag) {
@@ -34970,15 +35061,15 @@ function saveNewRunConfig(name, command, args, cwd) {
34970
35061
  saveConfig(config);
34971
35062
  }
34972
35063
  function createCommandFile(name) {
34973
- const dir = join94(".claude", "commands");
34974
- mkdirSync32(dir, { recursive: true });
35064
+ const dir = join95(".claude", "commands");
35065
+ mkdirSync33(dir, { recursive: true });
34975
35066
  const content = `---
34976
35067
  description: Run ${name}
34977
35068
  ---
34978
35069
 
34979
35070
  Run \`assist run ${name} $ARGUMENTS 2>&1\`.
34980
35071
  `;
34981
- const filePath = join94(dir, `${name}.md`);
35072
+ const filePath = join95(dir, `${name}.md`);
34982
35073
  writeFileSync50(filePath, content);
34983
35074
  console.log(`Created command file: ${filePath}`);
34984
35075
  }
@@ -35035,7 +35126,7 @@ function link2() {
35035
35126
 
35036
35127
  // src/commands/run/remove.ts
35037
35128
  import { existsSync as existsSync82, unlinkSync as unlinkSync21 } from "fs";
35038
- import { join as join95 } from "path";
35129
+ import { join as join96 } from "path";
35039
35130
  function findRemoveIndex() {
35040
35131
  const idx = process.argv.indexOf("remove");
35041
35132
  if (idx === -1 || idx + 1 >= process.argv.length) return -1;
@@ -35050,7 +35141,7 @@ function parseRemoveName() {
35050
35141
  return process.argv[idx + 1];
35051
35142
  }
35052
35143
  function deleteCommandFile(name) {
35053
- const filePath = join95(".claude", "commands", `${name}.md`);
35144
+ const filePath = join96(".claude", "commands", `${name}.md`);
35054
35145
  if (existsSync82(filePath)) {
35055
35146
  unlinkSync21(filePath);
35056
35147
  console.log(`Deleted command file: ${filePath}`);
@@ -35096,9 +35187,9 @@ function registerRun(program2) {
35096
35187
 
35097
35188
  // src/commands/screenshot/index.ts
35098
35189
  import { execSync as execSync62 } from "child_process";
35099
- import { existsSync as existsSync83, mkdirSync as mkdirSync33, unlinkSync as unlinkSync22, writeFileSync as writeFileSync51 } from "fs";
35190
+ import { existsSync as existsSync83, mkdirSync as mkdirSync34, unlinkSync as unlinkSync22, writeFileSync as writeFileSync51 } from "fs";
35100
35191
  import { tmpdir as tmpdir9 } from "os";
35101
- import { join as join96, resolve as resolve21 } from "path";
35192
+ import { join as join97, resolve as resolve21 } from "path";
35102
35193
  import chalk229 from "chalk";
35103
35194
 
35104
35195
  // src/commands/screenshot/captureWindowPs1.ts
@@ -35229,13 +35320,13 @@ Write-Output $OutputPath
35229
35320
  // src/commands/screenshot/index.ts
35230
35321
  function buildOutputPath(outputDir, processName) {
35231
35322
  if (!existsSync83(outputDir)) {
35232
- mkdirSync33(outputDir, { recursive: true });
35323
+ mkdirSync34(outputDir, { recursive: true });
35233
35324
  }
35234
35325
  const timestamp6 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
35235
35326
  return resolve21(outputDir, `${processName}-${timestamp6}.png`);
35236
35327
  }
35237
35328
  function runPowerShellScript(processName, outputPath) {
35238
- const scriptPath = join96(tmpdir9(), `assist-screenshot-${Date.now()}.ps1`);
35329
+ const scriptPath = join97(tmpdir9(), `assist-screenshot-${Date.now()}.ps1`);
35239
35330
  writeFileSync51(scriptPath, captureWindowPs1, "utf8");
35240
35331
  try {
35241
35332
  execSync62(
@@ -35469,7 +35560,7 @@ function requestDrain(socket, lines2) {
35469
35560
  }
35470
35561
 
35471
35562
  // src/commands/sessions/daemon/runDaemon.ts
35472
- import { mkdirSync as mkdirSync37 } from "fs";
35563
+ import { mkdirSync as mkdirSync38 } from "fs";
35473
35564
 
35474
35565
  // src/commands/sessions/daemon/createAutoExit.ts
35475
35566
  var DEFAULT_GRACE_MS = 6e4;
@@ -35990,12 +36081,12 @@ import { basename as basename24 } from "path";
35990
36081
 
35991
36082
  // src/commands/sessions/daemon/worktree/deleteStrandedTree.ts
35992
36083
  import { existsSync as existsSync87 } from "fs";
35993
- import { join as join99 } from "path";
36084
+ import { join as join100 } from "path";
35994
36085
 
35995
36086
  // src/commands/sessions/daemon/worktree/deleteTreeDirectly.ts
35996
36087
  import { statSync as statSync14 } from "fs";
35997
36088
  import { rm as rm4 } from "fs/promises";
35998
- import { join as join98 } from "path";
36089
+ import { join as join99 } from "path";
35999
36090
  async function deleteTreeDirectly(clone, worktreePath, why) {
36000
36091
  if (holdsAGitDirectoryRatherThanALink(worktreePath)) {
36001
36092
  const refusal = "it is a clone of its own, not a linked worktree";
@@ -36023,7 +36114,7 @@ async function deleteTreeDirectly(clone, worktreePath, why) {
36023
36114
  return { removed: true };
36024
36115
  }
36025
36116
  function holdsAGitDirectoryRatherThanALink(worktreePath) {
36026
- return statSync14(join98(worktreePath, ".git"), {
36117
+ return statSync14(join99(worktreePath, ".git"), {
36027
36118
  throwIfNoEntry: false
36028
36119
  })?.isDirectory() === true;
36029
36120
  }
@@ -36059,7 +36150,7 @@ async function deleteStrandedTree(clone, worktreePath, cause) {
36059
36150
  );
36060
36151
  }
36061
36152
  function strandedReason(worktreePath, cause) {
36062
- if (!existsSync87(join99(worktreePath, ".git")))
36153
+ if (!existsSync87(join100(worktreePath, ".git")))
36063
36154
  return "its .git link is already gone";
36064
36155
  if (/not a working tree|not a git repository/i.test(reason2(cause)))
36065
36156
  return "git no longer recognises it as a working tree";
@@ -36785,14 +36876,10 @@ function missingRunConfigCwd(session) {
36785
36876
  }
36786
36877
 
36787
36878
  // src/commands/sessions/daemon/exitOutputTail.ts
36788
- var ANSI = new RegExp(
36789
- `${String.fromCharCode(27)}\\[[0-9;?]*[ -/]*[@-~]`,
36790
- "g"
36791
- );
36792
36879
  var MAX_TAIL_LINES = 5;
36793
36880
  var MAX_TAIL_CHARS = 500;
36794
36881
  function exitOutputTail(scrollback) {
36795
- const lines2 = scrollback.replace(ANSI, "").split(/\r?\n|\r/).map((line) => line.trim()).filter((line) => line.length > 0);
36882
+ const lines2 = stripAnsi(scrollback).split(/\r?\n|\r/).map((line) => line.trim()).filter((line) => line.length > 0);
36796
36883
  if (lines2.length === 0) return void 0;
36797
36884
  return lines2.slice(-MAX_TAIL_LINES).join(" | ").slice(-MAX_TAIL_CHARS);
36798
36885
  }
@@ -36821,7 +36908,7 @@ function handleFailedResume(session, exitCode, onStatusChange) {
36821
36908
  }
36822
36909
 
36823
36910
  // src/commands/sessions/daemon/watchActivity.ts
36824
- import { existsSync as existsSync91, mkdirSync as mkdirSync34, watch as watch2 } from "fs";
36911
+ import { existsSync as existsSync91, mkdirSync as mkdirSync35, watch as watch2 } from "fs";
36825
36912
  import { dirname as dirname39 } from "path";
36826
36913
 
36827
36914
  // src/commands/sessions/daemon/applyActivityToSession.ts
@@ -36886,7 +36973,7 @@ function watchActivity(session, notify2, onClaudeSessionId) {
36886
36973
  const path91 = activityPath(session.id);
36887
36974
  const dir = dirname39(path91);
36888
36975
  try {
36889
- mkdirSync34(dir, { recursive: true });
36976
+ mkdirSync35(dir, { recursive: true });
36890
36977
  } catch {
36891
36978
  return;
36892
36979
  }
@@ -37089,10 +37176,10 @@ function headContainsSessionId(filePath, claudeSessionId) {
37089
37176
  }
37090
37177
 
37091
37178
  // src/commands/sessions/daemon/ensureProjectDirExists.ts
37092
- import { mkdirSync as mkdirSync35 } from "fs";
37179
+ import { mkdirSync as mkdirSync36 } from "fs";
37093
37180
  function ensureProjectDirExists(dir, sessionId) {
37094
37181
  try {
37095
- mkdirSync35(dir, { recursive: true });
37182
+ mkdirSync36(dir, { recursive: true });
37096
37183
  return true;
37097
37184
  } catch (error) {
37098
37185
  daemonLog(
@@ -40273,8 +40360,8 @@ function resumeSession(id, sessionId, cwd, name, holdPty, harness) {
40273
40360
  import { existsSync as existsSync97 } from "fs";
40274
40361
 
40275
40362
  // src/commands/sessions/daemon/worktree/carryTranscriptToTree.ts
40276
- import { copyFileSync as copyFileSync7, existsSync as existsSync96, mkdirSync as mkdirSync36 } from "fs";
40277
- import { join as join101 } from "path";
40363
+ import { copyFileSync as copyFileSync7, existsSync as existsSync96, mkdirSync as mkdirSync37 } from "fs";
40364
+ import { join as join102 } from "path";
40278
40365
  function carryTranscriptToTree(claudeSessionId, fromCwd, toCwd) {
40279
40366
  const dir = projectDirForCwd(toCwd);
40280
40367
  if (dir === projectDirForCwd(fromCwd)) {
@@ -40283,7 +40370,7 @@ function carryTranscriptToTree(claudeSessionId, fromCwd, toCwd) {
40283
40370
  );
40284
40371
  return;
40285
40372
  }
40286
- const dest = join101(dir, `${claudeSessionId}.jsonl`);
40373
+ const dest = join102(dir, `${claudeSessionId}.jsonl`);
40287
40374
  if (existsSync96(dest)) {
40288
40375
  daemonLog(`transcript ${claudeSessionId} already present in ${dir}`);
40289
40376
  return;
@@ -40296,7 +40383,7 @@ function carryTranscriptToTree(claudeSessionId, fromCwd, toCwd) {
40296
40383
  return;
40297
40384
  }
40298
40385
  try {
40299
- mkdirSync36(dir, { recursive: true });
40386
+ mkdirSync37(dir, { recursive: true });
40300
40387
  copyFileSync7(source, dest);
40301
40388
  daemonLog(
40302
40389
  `transcript ${source} copied to ${dest} so ${toCwd} can resume it`
@@ -41136,7 +41223,7 @@ async function recoverFromAddrInUse(server, manager, checkAutoExit) {
41136
41223
 
41137
41224
  // src/commands/sessions/daemon/runDaemon.ts
41138
41225
  async function runDaemon() {
41139
- mkdirSync37(daemonPaths.dir, { recursive: true });
41226
+ mkdirSync38(daemonPaths.dir, { recursive: true });
41140
41227
  daemonLog(
41141
41228
  `starting (reason: ${process.env.ASSIST_DAEMON_SPAWN_REASON ?? "manual"})`
41142
41229
  );
@@ -41435,9 +41522,9 @@ function buildLimitsSegment(rateLimits) {
41435
41522
 
41436
41523
  // src/commands/readGitBranch.ts
41437
41524
  import { readFileSync as readFileSync67, statSync as statSync16 } from "fs";
41438
- import { isAbsolute as isAbsolute5, join as join102, resolve as resolve23 } from "path";
41525
+ import { isAbsolute as isAbsolute5, join as join103, resolve as resolve23 } from "path";
41439
41526
  function resolveGitDir(cwd) {
41440
- const dotGit = join102(cwd, ".git");
41527
+ const dotGit = join103(cwd, ".git");
41441
41528
  let stat4;
41442
41529
  try {
41443
41530
  stat4 = statSync16(dotGit);
@@ -41467,7 +41554,7 @@ function readGitBranch(cwd) {
41467
41554
  }
41468
41555
  let head;
41469
41556
  try {
41470
- head = readFileSync67(join102(gitDir, "HEAD"), "utf8");
41557
+ head = readFileSync67(join103(gitDir, "HEAD"), "utf8");
41471
41558
  } catch {
41472
41559
  return null;
41473
41560
  }