@staff0rd/assist 0.469.5 → 0.471.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.469.5",
9
+ version: "0.471.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -506,7 +506,8 @@ var assistConfigSchema = z2.strictObject({
506
506
  windowsDaemonPort: z2.number().optional(),
507
507
  windowsVersionCheck: z2.enum(["block", "warn", "off"]).default("block"),
508
508
  includeCommittedChanges: z2.boolean().default(false),
509
- topBar: z2.boolean().default(true)
509
+ topBar: z2.boolean().default(true),
510
+ floatWaiting: z2.boolean().default(false)
510
511
  }).optional(),
511
512
  database: z2.strictObject({
512
513
  url: z2.string().optional()
@@ -9045,28 +9046,6 @@ async function handleItemRoute(req, res, pathname) {
9045
9046
  return false;
9046
9047
  }
9047
9048
 
9048
- // src/commands/sessions/web/execGit.ts
9049
- import { execFile as execFile3 } from "child_process";
9050
- import { promisify as promisify2 } from "util";
9051
- var execFileAsync2 = promisify2(execFile3);
9052
- async function execGit(cwd, args, opts = {}) {
9053
- const { maxBuffer, allowFailure } = opts;
9054
- const { file, argv } = /^[A-Za-z]:[\\/]/.test(cwd) ? { file: "git.exe", argv: ["-C", cwd, ...args] } : { file: "git", argv: args };
9055
- try {
9056
- const { stdout } = await execFileAsync2(file, argv, {
9057
- encoding: "utf8",
9058
- ...maxBuffer ? { maxBuffer } : {},
9059
- ...file === "git" ? { cwd } : {}
9060
- });
9061
- return stdout;
9062
- } catch (error) {
9063
- if (allowFailure && error && typeof error === "object" && "stdout" in error) {
9064
- return String(error.stdout);
9065
- }
9066
- throw error;
9067
- }
9068
- }
9069
-
9070
9049
  // src/commands/sessions/web/getCwdParam.ts
9071
9050
  function getCwdParam(req, res) {
9072
9051
  const url = new URL(req.url ?? "/", "http://localhost");
@@ -9084,6 +9063,31 @@ function getSessionParam(req) {
9084
9063
  return url.searchParams.get("session") ?? void 0;
9085
9064
  }
9086
9065
 
9066
+ // src/commands/sessions/web/windowsCwdToWslPath.ts
9067
+ function windowsCwdToWslPath(cwd) {
9068
+ const match = /^([A-Za-z]):[\\/](.*)$/.exec(cwd);
9069
+ if (!match) return cwd;
9070
+ const drive = match[1].toLowerCase();
9071
+ const rest = match[2].replace(/\\/g, "/");
9072
+ return `/mnt/${drive}/${rest}`;
9073
+ }
9074
+
9075
+ // src/commands/sessions/web/toGitCwd.ts
9076
+ function toGitCwd(cwd) {
9077
+ return detectPlatform() === "wsl" ? windowsCwdToWslPath(cwd) : cwd;
9078
+ }
9079
+
9080
+ // src/commands/sessions/web/includesCommittedChanges.ts
9081
+ function includesCommittedChanges(cwd) {
9082
+ try {
9083
+ return Boolean(
9084
+ loadConfigFrom(toGitCwd(cwd)).sessions?.includeCommittedChanges
9085
+ );
9086
+ } catch {
9087
+ return false;
9088
+ }
9089
+ }
9090
+
9087
9091
  // src/shared/db/findItemBySessionId.ts
9088
9092
  import { asc as asc6, eq as eq16 } from "drizzle-orm";
9089
9093
  async function findItemBySessionId(db, claudeSessionId) {
@@ -9120,15 +9124,53 @@ async function itemCommits(sessionId) {
9120
9124
  }
9121
9125
  }
9122
9126
 
9127
+ // src/commands/sessions/web/itemScopeCommits.ts
9128
+ async function itemScopeCommits(cwd, sessionId) {
9129
+ if (!sessionId || !includesCommittedChanges(cwd)) return [];
9130
+ return itemCommits(sessionId);
9131
+ }
9132
+
9133
+ // src/commands/sessions/web/resolveDiffScope.ts
9134
+ async function resolveDiffScope(cwd, sessionId, scope) {
9135
+ if (!scope || scope === "all") return { kind: "all" };
9136
+ if (scope === "uncommitted") return { kind: "uncommitted" };
9137
+ const commits2 = await itemScopeCommits(cwd, sessionId);
9138
+ if (!commits2.some((commit2) => commit2.sha === scope)) return void 0;
9139
+ return { kind: "commit", sha: scope };
9140
+ }
9141
+
9142
+ // src/commands/sessions/web/execGit.ts
9143
+ import { execFile as execFile3 } from "child_process";
9144
+ import { promisify as promisify2 } from "util";
9145
+ var execFileAsync2 = promisify2(execFile3);
9146
+ async function execGit(cwd, args, opts = {}) {
9147
+ const { maxBuffer, allowFailure } = opts;
9148
+ const { file, argv } = /^[A-Za-z]:[\\/]/.test(cwd) ? { file: "git.exe", argv: ["-C", cwd, ...args] } : { file: "git", argv: args };
9149
+ try {
9150
+ const { stdout } = await execFileAsync2(file, argv, {
9151
+ encoding: "utf8",
9152
+ ...maxBuffer ? { maxBuffer } : {},
9153
+ ...file === "git" ? { cwd } : {}
9154
+ });
9155
+ return stdout;
9156
+ } catch (error) {
9157
+ if (allowFailure && error && typeof error === "object" && "stdout" in error) {
9158
+ return String(error.stdout);
9159
+ }
9160
+ throw error;
9161
+ }
9162
+ }
9163
+
9123
9164
  // src/commands/sessions/web/resolveCommit.ts
9124
9165
  var GIT_EMPTY_TREE_HASH = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
9125
- async function resolveCommit(cwd, sha) {
9166
+ async function commitBase(cwd, sha) {
9126
9167
  if (!await commitExists(cwd, sha)) return void 0;
9127
- const parent = await parentOf(cwd, sha);
9128
- return {
9129
- base: parent ?? GIT_EMPTY_TREE_HASH,
9130
- paths: await commitPaths(cwd, sha)
9131
- };
9168
+ return await parentOf(cwd, sha) ?? GIT_EMPTY_TREE_HASH;
9169
+ }
9170
+ async function resolveCommit(cwd, sha) {
9171
+ const base = await commitBase(cwd, sha);
9172
+ if (!base) return void 0;
9173
+ return { base, paths: await commitPaths(cwd, sha) };
9132
9174
  }
9133
9175
  async function commitExists(cwd, sha) {
9134
9176
  try {
@@ -9197,20 +9239,6 @@ async function loadCommittedPaths(cwd, sessionId) {
9197
9239
  return { commits: commits2, bases };
9198
9240
  }
9199
9241
 
9200
- // src/commands/sessions/web/windowsCwdToWslPath.ts
9201
- function windowsCwdToWslPath(cwd) {
9202
- const match = /^([A-Za-z]):[\\/](.*)$/.exec(cwd);
9203
- if (!match) return cwd;
9204
- const drive = match[1].toLowerCase();
9205
- const rest = match[2].replace(/\\/g, "/");
9206
- return `/mnt/${drive}/${rest}`;
9207
- }
9208
-
9209
- // src/commands/sessions/web/toGitCwd.ts
9210
- function toGitCwd(cwd) {
9211
- return detectPlatform() === "wsl" ? windowsCwdToWslPath(cwd) : cwd;
9212
- }
9213
-
9214
9242
  // src/commands/sessions/web/itemChangeSet.ts
9215
9243
  async function itemChangeSet(cwd, sessionId) {
9216
9244
  if (!sessionId || !includesCommittedChanges(cwd)) return void 0;
@@ -9221,15 +9249,6 @@ async function itemChangeSet(cwd, sessionId) {
9221
9249
  if (dirty.length > 0) groups.push({ base: "HEAD", paths: dirty });
9222
9250
  return { commits: commits2, groups };
9223
9251
  }
9224
- function includesCommittedChanges(cwd) {
9225
- try {
9226
- return Boolean(
9227
- loadConfigFrom(toGitCwd(cwd)).sessions?.includeCommittedChanges
9228
- );
9229
- } catch {
9230
- return false;
9231
- }
9232
- }
9233
9252
  function groupByBase(bases) {
9234
9253
  const groups = /* @__PURE__ */ new Map();
9235
9254
  for (const [path71, base] of bases) {
@@ -9244,8 +9263,13 @@ async function dirtyPaths(cwd, bases) {
9244
9263
  return output.split("\n").filter((path71) => path71 && !bases.has(path71)).sort();
9245
9264
  }
9246
9265
 
9247
- // src/commands/sessions/web/diff.ts
9266
+ // src/commands/sessions/web/scopedDiff.ts
9248
9267
  var MAX_DIFF_BYTES = 50 * 1024 * 1024;
9268
+ async function scopedDiff(cwd, session, scope) {
9269
+ if (scope.kind === "commit") return commitDiff(cwd, scope.sha);
9270
+ const tracked = scope.kind === "uncommitted" ? await workingTreeDiff(cwd) : await trackedDiff(cwd, session);
9271
+ return tracked + await untrackedDiff(cwd);
9272
+ }
9249
9273
  async function untrackedFileDiff(cwd, path71) {
9250
9274
  return execGit(cwd, ["diff", "--no-index", "--", "/dev/null", path71], {
9251
9275
  maxBuffer: MAX_DIFF_BYTES,
@@ -9274,25 +9298,56 @@ async function groupedDiff(cwd, groups) {
9274
9298
  );
9275
9299
  return diffs.join("");
9276
9300
  }
9301
+ async function workingTreeDiff(cwd) {
9302
+ return execGit(cwd, ["diff", "HEAD"], { maxBuffer: MAX_DIFF_BYTES });
9303
+ }
9277
9304
  async function trackedDiff(cwd, session) {
9278
9305
  const changeSet = await itemChangeSet(cwd, session);
9279
9306
  if (changeSet) return groupedDiff(cwd, changeSet.groups);
9280
- return execGit(cwd, ["diff", "HEAD"], { maxBuffer: MAX_DIFF_BYTES });
9307
+ return workingTreeDiff(cwd);
9308
+ }
9309
+ async function commitDiff(cwd, sha) {
9310
+ const base = await commitBase(cwd, sha);
9311
+ if (!base) return "";
9312
+ return execGit(cwd, ["diff", base, sha], { maxBuffer: MAX_DIFF_BYTES });
9313
+ }
9314
+
9315
+ // src/commands/sessions/web/diff.ts
9316
+ function getScopeParam(req) {
9317
+ const url = new URL(req.url ?? "/", "http://localhost");
9318
+ return url.searchParams.get("scope") ?? void 0;
9281
9319
  }
9282
9320
  async function diff2(req, res) {
9283
9321
  const cwd = getCwdParam(req, res);
9284
9322
  if (!cwd) return;
9323
+ const session = getSessionParam(req);
9285
9324
  try {
9286
- const tracked = await trackedDiff(cwd, getSessionParam(req));
9287
- const untracked = await untrackedDiff(cwd);
9325
+ const scope = await resolveDiffScope(cwd, session, getScopeParam(req));
9326
+ if (!scope) {
9327
+ respondJson(res, 400, { error: "Invalid scope" });
9328
+ return;
9329
+ }
9330
+ const body = await scopedDiff(cwd, session, scope);
9288
9331
  res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
9289
- res.end(tracked + untracked);
9332
+ res.end(body);
9290
9333
  } catch {
9291
9334
  res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
9292
9335
  res.end("");
9293
9336
  }
9294
9337
  }
9295
9338
 
9339
+ // src/commands/sessions/web/diffScopes.ts
9340
+ async function diffScopes(req, res) {
9341
+ const cwd = getCwdParam(req, res);
9342
+ if (!cwd) return;
9343
+ try {
9344
+ const commits2 = await itemScopeCommits(cwd, getSessionParam(req));
9345
+ respondJson(res, 200, { commits: commits2 });
9346
+ } catch {
9347
+ respondJson(res, 200, { commits: [] });
9348
+ }
9349
+ }
9350
+
9296
9351
  // src/commands/sessions/web/fileContent.ts
9297
9352
  import { readFile, stat as stat2 } from "fs/promises";
9298
9353
  import { isAbsolute, relative as relative2, resolve as resolve9 } from "path";
@@ -10396,6 +10451,12 @@ function sessionLayout(_req, res) {
10396
10451
  respondJson(res, 200, { topBar });
10397
10452
  }
10398
10453
 
10454
+ // src/commands/sessions/web/sessionView.ts
10455
+ function sessionView(_req, res) {
10456
+ const floatWaiting = loadConfig().sessions?.floatWaiting ?? false;
10457
+ respondJson(res, 200, { floatWaiting });
10458
+ }
10459
+
10399
10460
  // src/commands/config/setNestedValue.ts
10400
10461
  function isPlainObject(val) {
10401
10462
  return val !== null && typeof val === "object" && !Array.isArray(val);
@@ -10930,11 +10991,13 @@ var routes2 = {
10930
10991
  "GET /api/github-url": githubUrl,
10931
10992
  "GET /api/git-status": gitStatus,
10932
10993
  "GET /api/diff": diff2,
10994
+ "GET /api/diff-scopes": diffScopes,
10933
10995
  "GET /api/file": fileContent,
10934
10996
  "GET /api/files": listFiles,
10935
10997
  "GET /api/jira-site": jiraSite,
10936
10998
  "GET /api/harness": harnessCapabilities,
10937
10999
  "GET /api/session-layout": sessionLayout,
11000
+ "GET /api/session-view": sessionView,
10938
11001
  "GET /api/pr-status": prStatus,
10939
11002
  "GET /api/server-runs": handleServerRuns,
10940
11003
  "GET /api/pr-list": prList,
@@ -27494,6 +27557,7 @@ function toSessionInfo({
27494
27557
  startedAt,
27495
27558
  runningMs,
27496
27559
  runningSince,
27560
+ waitingSince,
27497
27561
  runName,
27498
27562
  runArgs,
27499
27563
  server,
@@ -27526,6 +27590,7 @@ function toSessionInfo({
27526
27590
  startedAt,
27527
27591
  runningMs,
27528
27592
  runningSince,
27593
+ waitingSince,
27529
27594
  runName,
27530
27595
  runArgs,
27531
27596
  server,
@@ -27768,6 +27833,7 @@ function sessionBase(id, status3) {
27768
27833
  /* why: runningMs counts only running stretches, so a session that starts
27769
27834
  * waiting (idle, awaiting first input) has no open stretch to stamp. */
27770
27835
  runningSince: status3 === "running" ? startedAt : null,
27836
+ waitingSince: status3 === "waiting" ? startedAt : null,
27771
27837
  scrollback: ""
27772
27838
  };
27773
27839
  }
@@ -27976,7 +28042,13 @@ function setStatus2(session, newStatus) {
27976
28042
  session.runningMs += now - session.runningSince;
27977
28043
  }
27978
28044
  session.runningSince = newStatus === "running" ? now : null;
28045
+ session.waitingSince = newStatus === "waiting" ? now : null;
27979
28046
  session.status = newStatus;
28047
+ if (newStatus === "waiting") {
28048
+ daemonLog(
28049
+ `session ${session.id} waiting since ${new Date(now).toISOString()}`
28050
+ );
28051
+ }
27980
28052
  }
27981
28053
 
27982
28054
  // src/commands/sessions/daemon/worktree/watchGitState.ts
@@ -29290,6 +29362,7 @@ function errorSession(id, persisted, error) {
29290
29362
  startedAt: persisted.startedAt,
29291
29363
  runningMs: persisted.runningMs ?? 0,
29292
29364
  runningSince: null,
29365
+ waitingSince: null,
29293
29366
  pty: null,
29294
29367
  runName: persisted.runName,
29295
29368
  runArgs: persisted.runArgs,
@@ -29336,6 +29409,7 @@ function restoredSession(base, persisted, pty2, status3) {
29336
29409
  startedAt,
29337
29410
  runningMs: persisted.runningMs ?? 0,
29338
29411
  runningSince: status3 === "running" ? startedAt : null,
29412
+ waitingSince: status3 === "waiting" ? startedAt : null,
29339
29413
  pty: pty2,
29340
29414
  claudeSessionId: persisted.claudeSessionId,
29341
29415
  restored: true,
@@ -29382,6 +29456,7 @@ function notRestoredStub(base, persisted) {
29382
29456
  startedAt: persisted.startedAt,
29383
29457
  runningMs: persisted.runningMs ?? 0,
29384
29458
  runningSince: null,
29459
+ waitingSince: null,
29385
29460
  pty: null,
29386
29461
  runName: persisted.runName,
29387
29462
  runArgs: persisted.runArgs,
@@ -29397,6 +29472,7 @@ function updatedSession(id, persisted) {
29397
29472
  startedAt: persisted.startedAt,
29398
29473
  runningMs: persisted.runningMs ?? 0,
29399
29474
  runningSince: null,
29475
+ waitingSince: null,
29400
29476
  pty: null
29401
29477
  };
29402
29478
  }
@@ -30655,6 +30731,7 @@ function createAssistSession(id, assistArgs, cwd, meta, holdPty) {
30655
30731
  startedAt,
30656
30732
  runningMs: 0,
30657
30733
  runningSince: startedAt,
30734
+ waitingSince: null,
30658
30735
  ...startOrHoldPty(
30659
30736
  () => spawnPty(["assist", ...assistArgs], cwd, id),
30660
30737
  holdPty
@@ -30676,6 +30753,7 @@ function resumeSession(id, sessionId, cwd, name) {
30676
30753
  startedAt,
30677
30754
  runningMs: 0,
30678
30755
  runningSince: null,
30756
+ waitingSince: startedAt,
30679
30757
  pty: spawnClaude2({ resumeSessionId: sessionId, cwd, sessionId: id }),
30680
30758
  scrollback: "",
30681
30759
  cwd,
@@ -31444,6 +31522,11 @@ var sessionsConfigHelp = [
31444
31522
  setter: "assist config set sessions.topBar false -g",
31445
31523
  note: "set false to keep the active session's phase, elapsed, restored indicator, toggles and actions on its card instead of a sticky top bar above the terminal (default on)"
31446
31524
  },
31525
+ {
31526
+ key: "sessions.floatWaiting",
31527
+ setter: "assist config set sessions.floatWaiting true -g",
31528
+ note: "float sessions that have been waiting on input for a few seconds above the other unstarred cards in the sidebar (default off)"
31529
+ },
31447
31530
  {
31448
31531
  key: "worktree.enabled",
31449
31532
  setter: "assist config set worktree.enabled true -g --repo",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.469.5",
3
+ "version": "0.471.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {