@staff0rd/assist 0.643.4 → 0.644.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.643.4",
9
+ version: "0.644.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -13003,6 +13003,20 @@ async function readRequestBuffer(req, limit) {
13003
13003
  return Buffer.concat(chunks);
13004
13004
  }
13005
13005
 
13006
+ // src/commands/sessions/web/uploadSizeLimit.ts
13007
+ var MAX_IMAGE_BYTES = 25 * 1024 * 1024;
13008
+ var MAX_VIDEO_BYTES = 10 * 1024 * 1024;
13009
+ function uploadSizeLimit(contentType) {
13010
+ const mime = contentType.split(";")[0].trim().toLowerCase();
13011
+ return mime.startsWith("video/") ? {
13012
+ maxBytes: MAX_VIDEO_BYTES,
13013
+ tooLargeMessage: "Video too large (max 10MB)."
13014
+ } : {
13015
+ maxBytes: MAX_IMAGE_BYTES,
13016
+ tooLargeMessage: "Image too large (max 25MB)."
13017
+ };
13018
+ }
13019
+
13006
13020
  // src/commands/sessions/web/writeTempImage.ts
13007
13021
  import { mkdtemp, writeFile as writeFile2 } from "fs/promises";
13008
13022
  import { tmpdir } from "os";
@@ -13047,16 +13061,16 @@ async function writeTempImage(name, contentType, body) {
13047
13061
  }
13048
13062
 
13049
13063
  // src/commands/sessions/web/uploadPrImage.ts
13050
- var MAX_BYTES = 25 * 1024 * 1024;
13051
13064
  async function uploadPrImage(req, res) {
13052
13065
  const cwd = getCwdParam(req, res);
13053
13066
  if (!cwd) return;
13054
13067
  const url = new URL(req.url ?? "/", "http://localhost");
13055
13068
  const name = url.searchParams.get("name") ?? "";
13056
13069
  const contentType = req.headers["content-type"] ?? "";
13057
- const body = await readRequestBuffer(req, MAX_BYTES);
13070
+ const { maxBytes, tooLargeMessage } = uploadSizeLimit(contentType);
13071
+ const body = await readRequestBuffer(req, maxBytes);
13058
13072
  if (!body) {
13059
- respondJson(res, 413, { error: "Image too large (max 25MB)." });
13073
+ respondJson(res, 413, { error: tooLargeMessage });
13060
13074
  return;
13061
13075
  }
13062
13076
  if (body.length === 0) {
@@ -22792,7 +22806,7 @@ function registerCreateIssue(issueCommand) {
22792
22806
  []
22793
22807
  ).addHelpText(
22794
22808
  "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"
22809
+ "\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
22810
  ).action(createIssue);
22797
22811
  }
22798
22812
 
@@ -25228,25 +25242,25 @@ function isVisibleText(t) {
25228
25242
  return /[a-zA-Z]{3,}/.test(t);
25229
25243
  }
25230
25244
  var isHashtag = (t) => /^#[A-Za-z0-9_]+$/.test(t);
25231
- function collectRscText(v, resolve25, sink2, seen) {
25245
+ function collectRscText(v, resolve25, sink3, seen) {
25232
25246
  if (v == null) return;
25233
25247
  if (typeof v === "string") {
25234
25248
  if (isRscRef(v)) {
25235
25249
  if (!seen.has(v)) {
25236
25250
  seen.add(v);
25237
- collectRscText(resolve25(v), resolve25, sink2, seen);
25251
+ collectRscText(resolve25(v), resolve25, sink3, seen);
25238
25252
  }
25239
- } else if (isHashtag(v)) sink2.hashtags.push(v);
25240
- else if (isVisibleText(v)) sink2.text.push(v);
25253
+ } else if (isHashtag(v)) sink3.hashtags.push(v);
25254
+ else if (isVisibleText(v)) sink3.text.push(v);
25241
25255
  return;
25242
25256
  }
25243
25257
  if (Array.isArray(v)) {
25244
- for (const x of v) collectRscText(x, resolve25, sink2, seen);
25258
+ for (const x of v) collectRscText(x, resolve25, sink3, seen);
25245
25259
  return;
25246
25260
  }
25247
25261
  if (typeof v === "object") {
25248
25262
  for (const val of Object.values(v)) {
25249
- collectRscText(val, resolve25, sink2, seen);
25263
+ collectRscText(val, resolve25, sink3, seen);
25250
25264
  }
25251
25265
  }
25252
25266
  }
@@ -25285,9 +25299,9 @@ function buildMentionMap(rows, resolve25) {
25285
25299
  if (!url || o.children == null) return;
25286
25300
  const slug = slugFromProfileUrl(url);
25287
25301
  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();
25302
+ const sink3 = { text: [], hashtags: [] };
25303
+ collectRscText(o.children, resolve25, sink3, /* @__PURE__ */ new Set());
25304
+ const name = sink3.text.join(" ").replace(/\s+/g, " ").trim();
25291
25305
  map.set(slug, name ? { slug, name, url } : { slug, url });
25292
25306
  });
25293
25307
  return map;
@@ -25388,8 +25402,8 @@ function walkPostRow(v, resolve25, raw) {
25388
25402
  if (a) raw.related.push(a[0]);
25389
25403
  }
25390
25404
  if (isCommentary(o)) {
25391
- const sink2 = { text: raw.text, hashtags: raw.hashtags };
25392
- collectRscText(o.children, resolve25, sink2, /* @__PURE__ */ new Set());
25405
+ const sink3 = { text: raw.text, hashtags: raw.hashtags };
25406
+ collectRscText(o.children, resolve25, sink3, /* @__PURE__ */ new Set());
25393
25407
  }
25394
25408
  for (const val of Object.values(o)) walkPostRow(val, resolve25, raw);
25395
25409
  }
@@ -27212,8 +27226,8 @@ on rejection it exits non-zero with the reason. The reviewer may also attach
27212
27226
  inline comments to specific spans of the preview; on rejection these are printed
27213
27227
  as numbered quoted-span + note pairs on stderr. Address every comment (and the
27214
27228
  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
27229
+ is approved. The reviewer may also drop or paste screenshots or video into the
27230
+ pane; on approval these are appended to the PR body under a ## Screenshots section
27217
27231
  automatically (they are discarded on rejection), so you never author that section
27218
27232
  yourself. Just compose the sections and run the command.`;
27219
27233
  function raiseHelpText(promptJira, draft) {
@@ -27460,12 +27474,12 @@ import chalk190 from "chalk";
27460
27474
 
27461
27475
  // src/shared/createConnectionAuth.ts
27462
27476
  import chalk185 from "chalk";
27463
- function listConnections(connections, format) {
27477
+ function listConnections(connections, format2) {
27464
27478
  if (connections.length === 0) {
27465
27479
  console.log("No connections configured.");
27466
27480
  } else {
27467
27481
  for (const c of connections) {
27468
- console.log(format(c));
27482
+ console.log(format2(c));
27469
27483
  }
27470
27484
  }
27471
27485
  }
@@ -30488,6 +30502,77 @@ function renderEntry(entry, frame) {
30488
30502
  return `${SPINNER_FRAMES2[frame]} ${entry.text}`;
30489
30503
  }
30490
30504
 
30505
+ // src/commands/review/startReviewLog.ts
30506
+ import { format } from "util";
30507
+
30508
+ // src/commands/review/createReviewLogSink.ts
30509
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync22 } from "fs";
30510
+ import { join as join73 } from "path";
30511
+
30512
+ // src/shared/stripAnsi.ts
30513
+ var ANSI = new RegExp(
30514
+ `${String.fromCharCode(27)}\\[[0-9;?]*[ -/]*[@-~]`,
30515
+ "g"
30516
+ );
30517
+ function stripAnsi(text18) {
30518
+ return text18.replace(ANSI, "");
30519
+ }
30520
+
30521
+ // src/commands/review/createReviewLogSink.ts
30522
+ var LOG_FILE = "review.log";
30523
+ function createReviewLogSink() {
30524
+ let logPath2;
30525
+ let buffered = [];
30526
+ return {
30527
+ append(line) {
30528
+ const stripped = stripAnsi(line);
30529
+ if (!logPath2) {
30530
+ buffered.push(stripped);
30531
+ return;
30532
+ }
30533
+ appendFileSync2(logPath2, `${stripped}
30534
+ `);
30535
+ },
30536
+ attach(reviewDir) {
30537
+ mkdirSync22(reviewDir, { recursive: true });
30538
+ logPath2 = join73(reviewDir, LOG_FILE);
30539
+ const lines2 = [
30540
+ "",
30541
+ `=== ${(/* @__PURE__ */ new Date()).toISOString()} ===`,
30542
+ `$ ${process.argv.slice(1).join(" ")}`,
30543
+ ...buffered
30544
+ ];
30545
+ buffered = [];
30546
+ appendFileSync2(logPath2, `${lines2.join("\n")}
30547
+ `);
30548
+ }
30549
+ };
30550
+ }
30551
+
30552
+ // src/commands/review/startReviewLog.ts
30553
+ var sink2 = createReviewLogSink();
30554
+ var patched = false;
30555
+ function appendReviewLog(line) {
30556
+ sink2.append(line);
30557
+ }
30558
+ function attachReviewLog(reviewDir) {
30559
+ sink2.attach(reviewDir);
30560
+ }
30561
+ function patch(method) {
30562
+ const original = console[method].bind(console);
30563
+ console[method] = (...args) => {
30564
+ original(...args);
30565
+ appendReviewLog(format(...args));
30566
+ };
30567
+ }
30568
+ function startReviewLog() {
30569
+ if (patched) return;
30570
+ patched = true;
30571
+ patch("log");
30572
+ patch("error");
30573
+ patch("warn");
30574
+ }
30575
+
30491
30576
  // src/commands/review/MultiSpinner.ts
30492
30577
  var TICK_MS2 = 80;
30493
30578
  var MultiSpinner = class {
@@ -30532,6 +30617,7 @@ var MultiSpinner = class {
30532
30617
  entry.state = state;
30533
30618
  if (text18 !== void 0) entry.text = text18;
30534
30619
  entry.elapsedStart = void 0;
30620
+ appendReviewLog(renderEntry(entry, 0));
30535
30621
  this.render();
30536
30622
  this.maybeFinish();
30537
30623
  }
@@ -31109,12 +31195,14 @@ function buildCodexModelArgs() {
31109
31195
  if (!config) return { args: [], env: {} };
31110
31196
  return {
31111
31197
  args: [
31198
+ "-c",
31199
+ "model_providers.litellm.name=LiteLLM",
31112
31200
  "-c",
31113
31201
  `model_providers.litellm.base_url=${config.baseUrl}/v1`,
31114
31202
  "-c",
31115
31203
  `model_providers.litellm.env_key=${API_KEY_ENV}`,
31116
31204
  "-c",
31117
- "model_providers.litellm.wire_api=chat",
31205
+ "model_providers.litellm.wire_api=responses",
31118
31206
  "-c",
31119
31207
  "model_provider=litellm",
31120
31208
  "-m",
@@ -31440,6 +31528,7 @@ function runPostSynthesis(synthesisPath, prInfo, options2) {
31440
31528
  async function reviewPr(repoRoot2, options2) {
31441
31529
  const context = gatherChangedContext();
31442
31530
  const paths = setupReviewDir(repoRoot2, context, options2.force ?? false);
31531
+ attachReviewLog(paths.reviewDir);
31443
31532
  const synthesisOk = await runReviewPipeline(paths, {
31444
31533
  verbose: options2.verbose ?? false
31445
31534
  });
@@ -31483,6 +31572,7 @@ function validateCheckoutOnly(options2) {
31483
31572
  }
31484
31573
  async function review(options2 = {}) {
31485
31574
  validateOptions(options2);
31575
+ startReviewLog();
31486
31576
  const invokedIn = resolveRepoRoot();
31487
31577
  if (options2.checkoutOnly && options2.number)
31488
31578
  return checkoutOnlySession(options2.number);
@@ -32131,7 +32221,7 @@ function parseSlackThreadRef(value) {
32131
32221
  }
32132
32222
 
32133
32223
  // src/commands/slack/postSlackMessage.ts
32134
- import { mkdirSync as mkdirSync22, writeFileSync as writeFileSync42 } from "fs";
32224
+ import { mkdirSync as mkdirSync23, writeFileSync as writeFileSync42 } from "fs";
32135
32225
 
32136
32226
  // src/commands/slack/reviewProposedSlackMessage.ts
32137
32227
  import { randomUUID as randomUUID17 } from "crypto";
@@ -32156,11 +32246,11 @@ async function reviewProposedSlackMessage(target, body, workingPath) {
32156
32246
  }
32157
32247
 
32158
32248
  // src/commands/slack/slackWorkingFile.ts
32159
- import { join as join73 } from "path";
32249
+ import { join as join74 } from "path";
32160
32250
  function slackWorkingFile(channel) {
32161
32251
  const slug = channel.replace(/^[#@]/, "").toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "channel";
32162
- const dir = join73(getStoreDir(), "slack");
32163
- return { dir, bodyPath: join73(dir, `${slug}.md`) };
32252
+ const dir = join74(getStoreDir(), "slack");
32253
+ return { dir, bodyPath: join74(dir, `${slug}.md`) };
32164
32254
  }
32165
32255
 
32166
32256
  // src/commands/slack/postSlackMessage.ts
@@ -32181,7 +32271,7 @@ async function postSlackMessage(channelArg, options2) {
32181
32271
  }
32182
32272
  const threadTs = options2.thread;
32183
32273
  const { dir, bodyPath } = slackWorkingFile(channel);
32184
- mkdirSync22(dir, { recursive: true });
32274
+ mkdirSync23(dir, { recursive: true });
32185
32275
  writeFileSync42(bodyPath, `${body}
32186
32276
  `);
32187
32277
  await reviewProposedSlackMessage({ channel, threadTs }, body, bodyPath);
@@ -33176,21 +33266,21 @@ var FORMATS = ["md", "vtt"];
33176
33266
  function isCleanFormat(value) {
33177
33267
  return FORMATS.includes(value);
33178
33268
  }
33179
- function serialise(cues, format, timestamps) {
33180
- return format === "vtt" ? formatVtt(cues) : formatChatLog(cuesToChatMessages(cues), { timestamps });
33269
+ function serialise(cues, format2, timestamps) {
33270
+ return format2 === "vtt" ? formatVtt(cues) : formatChatLog(cuesToChatMessages(cues), { timestamps });
33181
33271
  }
33182
33272
  function clean(file, options2 = {}) {
33183
- const format = options2.format ?? "md";
33184
- if (!isCleanFormat(format)) {
33273
+ const format2 = options2.format ?? "md";
33274
+ if (!isCleanFormat(format2)) {
33185
33275
  console.error(
33186
- `Error: --format must be one of: ${FORMATS.join(", ")} (got: ${format})`
33276
+ `Error: --format must be one of: ${FORMATS.join(", ")} (got: ${format2})`
33187
33277
  );
33188
33278
  process.exit(1);
33189
33279
  }
33190
33280
  const timestamps = options2.timestamps ?? false;
33191
- if (timestamps && format !== "md") {
33281
+ if (timestamps && format2 !== "md") {
33192
33282
  console.error(
33193
- `Error: --timestamps applies only to --format md (got: ${format})`
33283
+ `Error: --timestamps applies only to --format md (got: ${format2})`
33194
33284
  );
33195
33285
  process.exit(1);
33196
33286
  }
@@ -33203,7 +33293,7 @@ function clean(file, options2 = {}) {
33203
33293
  console.error(`Error: no cues found in: ${file}`);
33204
33294
  process.exit(1);
33205
33295
  }
33206
- console.log(serialise(cues, format, timestamps));
33296
+ console.log(serialise(cues, format2, timestamps));
33207
33297
  }
33208
33298
 
33209
33299
  // src/commands/transcript/shared.ts
@@ -33278,20 +33368,20 @@ async function configure() {
33278
33368
 
33279
33369
  // src/commands/transcript/list.ts
33280
33370
  import { existsSync as existsSync72, readdirSync as readdirSync20, statSync as statSync11 } from "fs";
33281
- import { join as join84 } from "path";
33371
+ import { join as join85 } from "path";
33282
33372
  function list4() {
33283
33373
  const { vttDir } = getTranscriptConfig();
33284
33374
  if (!existsSync72(vttDir)) return;
33285
33375
  for (const entry of readdirSync20(vttDir)) {
33286
33376
  if (!entry.endsWith(".vtt")) continue;
33287
- if (statSync11(join84(vttDir, entry)).isDirectory()) continue;
33377
+ if (statSync11(join85(vttDir, entry)).isDirectory()) continue;
33288
33378
  console.log(entry);
33289
33379
  }
33290
33380
  }
33291
33381
 
33292
33382
  // src/commands/transcript/move.ts
33293
- import { existsSync as existsSync73, mkdirSync as mkdirSync28, renameSync as renameSync2, writeFileSync as writeFileSync46 } from "fs";
33294
- import { basename as basename22, join as join85 } from "path";
33383
+ import { existsSync as existsSync73, mkdirSync as mkdirSync29, renameSync as renameSync2, writeFileSync as writeFileSync46 } from "fs";
33384
+ import { basename as basename22, join as join86 } from "path";
33295
33385
 
33296
33386
  // src/commands/transcript/convertVttToMarkdown.ts
33297
33387
  function convertVttToMarkdown(inputPath) {
@@ -33301,9 +33391,9 @@ function convertVttToMarkdown(inputPath) {
33301
33391
  // src/commands/transcript/move.ts
33302
33392
  var DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
33303
33393
  function archiveRawVtt(vttDir, sourcePath, filename) {
33304
- const processedDir = join85(vttDir, "processed");
33305
- mkdirSync28(processedDir, { recursive: true });
33306
- renameSync2(sourcePath, join85(processedDir, filename));
33394
+ const processedDir = join86(vttDir, "processed");
33395
+ mkdirSync29(processedDir, { recursive: true });
33396
+ renameSync2(sourcePath, join86(processedDir, filename));
33307
33397
  }
33308
33398
  function move(file, options2) {
33309
33399
  const { date, client } = options2;
@@ -33313,19 +33403,19 @@ function move(file, options2) {
33313
33403
  }
33314
33404
  const { vttDir, transcriptsDir, summaryDir } = getTranscriptConfig();
33315
33405
  const filename = basename22(file);
33316
- const sourcePath = join85(vttDir, filename);
33406
+ const sourcePath = join86(vttDir, filename);
33317
33407
  if (!existsSync73(sourcePath)) {
33318
33408
  console.error(`Error: VTT file not found: ${sourcePath}`);
33319
33409
  process.exit(1);
33320
33410
  }
33321
33411
  const base = basename22(filename, ".vtt").replace(/ Transcription$/, "");
33322
33412
  const outputName = `${date} ${base}.md`;
33323
- const formattedDir = join85(transcriptsDir, client);
33324
- mkdirSync28(formattedDir, { recursive: true });
33325
- const formattedPath = join85(formattedDir, outputName);
33413
+ const formattedDir = join86(transcriptsDir, client);
33414
+ mkdirSync29(formattedDir, { recursive: true });
33415
+ const formattedPath = join86(formattedDir, outputName);
33326
33416
  writeFileSync46(formattedPath, convertVttToMarkdown(sourcePath), "utf8");
33327
33417
  archiveRawVtt(vttDir, sourcePath, filename);
33328
- const summaryPath = join85(summaryDir, client, outputName);
33418
+ const summaryPath = join86(summaryDir, client, outputName);
33329
33419
  console.log(`Formatted transcript: ${formattedPath}`);
33330
33420
  console.log(`Summary target: ${summaryPath}`);
33331
33421
  }
@@ -33639,38 +33729,38 @@ function registerVerify(program2) {
33639
33729
 
33640
33730
  // src/commands/voice/devices.ts
33641
33731
  import { spawnSync as spawnSync9 } from "child_process";
33642
- import { join as join87 } from "path";
33732
+ import { join as join88 } from "path";
33643
33733
 
33644
33734
  // src/commands/voice/shared.ts
33645
33735
  import { homedir as homedir25 } from "os";
33646
- import { dirname as dirname36, join as join86 } from "path";
33736
+ import { dirname as dirname36, join as join87 } from "path";
33647
33737
  import { fileURLToPath as fileURLToPath9 } from "url";
33648
33738
  var __dirname6 = dirname36(fileURLToPath9(import.meta.url));
33649
- var VOICE_DIR = join86(homedir25(), ".assist", "voice");
33739
+ var VOICE_DIR = join87(homedir25(), ".assist", "voice");
33650
33740
  var voicePaths = {
33651
33741
  dir: VOICE_DIR,
33652
- pid: join86(VOICE_DIR, "voice.pid"),
33653
- log: join86(VOICE_DIR, "voice.log"),
33654
- venv: join86(VOICE_DIR, ".venv"),
33655
- lock: join86(VOICE_DIR, "voice.lock")
33742
+ pid: join87(VOICE_DIR, "voice.pid"),
33743
+ log: join87(VOICE_DIR, "voice.log"),
33744
+ venv: join87(VOICE_DIR, ".venv"),
33745
+ lock: join87(VOICE_DIR, "voice.lock")
33656
33746
  };
33657
33747
  function getPythonDir() {
33658
- return join86(__dirname6, "commands", "voice", "python");
33748
+ return join87(__dirname6, "commands", "voice", "python");
33659
33749
  }
33660
33750
  function getVenvPython() {
33661
- return process.platform === "win32" ? join86(voicePaths.venv, "Scripts", "python.exe") : join86(voicePaths.venv, "bin", "python");
33751
+ return process.platform === "win32" ? join87(voicePaths.venv, "Scripts", "python.exe") : join87(voicePaths.venv, "bin", "python");
33662
33752
  }
33663
33753
  function getLockDir() {
33664
33754
  const config = loadConfig();
33665
33755
  return config.voice?.lockDir ?? VOICE_DIR;
33666
33756
  }
33667
33757
  function getLockFile() {
33668
- return join86(getLockDir(), "voice.lock");
33758
+ return join87(getLockDir(), "voice.lock");
33669
33759
  }
33670
33760
 
33671
33761
  // src/commands/voice/devices.ts
33672
33762
  function devices() {
33673
- const script = join87(getPythonDir(), "list_devices.py");
33763
+ const script = join88(getPythonDir(), "list_devices.py");
33674
33764
  spawnSync9(getVenvPython(), [script], { stdio: "inherit" });
33675
33765
  }
33676
33766
 
@@ -33704,13 +33794,13 @@ function logs(options2) {
33704
33794
 
33705
33795
  // src/commands/voice/setup.ts
33706
33796
  import { spawnSync as spawnSync10 } from "child_process";
33707
- import { mkdirSync as mkdirSync30 } from "fs";
33708
- import { join as join89 } from "path";
33797
+ import { mkdirSync as mkdirSync31 } from "fs";
33798
+ import { join as join90 } from "path";
33709
33799
 
33710
33800
  // src/commands/voice/checkLockFile.ts
33711
33801
  import { execSync as execSync60 } from "child_process";
33712
- import { existsSync as existsSync76, mkdirSync as mkdirSync29, readFileSync as readFileSync58, writeFileSync as writeFileSync48 } from "fs";
33713
- import { join as join88 } from "path";
33802
+ import { existsSync as existsSync76, mkdirSync as mkdirSync30, readFileSync as readFileSync58, writeFileSync as writeFileSync48 } from "fs";
33803
+ import { join as join89 } from "path";
33714
33804
  function isProcessAlive2(pid) {
33715
33805
  try {
33716
33806
  process.kill(pid, 0);
@@ -33747,7 +33837,7 @@ function bootstrapVenv() {
33747
33837
  }
33748
33838
  function writeLockFile(pid) {
33749
33839
  const lockFile = getLockFile();
33750
- mkdirSync29(join88(lockFile, ".."), { recursive: true });
33840
+ mkdirSync30(join89(lockFile, ".."), { recursive: true });
33751
33841
  writeFileSync48(
33752
33842
  lockFile,
33753
33843
  JSON.stringify({
@@ -33760,10 +33850,10 @@ function writeLockFile(pid) {
33760
33850
 
33761
33851
  // src/commands/voice/setup.ts
33762
33852
  function setup() {
33763
- mkdirSync30(voicePaths.dir, { recursive: true });
33853
+ mkdirSync31(voicePaths.dir, { recursive: true });
33764
33854
  bootstrapVenv();
33765
33855
  console.log("\nDownloading models...\n");
33766
- const script = join89(getPythonDir(), "setup_models.py");
33856
+ const script = join90(getPythonDir(), "setup_models.py");
33767
33857
  const result = spawnSync10(getVenvPython(), [script], {
33768
33858
  stdio: "inherit",
33769
33859
  env: { ...process.env, VOICE_LOG_FILE: voicePaths.log }
@@ -33776,8 +33866,8 @@ function setup() {
33776
33866
 
33777
33867
  // src/commands/voice/start.ts
33778
33868
  import { spawn as spawn8 } from "child_process";
33779
- import { mkdirSync as mkdirSync31, writeFileSync as writeFileSync49 } from "fs";
33780
- import { join as join90 } from "path";
33869
+ import { mkdirSync as mkdirSync32, writeFileSync as writeFileSync49 } from "fs";
33870
+ import { join as join91 } from "path";
33781
33871
 
33782
33872
  // src/commands/voice/buildDaemonEnv.ts
33783
33873
  function buildDaemonEnv(options2) {
@@ -33810,12 +33900,12 @@ function spawnBackground(python, script, env) {
33810
33900
  console.log(`Voice daemon started (PID ${pid})`);
33811
33901
  }
33812
33902
  function start2(options2) {
33813
- mkdirSync31(voicePaths.dir, { recursive: true });
33903
+ mkdirSync32(voicePaths.dir, { recursive: true });
33814
33904
  checkLockFile();
33815
33905
  bootstrapVenv();
33816
33906
  const debug = options2.debug || options2.foreground || process.platform === "win32";
33817
33907
  const env = buildDaemonEnv({ debug });
33818
- const script = join90(getPythonDir(), "voice_daemon.py");
33908
+ const script = join91(getPythonDir(), "voice_daemon.py");
33819
33909
  const python = getVenvPython();
33820
33910
  if (options2.foreground) {
33821
33911
  spawnForeground(python, script, env);
@@ -33946,11 +34036,11 @@ function changedPaths(from, cwd) {
33946
34036
  }
33947
34037
 
33948
34038
  // src/commands/watch/readBuiltVersion.ts
33949
- import { join as join91 } from "path";
34039
+ import { join as join92 } from "path";
33950
34040
  function readBuiltVersion(cwd) {
33951
34041
  try {
33952
34042
  const root = runGit3(["rev-parse", "--show-toplevel"], cwd);
33953
- return readPackageJson(join91(root, "package.json")).version ?? "unknown";
34043
+ return readPackageJson(join92(root, "package.json")).version ?? "unknown";
33954
34044
  } catch {
33955
34045
  return "unknown";
33956
34046
  }
@@ -34311,13 +34401,13 @@ import { existsSync as existsSync81 } from "fs";
34311
34401
  // src/commands/run/resolveCommand.ts
34312
34402
  import { execFileSync as execFileSync18 } from "child_process";
34313
34403
  import { existsSync as existsSync80 } from "fs";
34314
- import { dirname as dirname37, join as join92, resolve as resolve20 } from "path";
34404
+ import { dirname as dirname37, join as join93, resolve as resolve20 } from "path";
34315
34405
  function resolveCommand2(command) {
34316
34406
  if (process.platform !== "win32" || command !== "bash") return command;
34317
34407
  try {
34318
34408
  const gitPath = execFileSync18("where", ["git"], { encoding: "utf8" }).trim().split("\r\n")[0];
34319
34409
  const gitRoot = resolve20(dirname37(gitPath), "..");
34320
- const gitBash = join92(gitRoot, "bin", "bash.exe");
34410
+ const gitBash = join93(gitRoot, "bin", "bash.exe");
34321
34411
  if (existsSync80(gitBash)) return gitBash;
34322
34412
  } catch {
34323
34413
  return command;
@@ -34745,7 +34835,7 @@ async function auth() {
34745
34835
  // src/commands/roam/postRoamActivity.ts
34746
34836
  import { execFileSync as execFileSync20 } from "child_process";
34747
34837
  import { readdirSync as readdirSync21, readFileSync as readFileSync61, statSync as statSync12 } from "fs";
34748
- import { join as join93 } from "path";
34838
+ import { join as join94 } from "path";
34749
34839
  function findPortFile(roamDir) {
34750
34840
  let entries;
34751
34841
  try {
@@ -34754,7 +34844,7 @@ function findPortFile(roamDir) {
34754
34844
  return void 0;
34755
34845
  }
34756
34846
  const candidates = entries.filter((name) => /^roam-local-api(-[^.]+)?\.port$/.test(name)).map((name) => {
34757
- const path91 = join93(roamDir, name);
34847
+ const path91 = join94(roamDir, name);
34758
34848
  try {
34759
34849
  return { path: path91, mtimeMs: statSync12(path91).mtimeMs };
34760
34850
  } catch {
@@ -34771,7 +34861,7 @@ var PID_BY_APP = {
34771
34861
  function postRoamActivity(app, event) {
34772
34862
  const appData = process.env.APPDATA;
34773
34863
  if (!appData) return;
34774
- const portFile = findPortFile(join93(appData, "Roam"));
34864
+ const portFile = findPortFile(join94(appData, "Roam"));
34775
34865
  if (!portFile) return;
34776
34866
  let port;
34777
34867
  try {
@@ -34906,8 +34996,8 @@ async function run3(name, args) {
34906
34996
  }
34907
34997
 
34908
34998
  // src/commands/run/add.ts
34909
- import { mkdirSync as mkdirSync32, writeFileSync as writeFileSync50 } from "fs";
34910
- import { join as join94 } from "path";
34999
+ import { mkdirSync as mkdirSync33, writeFileSync as writeFileSync50 } from "fs";
35000
+ import { join as join95 } from "path";
34911
35001
 
34912
35002
  // src/commands/run/extractOption.ts
34913
35003
  function extractOption(args, flag) {
@@ -34968,15 +35058,15 @@ function saveNewRunConfig(name, command, args, cwd) {
34968
35058
  saveConfig(config);
34969
35059
  }
34970
35060
  function createCommandFile(name) {
34971
- const dir = join94(".claude", "commands");
34972
- mkdirSync32(dir, { recursive: true });
35061
+ const dir = join95(".claude", "commands");
35062
+ mkdirSync33(dir, { recursive: true });
34973
35063
  const content = `---
34974
35064
  description: Run ${name}
34975
35065
  ---
34976
35066
 
34977
35067
  Run \`assist run ${name} $ARGUMENTS 2>&1\`.
34978
35068
  `;
34979
- const filePath = join94(dir, `${name}.md`);
35069
+ const filePath = join95(dir, `${name}.md`);
34980
35070
  writeFileSync50(filePath, content);
34981
35071
  console.log(`Created command file: ${filePath}`);
34982
35072
  }
@@ -35033,7 +35123,7 @@ function link2() {
35033
35123
 
35034
35124
  // src/commands/run/remove.ts
35035
35125
  import { existsSync as existsSync82, unlinkSync as unlinkSync21 } from "fs";
35036
- import { join as join95 } from "path";
35126
+ import { join as join96 } from "path";
35037
35127
  function findRemoveIndex() {
35038
35128
  const idx = process.argv.indexOf("remove");
35039
35129
  if (idx === -1 || idx + 1 >= process.argv.length) return -1;
@@ -35048,7 +35138,7 @@ function parseRemoveName() {
35048
35138
  return process.argv[idx + 1];
35049
35139
  }
35050
35140
  function deleteCommandFile(name) {
35051
- const filePath = join95(".claude", "commands", `${name}.md`);
35141
+ const filePath = join96(".claude", "commands", `${name}.md`);
35052
35142
  if (existsSync82(filePath)) {
35053
35143
  unlinkSync21(filePath);
35054
35144
  console.log(`Deleted command file: ${filePath}`);
@@ -35094,9 +35184,9 @@ function registerRun(program2) {
35094
35184
 
35095
35185
  // src/commands/screenshot/index.ts
35096
35186
  import { execSync as execSync62 } from "child_process";
35097
- import { existsSync as existsSync83, mkdirSync as mkdirSync33, unlinkSync as unlinkSync22, writeFileSync as writeFileSync51 } from "fs";
35187
+ import { existsSync as existsSync83, mkdirSync as mkdirSync34, unlinkSync as unlinkSync22, writeFileSync as writeFileSync51 } from "fs";
35098
35188
  import { tmpdir as tmpdir9 } from "os";
35099
- import { join as join96, resolve as resolve21 } from "path";
35189
+ import { join as join97, resolve as resolve21 } from "path";
35100
35190
  import chalk229 from "chalk";
35101
35191
 
35102
35192
  // src/commands/screenshot/captureWindowPs1.ts
@@ -35227,13 +35317,13 @@ Write-Output $OutputPath
35227
35317
  // src/commands/screenshot/index.ts
35228
35318
  function buildOutputPath(outputDir, processName) {
35229
35319
  if (!existsSync83(outputDir)) {
35230
- mkdirSync33(outputDir, { recursive: true });
35320
+ mkdirSync34(outputDir, { recursive: true });
35231
35321
  }
35232
35322
  const timestamp6 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
35233
35323
  return resolve21(outputDir, `${processName}-${timestamp6}.png`);
35234
35324
  }
35235
35325
  function runPowerShellScript(processName, outputPath) {
35236
- const scriptPath = join96(tmpdir9(), `assist-screenshot-${Date.now()}.ps1`);
35326
+ const scriptPath = join97(tmpdir9(), `assist-screenshot-${Date.now()}.ps1`);
35237
35327
  writeFileSync51(scriptPath, captureWindowPs1, "utf8");
35238
35328
  try {
35239
35329
  execSync62(
@@ -35467,7 +35557,7 @@ function requestDrain(socket, lines2) {
35467
35557
  }
35468
35558
 
35469
35559
  // src/commands/sessions/daemon/runDaemon.ts
35470
- import { mkdirSync as mkdirSync37 } from "fs";
35560
+ import { mkdirSync as mkdirSync38 } from "fs";
35471
35561
 
35472
35562
  // src/commands/sessions/daemon/createAutoExit.ts
35473
35563
  var DEFAULT_GRACE_MS = 6e4;
@@ -35988,12 +36078,12 @@ import { basename as basename24 } from "path";
35988
36078
 
35989
36079
  // src/commands/sessions/daemon/worktree/deleteStrandedTree.ts
35990
36080
  import { existsSync as existsSync87 } from "fs";
35991
- import { join as join99 } from "path";
36081
+ import { join as join100 } from "path";
35992
36082
 
35993
36083
  // src/commands/sessions/daemon/worktree/deleteTreeDirectly.ts
35994
36084
  import { statSync as statSync14 } from "fs";
35995
36085
  import { rm as rm4 } from "fs/promises";
35996
- import { join as join98 } from "path";
36086
+ import { join as join99 } from "path";
35997
36087
  async function deleteTreeDirectly(clone, worktreePath, why) {
35998
36088
  if (holdsAGitDirectoryRatherThanALink(worktreePath)) {
35999
36089
  const refusal = "it is a clone of its own, not a linked worktree";
@@ -36021,7 +36111,7 @@ async function deleteTreeDirectly(clone, worktreePath, why) {
36021
36111
  return { removed: true };
36022
36112
  }
36023
36113
  function holdsAGitDirectoryRatherThanALink(worktreePath) {
36024
- return statSync14(join98(worktreePath, ".git"), {
36114
+ return statSync14(join99(worktreePath, ".git"), {
36025
36115
  throwIfNoEntry: false
36026
36116
  })?.isDirectory() === true;
36027
36117
  }
@@ -36057,7 +36147,7 @@ async function deleteStrandedTree(clone, worktreePath, cause) {
36057
36147
  );
36058
36148
  }
36059
36149
  function strandedReason(worktreePath, cause) {
36060
- if (!existsSync87(join99(worktreePath, ".git")))
36150
+ if (!existsSync87(join100(worktreePath, ".git")))
36061
36151
  return "its .git link is already gone";
36062
36152
  if (/not a working tree|not a git repository/i.test(reason2(cause)))
36063
36153
  return "git no longer recognises it as a working tree";
@@ -36783,14 +36873,10 @@ function missingRunConfigCwd(session) {
36783
36873
  }
36784
36874
 
36785
36875
  // src/commands/sessions/daemon/exitOutputTail.ts
36786
- var ANSI = new RegExp(
36787
- `${String.fromCharCode(27)}\\[[0-9;?]*[ -/]*[@-~]`,
36788
- "g"
36789
- );
36790
36876
  var MAX_TAIL_LINES = 5;
36791
36877
  var MAX_TAIL_CHARS = 500;
36792
36878
  function exitOutputTail(scrollback) {
36793
- const lines2 = scrollback.replace(ANSI, "").split(/\r?\n|\r/).map((line) => line.trim()).filter((line) => line.length > 0);
36879
+ const lines2 = stripAnsi(scrollback).split(/\r?\n|\r/).map((line) => line.trim()).filter((line) => line.length > 0);
36794
36880
  if (lines2.length === 0) return void 0;
36795
36881
  return lines2.slice(-MAX_TAIL_LINES).join(" | ").slice(-MAX_TAIL_CHARS);
36796
36882
  }
@@ -36819,7 +36905,7 @@ function handleFailedResume(session, exitCode, onStatusChange) {
36819
36905
  }
36820
36906
 
36821
36907
  // src/commands/sessions/daemon/watchActivity.ts
36822
- import { existsSync as existsSync91, mkdirSync as mkdirSync34, watch as watch2 } from "fs";
36908
+ import { existsSync as existsSync91, mkdirSync as mkdirSync35, watch as watch2 } from "fs";
36823
36909
  import { dirname as dirname39 } from "path";
36824
36910
 
36825
36911
  // src/commands/sessions/daemon/applyActivityToSession.ts
@@ -36884,7 +36970,7 @@ function watchActivity(session, notify2, onClaudeSessionId) {
36884
36970
  const path91 = activityPath(session.id);
36885
36971
  const dir = dirname39(path91);
36886
36972
  try {
36887
- mkdirSync34(dir, { recursive: true });
36973
+ mkdirSync35(dir, { recursive: true });
36888
36974
  } catch {
36889
36975
  return;
36890
36976
  }
@@ -37087,10 +37173,10 @@ function headContainsSessionId(filePath, claudeSessionId) {
37087
37173
  }
37088
37174
 
37089
37175
  // src/commands/sessions/daemon/ensureProjectDirExists.ts
37090
- import { mkdirSync as mkdirSync35 } from "fs";
37176
+ import { mkdirSync as mkdirSync36 } from "fs";
37091
37177
  function ensureProjectDirExists(dir, sessionId) {
37092
37178
  try {
37093
- mkdirSync35(dir, { recursive: true });
37179
+ mkdirSync36(dir, { recursive: true });
37094
37180
  return true;
37095
37181
  } catch (error) {
37096
37182
  daemonLog(
@@ -40271,8 +40357,8 @@ function resumeSession(id, sessionId, cwd, name, holdPty, harness) {
40271
40357
  import { existsSync as existsSync97 } from "fs";
40272
40358
 
40273
40359
  // src/commands/sessions/daemon/worktree/carryTranscriptToTree.ts
40274
- import { copyFileSync as copyFileSync7, existsSync as existsSync96, mkdirSync as mkdirSync36 } from "fs";
40275
- import { join as join101 } from "path";
40360
+ import { copyFileSync as copyFileSync7, existsSync as existsSync96, mkdirSync as mkdirSync37 } from "fs";
40361
+ import { join as join102 } from "path";
40276
40362
  function carryTranscriptToTree(claudeSessionId, fromCwd, toCwd) {
40277
40363
  const dir = projectDirForCwd(toCwd);
40278
40364
  if (dir === projectDirForCwd(fromCwd)) {
@@ -40281,7 +40367,7 @@ function carryTranscriptToTree(claudeSessionId, fromCwd, toCwd) {
40281
40367
  );
40282
40368
  return;
40283
40369
  }
40284
- const dest = join101(dir, `${claudeSessionId}.jsonl`);
40370
+ const dest = join102(dir, `${claudeSessionId}.jsonl`);
40285
40371
  if (existsSync96(dest)) {
40286
40372
  daemonLog(`transcript ${claudeSessionId} already present in ${dir}`);
40287
40373
  return;
@@ -40294,7 +40380,7 @@ function carryTranscriptToTree(claudeSessionId, fromCwd, toCwd) {
40294
40380
  return;
40295
40381
  }
40296
40382
  try {
40297
- mkdirSync36(dir, { recursive: true });
40383
+ mkdirSync37(dir, { recursive: true });
40298
40384
  copyFileSync7(source, dest);
40299
40385
  daemonLog(
40300
40386
  `transcript ${source} copied to ${dest} so ${toCwd} can resume it`
@@ -41134,7 +41220,7 @@ async function recoverFromAddrInUse(server, manager, checkAutoExit) {
41134
41220
 
41135
41221
  // src/commands/sessions/daemon/runDaemon.ts
41136
41222
  async function runDaemon() {
41137
- mkdirSync37(daemonPaths.dir, { recursive: true });
41223
+ mkdirSync38(daemonPaths.dir, { recursive: true });
41138
41224
  daemonLog(
41139
41225
  `starting (reason: ${process.env.ASSIST_DAEMON_SPAWN_REASON ?? "manual"})`
41140
41226
  );
@@ -41433,9 +41519,9 @@ function buildLimitsSegment(rateLimits) {
41433
41519
 
41434
41520
  // src/commands/readGitBranch.ts
41435
41521
  import { readFileSync as readFileSync67, statSync as statSync16 } from "fs";
41436
- import { isAbsolute as isAbsolute5, join as join102, resolve as resolve23 } from "path";
41522
+ import { isAbsolute as isAbsolute5, join as join103, resolve as resolve23 } from "path";
41437
41523
  function resolveGitDir(cwd) {
41438
- const dotGit = join102(cwd, ".git");
41524
+ const dotGit = join103(cwd, ".git");
41439
41525
  let stat4;
41440
41526
  try {
41441
41527
  stat4 = statSync16(dotGit);
@@ -41465,7 +41551,7 @@ function readGitBranch(cwd) {
41465
41551
  }
41466
41552
  let head;
41467
41553
  try {
41468
- head = readFileSync67(join102(gitDir, "HEAD"), "utf8");
41554
+ head = readFileSync67(join103(gitDir, "HEAD"), "utf8");
41469
41555
  } catch {
41470
41556
  return null;
41471
41557
  }