@staff0rd/assist 0.962.0 → 0.963.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.962.0",
9
+ version: "0.963.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -6207,7 +6207,7 @@ var adviceConfigHelp = [
6207
6207
  {
6208
6208
  key: "advice.rules",
6209
6209
  setter: "assist rules add '<text>' --title '<title>' --user",
6210
- note: `listed under "Your rules"; user (db), user (node) and repo entries are concatenated in that order. --node writes ~/.assist.yml instead; 'assist config import-user' moves those into user (db)`
6210
+ note: `listed under "Your rules"; user (db), user (node), repo (db) and repo (git) entries are concatenated in that order. --node writes ~/.assist.yml instead; 'assist config import-user' moves those into user (db)`
6211
6211
  }
6212
6212
  ];
6213
6213
 
@@ -6215,7 +6215,7 @@ var adviceConfigHelp = [
6215
6215
  var askQuestionConfigHelp = [
6216
6216
  {
6217
6217
  key: "sessions.askQuestion",
6218
- setter: "assist config set sessions.askQuestion true -g",
6218
+ setter: "assist config set sessions.askQuestion true --node",
6219
6219
  note: "opt in: assist ask-question answers AskUserQuestion questions from the session's web preview pane; when on, assist cli-hook denies AskUserQuestion in assist sessions and an advice fragment points Claude at assist ask-question; when off it exits non-zero so Claude uses the built-in tool (default off)"
6220
6220
  }
6221
6221
  ];
@@ -6276,7 +6276,7 @@ var backupConfigHelp = [
6276
6276
  var autoUpdateConfigHelp = [
6277
6277
  {
6278
6278
  key: "autoUpdate.enabled",
6279
- setter: "assist config set autoUpdate.enabled false -g",
6279
+ setter: "assist config set autoUpdate.enabled false --node",
6280
6280
  note: "when this install is a git clone, the daemon pulls and builds it in the background as origin moves (assist watch wait --pull --build), logging each lap to ~/.assist/watchers/; a branch that cannot fast-forward starts a claude session to reconcile it and the loop resumes once it closes. Set false to stop it (default on)"
6281
6281
  }
6282
6282
  ];
@@ -6317,8 +6317,8 @@ var complexityConfigHelp = [
6317
6317
  var configConfigHelp = [
6318
6318
  {
6319
6319
  key: "repos",
6320
- setter: "assist config set worktree.enabled true -g --repo",
6321
- note: "per-repo overrides stored in the shared db, keyed by repo identity and seen by every node that uses it; written by 'config set -g --repo [name]' or the web /config repo scope, and a yml repos: entry applies only until 'config import-repos' moves it"
6320
+ setter: "assist config set worktree.enabled true --repo",
6321
+ note: "repo (db): per-repo overrides stored in the shared db, keyed by repo identity and seen by every node that uses it; written by 'config set --repo [name]' or the web /config repo (db) scope, and a yml repos: entry applies only until 'config import-repos' moves it"
6322
6322
  }
6323
6323
  ];
6324
6324
 
@@ -6418,32 +6418,32 @@ var newsConfigHelp = [
6418
6418
  var nextConfigHelp = [
6419
6419
  {
6420
6420
  key: "next.peers",
6421
- setter: "assist config set next.peers alice,bob -g --repo",
6421
+ setter: "assist config set next.peers alice,bob --repo",
6422
6422
  note: "GitHub logins whose open, non-draft PRs the web /next view lists for review alongside PRs requesting your review; PRs you approved with no newer commits are hidden"
6423
6423
  },
6424
6424
  {
6425
6425
  key: "next.repos",
6426
- setter: "assist config set next.repos my-org,other/web -g --repo",
6426
+ setter: "assist config set next.repos my-org,other/web --repo",
6427
6427
  note: "GitHub repos the web /next view reads peer PRs and issues assigned to you from: owner/name for one repo, or a bare owner (org or user) for all its unarchived repos (default: the selected repo)"
6428
6428
  },
6429
6429
  {
6430
6430
  key: "next.projects",
6431
- setter: "assist config set next.projects my-org/3,my-org/5 -g --repo",
6431
+ setter: "assist config set next.projects my-org/3,my-org/5 --repo",
6432
6432
  note: "GitHub Projects (owner/number, from each URL) the web /next view suggests unassigned issues to pick up from; needs the gh project scope"
6433
6433
  },
6434
6434
  {
6435
6435
  key: "next.pickStatuses",
6436
- setter: "assist config set next.pickStatuses Ready,Todo -g --repo",
6436
+ setter: "assist config set next.pickStatuses Ready,Todo --repo",
6437
6437
  note: "Project Status values whose unassigned items the web /next view offers to pick up (default: Ready, Todo)"
6438
6438
  },
6439
6439
  {
6440
6440
  key: "next.excludeLabels",
6441
- setter: "assist config set next.excludeLabels blocked,spike -g --repo",
6441
+ setter: "assist config set next.excludeLabels blocked,spike --repo",
6442
6442
  note: "Labels whose project items the web /next view never offers to pick up (case-insensitive)"
6443
6443
  },
6444
6444
  {
6445
6445
  key: "next.excludeTypes",
6446
- setter: "assist config set next.excludeTypes Epic -g --repo",
6446
+ setter: "assist config set next.excludeTypes Epic --repo",
6447
6447
  note: "GitHub issue types whose project items the web /next view never offers to pick up (case-insensitive)"
6448
6448
  }
6449
6449
  ];
@@ -6577,7 +6577,7 @@ var reviewConfigHelp = [
6577
6577
 
6578
6578
  // src/commands/sync/printAutoConfirmHint.ts
6579
6579
  import chalk8 from "chalk";
6580
- var autoConfirmHintCommand = "assist config set sync.autoConfirm true --global";
6580
+ var autoConfirmHintCommand = "assist config set sync.autoConfirm true --node";
6581
6581
  function printAutoConfirmHint() {
6582
6582
  console.log(
6583
6583
  chalk8.dim(
@@ -6666,7 +6666,7 @@ var nodesConfigHelp = [
6666
6666
  },
6667
6667
  {
6668
6668
  key: "sessions.linkVersionCheck",
6669
- setter: "assist config set sessions.linkVersionCheck block -g",
6669
+ setter: "assist config set sessions.linkVersionCheck block --node",
6670
6670
  note: "reaction when a linked node's protocol range doesn't overlap this node's (app versions may differ; never restarts a peer): block (default) holds the link version-blocked until the older node is updated, warn proceeds, off skips the check"
6671
6671
  }
6672
6672
  ];
@@ -6716,7 +6716,7 @@ var EXAMPLES = {
6716
6716
  };
6717
6717
  var hotkeysConfigHelp = Object.keys(ACTIONS).map((name) => ({
6718
6718
  key: `sessions.hotkeys.${name}`,
6719
- setter: `assist config set sessions.hotkeys.${name} ${EXAMPLES[name] ?? defaultHotkeys[name][0]} -g`,
6719
+ setter: `assist config set sessions.hotkeys.${name} ${EXAMPLES[name] ?? defaultHotkeys[name][0]} --node`,
6720
6720
  note: `${ACTIONS[name]}; default ${defaultHotkeys[name].join(" / ")}`
6721
6721
  }));
6722
6722
 
@@ -6724,52 +6724,52 @@ var hotkeysConfigHelp = Object.keys(ACTIONS).map((name) => ({
6724
6724
  var sessionsConfigHelp = [
6725
6725
  {
6726
6726
  key: "sessions.nodeName",
6727
- setter: "assist config set sessions.nodeName <name> -g",
6727
+ setter: "assist config set sessions.nodeName <name> --node",
6728
6728
  note: "this install's node label, shown in the web UI hamburger menu and reported by /api/health (default: OS hostname, with -wsl under WSL)"
6729
6729
  },
6730
6730
  {
6731
6731
  key: "sessions.includeCommittedChanges",
6732
- setter: "assist config set sessions.includeCommittedChanges false -g",
6732
+ setter: "assist config set sessions.includeCommittedChanges false --node",
6733
6733
  note: "default on: session card counts, /diff and the diff scope picker span the commits recorded against the session's backlog item as well as uncommitted work, so they survive the agent committing. Set false to show only uncommitted changes"
6734
6734
  },
6735
6735
  {
6736
6736
  key: "sessions.maxLive",
6737
- setter: "assist config set sessions.maxLive 24 -g",
6737
+ setter: "assist config set sessions.maxLive 24 --node",
6738
6738
  note: "ceiling on concurrent live sessions one daemon holds; spawning past it is refused, and restore respawns at most this many persisted sessions (default: 24)"
6739
6739
  },
6740
6740
  {
6741
6741
  key: "sessions.newSessionMode",
6742
- setter: "assist config set sessions.newSessionMode bug -g",
6742
+ setter: "assist config set sessions.newSessionMode bug --node",
6743
6743
  note: "default mode pre-selected in the Ctrl+N new session dialog: draft | bug | prompt | design (default: draft)"
6744
6744
  },
6745
6745
  {
6746
6746
  key: "worktree.enabled",
6747
- setter: "assist config set worktree.enabled true -g --repo",
6747
+ setter: "assist config set worktree.enabled true --repo",
6748
6748
  note: "opt in per repo: every session spills into a fresh adjacent <clone>-N worktree and the clone itself is never allocated; only inPlace launches (e.g. PR Review) and agents joining a stream stay in their tree (default off)"
6749
6749
  },
6750
6750
  {
6751
6751
  key: "worktree.trunk",
6752
- setter: "assist config set worktree.trunk true -g --repo",
6752
+ setter: "assist config set worktree.trunk true --repo",
6753
6753
  note: "trunk-based: a worktree's branch tracks origin/<the clone's branch> so commits land on the mainline (default off: a worktree starts off the remote default with no mainline tracking, so the session raises its own branch and PR)"
6754
6754
  },
6755
6755
  {
6756
6756
  key: "worktree.root",
6757
- setter: "assist config set worktree.root ~/git -g --repo",
6757
+ setter: "assist config set worktree.root ~/git --repo",
6758
6758
  note: "optional; parent dir for <clone>-N worktrees (default: the clone's parent)"
6759
6759
  },
6760
6760
  {
6761
6761
  key: "worktree.install",
6762
- setter: "assist config set worktree.install true -g --repo",
6762
+ setter: "assist config set worktree.install true --repo",
6763
6763
  note: `per-worktree dep install: true auto-detects the package manager (npm ci when a package-lock.json exists), or give an explicit command; false to skip; or a JSON list of paths (e.g. '[".","packages/ui"]') to auto-detect and install in each, in order, stopping at the first failure`
6764
6764
  },
6765
6765
  {
6766
6766
  key: "worktree.commitBeforePhaseEnd",
6767
- setter: "assist config set worktree.commitBeforePhaseEnd true -g --repo",
6767
+ setter: "assist config set worktree.commitBeforePhaseEnd true --repo",
6768
6768
  note: "phase prompts run /commit once verify passes \u2014 before asking the user to perform manual checks, or before phase-done when the phase has none \u2014 so work is not left uncommitted in a reapable worktree (default off)"
6769
6769
  },
6770
6770
  {
6771
6771
  key: "worktree.copy",
6772
- setter: "assist config set worktree.copy .env,.claude/settings.local.json -g --repo",
6772
+ setter: "assist config set worktree.copy .env,.claude/settings.local.json --repo",
6773
6773
  note: "gitignored files copied into a new worktree so it can build/run"
6774
6774
  }
6775
6775
  ];
@@ -6778,12 +6778,12 @@ var sessionsConfigHelp = [
6778
6778
  var webConfigHelp = [
6779
6779
  {
6780
6780
  key: "sessions.tailscaleServe",
6781
- setter: "assist config set sessions.tailscaleServe false -g",
6781
+ setter: "assist config set sessions.tailscaleServe false --node",
6782
6782
  note: "default on: the web server exposes its port on the tailnet with tailscale serve when it starts, so other nodes can link it by Tailscale name; set false to keep it off the tailnet"
6783
6783
  },
6784
6784
  {
6785
6785
  key: "extension.redirects",
6786
- setter: "assist config set extension.redirects.<owner>/<repo> <owner>/<repo> -g",
6786
+ setter: "assist config set extension.redirects.<owner>/<repo> <owner>/<repo> --node",
6787
6787
  note: "maps a GitHub owner/repo to the owner/repo whose clone under clone.baseDir hosts the browser extension's issue sessions; the prompt keeps the original link, and PR review sends are not redirected"
6788
6788
  }
6789
6789
  ];
@@ -6907,6 +6907,20 @@ function isGlobalOnlyConfigKey(key) {
6907
6907
  return GLOBAL_ONLY_KEYS.some((k) => key.startsWith(k));
6908
6908
  }
6909
6909
 
6910
+ // src/commands/config/isNodeOnlyConfigKey.ts
6911
+ var DB_EXCLUDED_ROOTS = ["repos", "database"];
6912
+ function isNodeOnlyConfigKey(key) {
6913
+ const root2 = key.split(".")[0];
6914
+ return isGlobalOnlyConfigKey(key) || DB_EXCLUDED_ROOTS.includes(root2);
6915
+ }
6916
+
6917
+ // src/commands/config/configWritableScopes.ts
6918
+ function configWritableScopes(key) {
6919
+ if (isGlobalOnlyConfigKey(key)) return ["global"];
6920
+ if (isNodeOnlyConfigKey(key)) return ["project", "repo", "global"];
6921
+ return void 0;
6922
+ }
6923
+
6910
6924
  // src/commands/config/resolveConfigSources.ts
6911
6925
  function resolveConfigSources(key, layers) {
6912
6926
  const sources = [];
@@ -6924,6 +6938,7 @@ function configEntryFor(leaf, { config, layers, globalConfigFile, schema: schema
6924
6938
  const sources = resolveConfigSources(leaf.key, layers);
6925
6939
  const node = configEntryNode(schema2, leaf.key);
6926
6940
  const help = configHelpForKey(leaf.key);
6941
+ const writableScopes = configWritableScopes(leaf.key);
6927
6942
  return {
6928
6943
  ...leaf,
6929
6944
  ...help ? { note: help.note, setter: help.setter } : {},
@@ -6935,7 +6950,7 @@ function configEntryFor(leaf, { config, layers, globalConfigFile, schema: schema
6935
6950
  ...layers.repoKey ? { repoKey: layers.repoKey } : {},
6936
6951
  ...layers.repoSource ? { repoSource: layers.repoSource } : {},
6937
6952
  globalConfigFile,
6938
- globalOnly: isGlobalOnlyConfigKey(leaf.key),
6953
+ ...writableScopes ? { writableScopes } : {},
6939
6954
  node
6940
6955
  };
6941
6956
  }
@@ -6977,7 +6992,7 @@ function isDbBackedConfigKey(key) {
6977
6992
  return Object.hasOwn(dbBackedConfigKeys, key);
6978
6993
  }
6979
6994
  function dbBackedRepoError(key) {
6980
- return `"${key}" is stored in the database and shared by every repo; drop --repo`;
6995
+ return `"${key}" is stored in user (db) and shared by every repo; drop --repo`;
6981
6996
  }
6982
6997
 
6983
6998
  // src/commands/config/setNestedValue.ts
@@ -7048,17 +7063,15 @@ async function withDbBackedConfigValues(config, keys) {
7048
7063
  }
7049
7064
 
7050
7065
  // src/commands/config/withDbBackedConfigEntries.ts
7051
- var DB_BACKED_CONFIG_FILE = "the shared db (all nodes)";
7052
7066
  function dbBackedEntry(entry, value) {
7053
7067
  const set = value !== void 0;
7054
7068
  return {
7055
7069
  ...entry,
7056
7070
  value: set ? value : entry.defaultValue,
7057
- source: set ? "global" : "default",
7058
- sources: set ? ["global"] : [],
7059
- layers: set ? { global: value } : {},
7060
- globalConfigFile: DB_BACKED_CONFIG_FILE,
7061
- globalOnly: true
7071
+ source: set ? "user" : "default",
7072
+ sources: set ? ["user"] : [],
7073
+ layers: set ? { user: value } : {},
7074
+ writableScopes: ["user"]
7062
7075
  };
7063
7076
  }
7064
7077
  async function withDbBackedConfigEntries(entries) {
@@ -10969,25 +10982,31 @@ async function nodeDoctor(req, res) {
10969
10982
  respondJson(res, 200, { hop: await firstBrokenHop(link3) ?? null });
10970
10983
  }
10971
10984
 
10985
+ // src/commands/config/configScopeNames.ts
10986
+ var configScopeNames = {
10987
+ project: "repo (git)",
10988
+ repo: "repo (db)",
10989
+ global: "user (node)",
10990
+ user: "user (db)"
10991
+ };
10992
+
10972
10993
  // src/commands/config/applyDbBackedConfigWrite.ts
10973
10994
  async function applyDbBackedConfigWrite(key, scope, write) {
10974
- if (scope !== "global")
10995
+ const target = configScopeNames.user;
10996
+ if (scope !== "user")
10975
10997
  return {
10976
10998
  ok: false,
10977
- errors: [`"${key}" is stored in the shared db; save it globally`]
10999
+ errors: [`"${key}" is stored in ${target}; save it there`]
10978
11000
  };
10979
11001
  const handler = dbBackedConfigKeys[key];
10980
11002
  const db = await getDb();
10981
11003
  if ("unset" in write)
10982
11004
  return {
10983
11005
  ok: true,
10984
- payload: {
10985
- target: DB_BACKED_CONFIG_FILE,
10986
- removed: await handler.unset(db)
10987
- }
11006
+ payload: { target, removed: await handler.unset(db) }
10988
11007
  };
10989
11008
  await handler.write(db, write.value);
10990
- return { ok: true, payload: { target: DB_BACKED_CONFIG_FILE } };
11009
+ return { ok: true, payload: { target } };
10991
11010
  }
10992
11011
 
10993
11012
  // src/commands/config/coerceConfigScalar.ts
@@ -11213,7 +11232,7 @@ function applyConfigSet(key, coerced, global, cwd = process.cwd(), globalConfigP
11213
11232
  return {
11214
11233
  ok: false,
11215
11234
  errors: [
11216
- `"${key}" is a global-only key. Use --global to set it in ~/.assist.yml`
11235
+ `"${key}" is a user (node)-only key. Use --node to set it in ~/.assist.yml`
11217
11236
  ]
11218
11237
  };
11219
11238
  }
@@ -11235,7 +11254,7 @@ function globalOnlyRepoKeyError(key, verb) {
11235
11254
  return {
11236
11255
  ok: false,
11237
11256
  errors: [
11238
- `"${key}" is a global-only key. ${verb} it in ~/.assist.yml rather than under repos:`
11257
+ `"${key}" is a user (node)-only key. ${verb} it in ~/.assist.yml with --node rather than in repo (db)`
11239
11258
  ]
11240
11259
  };
11241
11260
  }
@@ -11342,10 +11361,10 @@ async function applySharedRepoConfigSet(key, coerced, repoName, cwd = process.cw
11342
11361
 
11343
11362
  // src/shared/db/updateUserConfig.ts
11344
11363
  import { eq as eq24 } from "drizzle-orm";
11345
- async function updateUserConfig(orm, update3) {
11364
+ async function updateUserConfig(orm, update4) {
11346
11365
  return orm.transaction(async (tx) => {
11347
11366
  const [row3] = await tx.select().from(userConfig).where(eq24(userConfig.id, 1)).for("update");
11348
- const outcome = update3(row3?.config ?? {});
11367
+ const outcome = update4(row3?.config ?? {});
11349
11368
  if (!outcome.ok) return outcome;
11350
11369
  if (Object.keys(outcome.config).length === 0) {
11351
11370
  await tx.delete(userConfig).where(eq24(userConfig.id, 1));
@@ -11360,17 +11379,12 @@ async function updateUserConfig(orm, update3) {
11360
11379
  }
11361
11380
 
11362
11381
  // src/commands/config/userDbKeyError.ts
11363
- var DB_EXCLUDED_ROOTS = ["repos", "database"];
11364
- function isNodeOnlyKey(key) {
11365
- const root2 = key.split(".")[0];
11366
- return isGlobalOnlyConfigKey(key) || DB_EXCLUDED_ROOTS.includes(root2);
11367
- }
11368
11382
  function userDbKeyError(key, verb) {
11369
- if (!isNodeOnlyKey(key)) return void 0;
11383
+ if (!isNodeOnlyConfigKey(key)) return void 0;
11370
11384
  return {
11371
11385
  ok: false,
11372
11386
  errors: [
11373
- `"${key}" can't be stored in user (db); it stays in user (node). ${verb} it in ~/.assist.yml with -g`
11387
+ `"${key}" can't be stored in user (db); it stays in user (node). ${verb} it in ~/.assist.yml with --node`
11374
11388
  ]
11375
11389
  };
11376
11390
  }
@@ -11430,7 +11444,10 @@ async function parseConfigWriteRequest(req) {
11430
11444
  if (typeof cwd !== "string" || cwd === "")
11431
11445
  return { ok: false, error: "Missing cwd" };
11432
11446
  if (!isConfigWriteScope(scope))
11433
- return { ok: false, error: 'scope must be "project", "global" or "repo"' };
11447
+ return {
11448
+ ok: false,
11449
+ error: 'scope must be "project", "repo", "global" or "user"'
11450
+ };
11434
11451
  return { ok: true, key, value: body.value, cwd, scope };
11435
11452
  }
11436
11453
 
@@ -11564,7 +11581,7 @@ function applyConfigUnset(key, global, cwd = process.cwd(), globalConfigPath) {
11564
11581
  return {
11565
11582
  ok: false,
11566
11583
  errors: [
11567
- `"${key}" is a global-only key. Use --global to unset it in ~/.assist.yml`
11584
+ `"${key}" is a user (node)-only key. Use --node to unset it in ~/.assist.yml`
11568
11585
  ]
11569
11586
  };
11570
11587
  }
@@ -21477,7 +21494,7 @@ function loadDenyConfig(global) {
21477
21494
 
21478
21495
  // src/commands/deny/denyAdd.ts
21479
21496
  function denyAdd(pattern2, message3, options2) {
21480
- const { deny, saveDeny } = loadDenyConfig(options2.global);
21497
+ const { deny, saveDeny } = loadDenyConfig(options2.node);
21481
21498
  if (deny.some((r) => r.pattern === pattern2)) {
21482
21499
  console.log(chalk112.yellow(`Deny rule already exists for: ${pattern2}`));
21483
21500
  return;
@@ -21515,7 +21532,7 @@ function denyList() {
21515
21532
  // src/commands/deny/denyRemove.ts
21516
21533
  import chalk114 from "chalk";
21517
21534
  function denyRemove(pattern2, options2) {
21518
- const { deny, saveDeny } = loadDenyConfig(options2.global);
21535
+ const { deny, saveDeny } = loadDenyConfig(options2.node);
21519
21536
  const index2 = deny.findIndex((r) => r.pattern === pattern2);
21520
21537
  if (index2 === -1) {
21521
21538
  console.log(chalk114.yellow(`No deny rule found for: ${pattern2}`));
@@ -21530,8 +21547,8 @@ function denyRemove(pattern2, options2) {
21530
21547
  function registerDeny(parent) {
21531
21548
  const denyCommand = parent.command("deny").description("Manage command deny rules").action(denyList);
21532
21549
  configHelp(denyCommand, denyConfigHelp);
21533
- denyCommand.command("add").description("Add a deny rule for a command pattern").argument("<pattern>", "Command prefix to deny").argument("<message>", "Correction message shown to the agent").option("-g, --global", "Write to global ~/.assist.yml").action(denyAdd);
21534
- denyCommand.command("remove").description("Remove a deny rule by pattern").argument("<pattern>", "Command prefix to remove").option("-g, --global", "Remove from global ~/.assist.yml").action(denyRemove);
21550
+ denyCommand.command("add").description("Add a deny rule for a command pattern").argument("<pattern>", "Command prefix to deny").argument("<message>", "Correction message shown to the agent").option("--node", "Write to user (node): this node's ~/.assist.yml").action(denyAdd);
21551
+ denyCommand.command("remove").description("Remove a deny rule by pattern").argument("<pattern>", "Command prefix to remove").option("--node", "Remove from user (node): this node's ~/.assist.yml").action(denyRemove);
21535
21552
  denyCommand.command("list").description("List all deny rules").action(denyList);
21536
21553
  }
21537
21554
 
@@ -23427,25 +23444,25 @@ import chalk136 from "chalk";
23427
23444
  async function setDbBackedConfigKey(key, value, useRepo) {
23428
23445
  if (useRepo) exitWithConfigErrors([dbBackedRepoError(key)]);
23429
23446
  await dbBackedConfigKeys[key].write(await getDb(), value);
23430
- console.log(chalk136.green(`Set ${key} = ${JSON.stringify(value)} (database)`));
23447
+ console.log(chalk136.green(`Set ${key} = ${JSON.stringify(value)} (user (db))`));
23431
23448
  }
23432
23449
 
23433
23450
  // src/commands/config/setFileConfigKey.ts
23434
23451
  import chalk137 from "chalk";
23435
- async function setFileConfigKey({ key, useRepo, repoName, global, user }, coerced) {
23436
- const target = useRepo ? `repo: ${await applyRepoOrExit(key, coerced, repoName)}` : user ? await applyUserOrExit(key, coerced) : applyOrExit(key, coerced, global);
23452
+ async function setFileConfigKey({ key, useRepo, repoName, node, user }, coerced) {
23453
+ const target = useRepo ? `${configScopeNames.repo}: ${await applyRepoOrExit(key, coerced, repoName)}` : user ? await applyUserOrExit(key, coerced) : applyOrExit(key, coerced, node);
23437
23454
  const shown = JSON.stringify(maskConfigKeySecrets(key, coerced));
23438
23455
  console.log(chalk137.green(`Set ${key} = ${shown} (${target})`));
23439
23456
  }
23440
- function applyOrExit(key, coerced, global) {
23441
- const result = applyConfigSet(key, coerced, global);
23457
+ function applyOrExit(key, coerced, node) {
23458
+ const result = applyConfigSet(key, coerced, node);
23442
23459
  if (!result.ok) exitWithConfigErrors(result.errors);
23443
- return result.target;
23460
+ return configScopeNames[result.target];
23444
23461
  }
23445
23462
  async function applyUserOrExit(key, coerced) {
23446
23463
  const result = await applyUserConfigSet(key, coerced);
23447
23464
  if (!result.ok) exitWithConfigErrors(result.errors);
23448
- return "user (db)";
23465
+ return configScopeNames.user;
23449
23466
  }
23450
23467
  async function applyRepoOrExit(key, coerced, repoName) {
23451
23468
  const result = await applySharedRepoConfigSet(key, coerced, repoName);
@@ -23454,17 +23471,20 @@ async function applyRepoOrExit(key, coerced, repoName) {
23454
23471
  }
23455
23472
 
23456
23473
  // src/commands/config/scopeOptionsError.ts
23457
- function scopeOptionsError(options2, repoAction) {
23458
- if (options2.user && (options2.global || options2.repo !== void 0))
23459
- return "--user writes user (db); drop -g and --repo";
23460
- if (options2.repo !== void 0 && !options2.global)
23461
- return `--repo ${repoAction} the global config; add -g (e.g. -g --repo)`;
23474
+ function scopeOptionsError(options2) {
23475
+ const chosen = [
23476
+ options2.repo !== void 0 && "--repo",
23477
+ options2.node && "--node",
23478
+ options2.user && "--user"
23479
+ ].filter(Boolean);
23480
+ if (chosen.length > 1)
23481
+ return `${chosen.join(" and ")} pick different scopes; pass one: --repo for repo (db), --node for user (node), --user for user (db)`;
23462
23482
  return void 0;
23463
23483
  }
23464
23484
 
23465
23485
  // src/commands/config/configSet.ts
23466
23486
  async function configSet(key, value, options2 = {}) {
23467
- const scopeError = scopeOptionsError(options2, "writes to");
23487
+ const scopeError = scopeOptionsError(options2);
23468
23488
  if (scopeError) exitWithConfigErrors([scopeError]);
23469
23489
  const resolved = resolveRepoTarget(key, value, options2.repo);
23470
23490
  if (resolved.value === void 0) {
@@ -23478,7 +23498,7 @@ async function configSet(key, value, options2 = {}) {
23478
23498
  await setFileConfigKey(
23479
23499
  {
23480
23500
  ...resolved,
23481
- global: options2.global ?? false,
23501
+ node: options2.node ?? false,
23482
23502
  user: options2.user ?? false
23483
23503
  },
23484
23504
  coercion.value
@@ -23504,7 +23524,7 @@ async function unsetDbBackedConfigKey(key, useRepo) {
23504
23524
  if (useRepo) exitWithConfigErrors([dbBackedRepoError(key)]);
23505
23525
  const removed = await dbBackedConfigKeys[key].unset(await getDb());
23506
23526
  console.log(
23507
- removed ? chalk139.green(`Unset ${key} (database)`) : chalk139.yellow(`${key} is not set in the database`)
23527
+ removed ? chalk139.green(`Unset ${key} (user (db))`) : chalk139.yellow(`${key} is not set in user (db)`)
23508
23528
  );
23509
23529
  }
23510
23530
 
@@ -23514,31 +23534,26 @@ async function unsetFileConfigKey({
23514
23534
  key,
23515
23535
  useRepo,
23516
23536
  repoName,
23517
- global,
23537
+ node,
23518
23538
  user
23519
23539
  }) {
23520
- const result = useRepo ? await applySharedRepoConfigUnset(key, repoName) : user ? await applyUserConfigUnset(key) : applyConfigUnset(key, global);
23540
+ const result = useRepo ? await applySharedRepoConfigUnset(key, repoName) : user ? await applyUserConfigUnset(key) : applyConfigUnset(key, node);
23521
23541
  if (!result.ok) exitWithConfigErrors(result.errors);
23522
23542
  if (!result.removed) {
23523
- console.log(chalk140.yellow(`${key} is not set in ${whereLabel(result)}`));
23543
+ console.log(chalk140.yellow(`${key} is not set in ${targetLabel(result)}`));
23524
23544
  return;
23525
23545
  }
23526
23546
  console.log(chalk140.green(`Unset ${key} (${targetLabel(result)})`));
23527
23547
  }
23528
- function whereLabel(result) {
23529
- if (result.target === "repo") return `repos.${result.label}`;
23530
- if (result.target === "user") return "user (db)";
23531
- return `the ${result.target} config`;
23532
- }
23533
23548
  function targetLabel(result) {
23534
- if (result.target === "repo") return `repo: ${result.label}`;
23535
- if (result.target === "user") return "user (db)";
23536
- return result.target;
23549
+ if (result.target === "repo")
23550
+ return `${configScopeNames.repo}: ${result.label}`;
23551
+ return configScopeNames[result.target];
23537
23552
  }
23538
23553
 
23539
23554
  // src/commands/config/configUnset.ts
23540
23555
  async function configUnset(key, options2 = {}) {
23541
- const scopeError = scopeOptionsError(options2, "removes from");
23556
+ const scopeError = scopeOptionsError(options2);
23542
23557
  if (scopeError) exitWithConfigErrors([scopeError]);
23543
23558
  const resolved = resolveRepoUnsetTarget(key, options2.repo);
23544
23559
  if (resolved.key === void 0) {
@@ -23552,24 +23567,28 @@ async function configUnset(key, options2 = {}) {
23552
23567
  key: resolved.key,
23553
23568
  useRepo: resolved.useRepo,
23554
23569
  repoName: resolved.repoName,
23555
- global: options2.global ?? false,
23570
+ node: options2.node ?? false,
23556
23571
  user: options2.user ?? false
23557
23572
  });
23558
23573
  }
23559
23574
 
23560
23575
  // src/commands/config/registerConfigWrites.ts
23561
23576
  function registerConfigWrites(configCommand) {
23562
- configCommand.command("set <key> [value]").description("Set a config value (e.g. commit.push true)").option("-g, --global", "Write to global ~/.assist.yml").option(
23563
- "-r, --repo [name]",
23564
- "Requires -g: write a repo's override to the shared db, seen by every node (defaults to the current repo)"
23577
+ configCommand.command("set <key> [value]").description(
23578
+ "Set a config value (e.g. commit.push true); with no scope flag it writes repo (git), this repo's assist.yml"
23565
23579
  ).option(
23580
+ "-r, --repo [name]",
23581
+ "Write to repo (db): a repo's override in the shared db, seen by every node (defaults to the current repo)"
23582
+ ).option("--node", "Write to user (node): this node's ~/.assist.yml").option(
23566
23583
  "-u, --user",
23567
23584
  "Write to user (db): the shared db, applied to every repo on every node"
23568
23585
  ).action((key, value, options2) => configSet(key, value, options2));
23569
- configCommand.command("unset [key]").description("Remove a config value (e.g. commit.push)").option("-g, --global", "Remove from global ~/.assist.yml").option(
23586
+ configCommand.command("unset [key]").description(
23587
+ "Remove a config value (e.g. commit.push); with no scope flag it removes it from repo (git), this repo's assist.yml"
23588
+ ).option(
23570
23589
  "-r, --repo [name]",
23571
- "Requires -g: remove the key from a repo's shared db override (defaults to the current repo)"
23572
- ).option("-u, --user", "Remove from user (db) in the shared db").action((key, options2) => configUnset(key, options2));
23590
+ "Remove from repo (db): a repo's override in the shared db (defaults to the current repo)"
23591
+ ).option("--node", "Remove from user (node): this node's ~/.assist.yml").option("-u, --user", "Remove from user (db) in the shared db").action((key, options2) => configUnset(key, options2));
23573
23592
  }
23574
23593
 
23575
23594
  // src/commands/registerConfig.ts
@@ -29917,9 +29936,9 @@ async function saveMiroExtract(name, extract2, options2, paths = {}) {
29917
29936
  cwd: paths.cwd ?? process.cwd(),
29918
29937
  globalConfigPath: paths.globalConfigPath ?? getGlobalConfigPath()
29919
29938
  };
29920
- if (options2.repo !== void 0 && !options2.global)
29939
+ if (options2.repo !== void 0 && options2.node)
29921
29940
  throw new MiroExtractError(
29922
- "--repo writes to the global config; add -g (e.g. -g --repo)"
29941
+ "--repo writes repo (db) and --node writes user (node); pass one"
29923
29942
  );
29924
29943
  const key = `miro.extracts.${name}`;
29925
29944
  if (options2.repo !== void 0)
@@ -29927,7 +29946,7 @@ async function saveMiroExtract(name, extract2, options2, paths = {}) {
29927
29946
  const result = applyConfigSet(
29928
29947
  key,
29929
29948
  extract2,
29930
- options2.global ?? false,
29949
+ options2.node ?? false,
29931
29950
  resolved.cwd,
29932
29951
  resolved.globalConfigPath
29933
29952
  );
@@ -30194,9 +30213,12 @@ function registerMiro(program) {
30194
30213
  ).option(
30195
30214
  "--save <name>",
30196
30215
  "Save the selection as this named extract without asking"
30197
- ).option("-g, --global", "Save the extract to global ~/.assist.yml").option(
30216
+ ).option(
30217
+ "--node",
30218
+ "Save the extract to user (node), this node's ~/.assist.yml"
30219
+ ).option(
30198
30220
  "-r, --repo [name]",
30199
- "Requires -g: save the extract to a repo's shared db override instead (defaults to the current repo)"
30221
+ "Save the extract to repo (db), a repo's shared db override (defaults to the current repo)"
30200
30222
  ).action(
30201
30223
  (name, options2) => runExtract(name, options2)
30202
30224
  );
@@ -30651,8 +30673,8 @@ function extractLinkedInPosts(flight, author = findAuthorSlug(flight)) {
30651
30673
  }
30652
30674
 
30653
30675
  // src/commands/netcap/collectVoyagerAttributes.ts
30654
- function attributeDetails(update3) {
30655
- const attrs = asObject(asObject(update3.commentary)?.text)?.attributesV2;
30676
+ function attributeDetails(update4) {
30677
+ const attrs = asObject(asObject(update4.commentary)?.text)?.attributesV2;
30656
30678
  if (!Array.isArray(attrs)) return [];
30657
30679
  return attrs.map((a) => asObject(asObject(a)?.detailData)).filter((d) => d !== void 0);
30658
30680
  }
@@ -30679,23 +30701,23 @@ function hashtagsFrom(details) {
30679
30701
  function textLinksFrom(details) {
30680
30702
  return details.map((detail) => asObject(detail.textLink)?.url).filter((url) => typeof url === "string");
30681
30703
  }
30682
- function articleCardLinksFrom(update3) {
30683
- const article = asObject(asObject(update3.content)?.articleComponent);
30704
+ function articleCardLinksFrom(update4) {
30705
+ const article = asObject(asObject(update4.content)?.articleComponent);
30684
30706
  const url = asObject(article?.navigationContext)?.actionTarget;
30685
30707
  return typeof url === "string" ? [url] : [];
30686
30708
  }
30687
- function linksFrom(update3, details) {
30709
+ function linksFrom(update4, details) {
30688
30710
  return dedupeLinks([
30689
30711
  ...textLinksFrom(details),
30690
- ...articleCardLinksFrom(update3)
30712
+ ...articleCardLinksFrom(update4)
30691
30713
  ]);
30692
30714
  }
30693
- function collectVoyagerAttributes(update3, profiles, authorSlug) {
30694
- const details = attributeDetails(update3);
30715
+ function collectVoyagerAttributes(update4, profiles, authorSlug) {
30716
+ const details = attributeDetails(update4);
30695
30717
  return {
30696
30718
  mentions: mentionsFrom(details, profiles, authorSlug),
30697
30719
  hashtags: hashtagsFrom(details),
30698
- links: linksFrom(update3, details)
30720
+ links: linksFrom(update4, details)
30699
30721
  };
30700
30722
  }
30701
30723
 
@@ -30706,8 +30728,8 @@ function profileSlug(actor) {
30706
30728
  if (typeof target !== "string") return void 0;
30707
30729
  return target.match(/\/in\/([^/?]+)/)?.[1];
30708
30730
  }
30709
- function resolveVoyagerAuthor(update3) {
30710
- const actor = asObject(update3.actor);
30731
+ function resolveVoyagerAuthor(update4) {
30732
+ const actor = asObject(update4.actor);
30711
30733
  const slug2 = profileSlug(actor);
30712
30734
  if (!slug2) return void 0;
30713
30735
  const name = asObject(actor?.name)?.text;
@@ -30717,24 +30739,24 @@ function resolveVoyagerAuthor(update3) {
30717
30739
  }
30718
30740
 
30719
30741
  // src/commands/netcap/buildVoyagerPost.ts
30720
- function commentaryText(update3) {
30721
- const body = asObject(asObject(update3.commentary)?.text)?.text;
30742
+ function commentaryText(update4) {
30743
+ const body = asObject(asObject(update4.commentary)?.text)?.text;
30722
30744
  return typeof body === "string" && body.trim() ? body.trim() : void 0;
30723
30745
  }
30724
- function activityUrnOf(update3) {
30725
- const urn = asObject(update3.metadata)?.backendUrn;
30746
+ function activityUrnOf(update4) {
30747
+ const urn = asObject(update4.metadata)?.backendUrn;
30726
30748
  return typeof urn === "string" ? urn : void 0;
30727
30749
  }
30728
- function buildVoyagerPost(update3, profiles) {
30729
- const text18 = commentaryText(update3);
30750
+ function buildVoyagerPost(update4, profiles) {
30751
+ const text18 = commentaryText(update4);
30730
30752
  if (!text18) return void 0;
30731
- const author = resolveVoyagerAuthor(update3);
30753
+ const author = resolveVoyagerAuthor(update4);
30732
30754
  const { mentions, hashtags, links: links2 } = collectVoyagerAttributes(
30733
- update3,
30755
+ update4,
30734
30756
  profiles,
30735
30757
  author?.slug
30736
30758
  );
30737
- const activityUrn = activityUrnOf(update3);
30759
+ const activityUrn = activityUrnOf(update4);
30738
30760
  return {
30739
30761
  text: text18,
30740
30762
  markdown: linkifyMentions(text18, mentions),
@@ -30802,9 +30824,9 @@ function extractVoyagerPosts(body) {
30802
30824
  const profiles = voyagerProfileMentions(included);
30803
30825
  const posts = [];
30804
30826
  for (const ref of elementUrns(rootObj)) {
30805
- const update3 = byUrn.get(ref);
30806
- if (!update3) continue;
30807
- const post2 = buildVoyagerPost(update3, profiles);
30827
+ const update4 = byUrn.get(ref);
30828
+ if (!update4) continue;
30829
+ const post2 = buildVoyagerPost(update4, profiles);
30808
30830
  if (post2) posts.push(post2);
30809
30831
  }
30810
30832
  return posts;
@@ -33971,7 +33993,7 @@ async function writeConfigKeys(writes, scope, cwd = process.cwd()) {
33971
33993
  return {
33972
33994
  ok: false,
33973
33995
  errors: globalOnly.map(
33974
- (write) => `"${write.key}" is a global-only key. Set it with 'assist config set ${write.key} <value> -g'`
33996
+ (write) => `"${write.key}" is a user (node)-only key. Set it with 'assist config set ${write.key} <value> --node'`
33975
33997
  )
33976
33998
  };
33977
33999
  }
@@ -33979,7 +34001,7 @@ async function writeConfigKeys(writes, scope, cwd = process.cwd()) {
33979
34001
  const applied = applyConfigWrites(loadProjectConfig(cwd), writes);
33980
34002
  if (!applied.ok) return applied;
33981
34003
  saveConfig(applied.updated, cwd);
33982
- return { ok: true, target: "project assist.yml" };
34004
+ return { ok: true, target: "repo (git), this repo's assist.yml" };
33983
34005
  }
33984
34006
  async function writeRepoBlock(writes, cwd) {
33985
34007
  const orm = await getDb();
@@ -33987,7 +34009,10 @@ async function writeRepoBlock(writes, cwd) {
33987
34009
  const applied = applyConfigWrites(block, writes, repoConfigSchema);
33988
34010
  if (!applied.ok) return applied;
33989
34011
  await saveSharedRepoBlock(orm, label, applied.updated);
33990
- return { ok: true, target: `${SHARED_REPO_CONFIG_SOURCE}, repo: ${label}` };
34012
+ return {
34013
+ ok: true,
34014
+ target: `repo (db), ${SHARED_REPO_CONFIG_SOURCE}, repo: ${label}`
34015
+ };
33991
34016
  }
33992
34017
 
33993
34018
  // src/commands/releases/declaredStreamsInScope.ts
@@ -37115,11 +37140,11 @@ async function promptConfigScope() {
37115
37140
  choices: [
37116
37141
  {
37117
37142
  name: "repo",
37118
- message: "this repo's override in the shared db \u2014 personal, seen by every node, as 'config set -g --repo' writes it"
37143
+ message: "repo (db): this repo's override in the shared db \u2014 personal, seen by every node, as 'config set --repo' writes it"
37119
37144
  },
37120
37145
  {
37121
37146
  name: "project",
37122
- message: "assist.yml in this repo \u2014 checked in, shared with the team"
37147
+ message: "repo (git): assist.yml in this repo \u2014 checked in, shared with the team"
37123
37148
  }
37124
37149
  ]
37125
37150
  }).run()
@@ -37840,15 +37865,15 @@ function checkDescriptionSections(body) {
37840
37865
  }
37841
37866
 
37842
37867
  // src/commands/review/highLevel/checkDescriptionWordCap.ts
37843
- function checkDescriptionWordCap(body, cap2) {
37868
+ function checkDescriptionWordCap(body, cap3) {
37844
37869
  const { prose, code } = countReadingWords(body);
37845
37870
  const words = prose + code;
37846
- if (words > cap2)
37871
+ if (words > cap3)
37847
37872
  return {
37848
37873
  status: "fail",
37849
- reason: `${words} words, ${words - cap2} over the ${cap2}-word cap`
37874
+ reason: `${words} words, ${words - cap3} over the ${cap3}-word cap`
37850
37875
  };
37851
- return { status: "pass", reason: `${words} of ${cap2} words` };
37876
+ return { status: "pass", reason: `${words} of ${cap3} words` };
37852
37877
  }
37853
37878
 
37854
37879
  // src/commands/review/highLevel/checkNoMergeConflicts.ts
@@ -45972,6 +45997,25 @@ function toSessionRunInfo({
45972
45997
  };
45973
45998
  }
45974
45999
 
46000
+ // src/commands/sessions/daemon/toSessionViewInfo.ts
46001
+ function toSessionViewInfo({
46002
+ lastUserMessage,
46003
+ lastAssistantMessage,
46004
+ cols,
46005
+ rows,
46006
+ activeViewer,
46007
+ activeViewerNode
46008
+ }) {
46009
+ return {
46010
+ lastUserMessage,
46011
+ lastAssistantMessage,
46012
+ cols,
46013
+ rows,
46014
+ activeViewer,
46015
+ activeViewerNode
46016
+ };
46017
+ }
46018
+
45975
46019
  // src/commands/sessions/daemon/worktree/joinRefusal.ts
45976
46020
  import { existsSync as existsSync85 } from "fs";
45977
46021
  function joinRefusal(session) {
@@ -46006,13 +46050,11 @@ function toSessionInfo(session) {
46006
46050
  seeding,
46007
46051
  pendingPrPreview,
46008
46052
  pendingShow,
46009
- undurable,
46010
- lastUserMessage,
46011
- activeViewer,
46012
- activeViewerNode
46053
+ undurable
46013
46054
  } = session;
46014
46055
  return {
46015
46056
  ...toSessionRunInfo(session),
46057
+ ...toSessionViewInfo(session),
46016
46058
  id,
46017
46059
  name,
46018
46060
  title,
@@ -46035,9 +46077,6 @@ function toSessionInfo(session) {
46035
46077
  pendingPrPreview,
46036
46078
  pendingShow,
46037
46079
  undurable,
46038
- lastUserMessage,
46039
- activeViewer,
46040
- activeViewerNode,
46041
46080
  promptIssue: promptIssueRef(session.initialPrompt),
46042
46081
  promptPr: promptPrNumber(session.initialPrompt),
46043
46082
  joinable: joinRefusal(session) === void 0
@@ -46550,10 +46589,13 @@ var SessionIo = class {
46550
46589
  }
46551
46590
  writeAsDaemon = (id, data) => this.write(id, data, {});
46552
46591
  resize(id, cols, rows, viewer) {
46592
+ const s = this.sessions.get(id);
46593
+ const before = `${s?.cols}x${s?.rows}`;
46553
46594
  resizeSession(this.sessions, id, cols, rows, {
46554
46595
  ...viewer,
46555
46596
  onClaim: this.notify
46556
46597
  });
46598
+ if (s && `${s.cols}x${s.rows}` !== before) this.notify();
46557
46599
  }
46558
46600
  };
46559
46601
 
@@ -48917,73 +48959,6 @@ function deriveTranscriptStatus(entries, opts = {}) {
48917
48959
  return "running";
48918
48960
  }
48919
48961
 
48920
- // src/commands/sessions/shared/renderPrompt.ts
48921
- function renderPrompt(raw) {
48922
- const command = marker(raw, "command-name");
48923
- if (command) {
48924
- const args = marker(raw, "command-args");
48925
- const name = `/${command.replace(/^\/+/, "")}`;
48926
- return args ? `${name} ${args}` : name;
48927
- }
48928
- const bashInput = marker(raw, "bash-input");
48929
- if (bashInput) return `! ${bashInput}`;
48930
- return stripBlocks(raw);
48931
- }
48932
- function marker(raw, tag) {
48933
- const match = raw.match(new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`));
48934
- return match ? match[1].trim() : "";
48935
- }
48936
- function stripBlocks(raw) {
48937
- return raw.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").replace(/<task-notification>[\s\S]*?<\/task-notification>/g, "").replace(/<command-[^>]*>[\s\S]*?<\/command-[^>]*>/g, "").replace(/<local-command-[^>]*>[\s\S]*?<\/local-command-[^>]*>/g, "").replace(/<bash-(stdout|stderr)>[\s\S]*?<\/bash-\1>/g, "").trim();
48938
- }
48939
-
48940
- // src/commands/sessions/shared/renderUserPrompt.ts
48941
- var MAX_LENGTH = 2048;
48942
- var INTERRUPT_PREFIX2 = "[Request interrupted";
48943
- function renderUserPrompt(entry, maxLength = MAX_LENGTH) {
48944
- if (entry.isSidechain || entry.isMeta) return void 0;
48945
- if (entry.type !== "user" || isTaskNotification(entry)) return void 0;
48946
- const raw = promptTextOrNull(entry);
48947
- if (raw === null) return void 0;
48948
- const message3 = renderPrompt(raw);
48949
- return message3 ? cap(message3, maxLength) : void 0;
48950
- }
48951
- function isTaskNotification(entry) {
48952
- return asRecord4(entry.origin)?.kind === "task-notification";
48953
- }
48954
- function promptTextOrNull(entry) {
48955
- const content = asRecord4(entry.message)?.content;
48956
- if (typeof content === "string") return isInterrupt(content) ? null : content;
48957
- if (!Array.isArray(content)) return null;
48958
- const parts = [];
48959
- for (const block of content) {
48960
- const b = asRecord4(block);
48961
- if (b?.type !== "text") continue;
48962
- const text18 = typeof b.text === "string" ? b.text : "";
48963
- if (isInterrupt(text18)) return null;
48964
- parts.push(text18);
48965
- }
48966
- return parts.length === 0 ? null : parts.join("\n");
48967
- }
48968
- function cap(message3, maxLength) {
48969
- return message3.length <= maxLength ? message3 : `${message3.slice(0, maxLength).trimEnd()}\u2026`;
48970
- }
48971
- function isInterrupt(value) {
48972
- return value.trimStart().startsWith(INTERRUPT_PREFIX2);
48973
- }
48974
- function asRecord4(value) {
48975
- return value && typeof value === "object" ? value : null;
48976
- }
48977
-
48978
- // src/commands/sessions/shared/extractLastUserMessage.ts
48979
- function extractLastUserMessage(entries, maxLength) {
48980
- for (let i = entries.length - 1; i >= 0; i--) {
48981
- const message3 = renderUserPrompt(entries[i], maxLength);
48982
- if (message3) return message3;
48983
- }
48984
- return void 0;
48985
- }
48986
-
48987
48962
  // src/commands/sessions/shared/readTranscriptTail.ts
48988
48963
  import * as fs52 from "fs";
48989
48964
  var DEFAULT_MAX_BYTES = 256 * 1024;
@@ -49058,16 +49033,128 @@ function transcriptTailFingerprint(entries) {
49058
49033
  return null;
49059
49034
  }
49060
49035
 
49061
- // src/commands/sessions/daemon/reconcileTranscriptStatus.ts
49062
- function updateLastUserMessage(session, entries, notify2) {
49063
- const message3 = extractLastUserMessage(entries);
49064
- if (message3 === session.lastUserMessage) return;
49065
- session.lastUserMessage = message3;
49036
+ // src/commands/sessions/shared/extractLastAssistantMessage.ts
49037
+ var MAX_LENGTH = 500;
49038
+ function extractLastAssistantMessage(entries) {
49039
+ for (let i = entries.length - 1; i >= 0; i--) {
49040
+ const entry = entries[i];
49041
+ if (entry.isSidechain || entry.type !== "assistant") continue;
49042
+ const text18 = assistantText(entry);
49043
+ if (text18) return cap(text18);
49044
+ }
49045
+ return void 0;
49046
+ }
49047
+ function assistantText(entry) {
49048
+ const content = asRecord4(entry.message)?.content;
49049
+ if (!Array.isArray(content)) return "";
49050
+ const parts = [];
49051
+ for (const block of content) {
49052
+ const b = asRecord4(block);
49053
+ if (b?.type === "text" && typeof b.text === "string" && b.text.trim())
49054
+ parts.push(b.text.trim());
49055
+ }
49056
+ return parts.join("\n");
49057
+ }
49058
+ function cap(text18) {
49059
+ return text18.length <= MAX_LENGTH ? text18 : `${text18.slice(0, MAX_LENGTH).trimEnd()}\u2026`;
49060
+ }
49061
+ function asRecord4(value) {
49062
+ return value && typeof value === "object" ? value : null;
49063
+ }
49064
+
49065
+ // src/commands/sessions/shared/renderPrompt.ts
49066
+ function renderPrompt(raw) {
49067
+ const command = marker(raw, "command-name");
49068
+ if (command) {
49069
+ const args = marker(raw, "command-args");
49070
+ const name = `/${command.replace(/^\/+/, "")}`;
49071
+ return args ? `${name} ${args}` : name;
49072
+ }
49073
+ const bashInput = marker(raw, "bash-input");
49074
+ if (bashInput) return `! ${bashInput}`;
49075
+ return stripBlocks(raw);
49076
+ }
49077
+ function marker(raw, tag) {
49078
+ const match = raw.match(new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`));
49079
+ return match ? match[1].trim() : "";
49080
+ }
49081
+ function stripBlocks(raw) {
49082
+ return raw.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").replace(/<task-notification>[\s\S]*?<\/task-notification>/g, "").replace(/<command-[^>]*>[\s\S]*?<\/command-[^>]*>/g, "").replace(/<local-command-[^>]*>[\s\S]*?<\/local-command-[^>]*>/g, "").replace(/<bash-(stdout|stderr)>[\s\S]*?<\/bash-\1>/g, "").trim();
49083
+ }
49084
+
49085
+ // src/commands/sessions/shared/renderUserPrompt.ts
49086
+ var MAX_LENGTH2 = 2048;
49087
+ var INTERRUPT_PREFIX2 = "[Request interrupted";
49088
+ function renderUserPrompt(entry, maxLength = MAX_LENGTH2) {
49089
+ if (entry.isSidechain || entry.isMeta) return void 0;
49090
+ if (entry.type !== "user" || isTaskNotification(entry)) return void 0;
49091
+ const raw = promptTextOrNull(entry);
49092
+ if (raw === null) return void 0;
49093
+ const message3 = renderPrompt(raw);
49094
+ return message3 ? cap2(message3, maxLength) : void 0;
49095
+ }
49096
+ function isTaskNotification(entry) {
49097
+ return asRecord5(entry.origin)?.kind === "task-notification";
49098
+ }
49099
+ function promptTextOrNull(entry) {
49100
+ const content = asRecord5(entry.message)?.content;
49101
+ if (typeof content === "string") return isInterrupt(content) ? null : content;
49102
+ if (!Array.isArray(content)) return null;
49103
+ const parts = [];
49104
+ for (const block of content) {
49105
+ const b = asRecord5(block);
49106
+ if (b?.type !== "text") continue;
49107
+ const text18 = typeof b.text === "string" ? b.text : "";
49108
+ if (isInterrupt(text18)) return null;
49109
+ parts.push(text18);
49110
+ }
49111
+ return parts.length === 0 ? null : parts.join("\n");
49112
+ }
49113
+ function cap2(message3, maxLength) {
49114
+ return message3.length <= maxLength ? message3 : `${message3.slice(0, maxLength).trimEnd()}\u2026`;
49115
+ }
49116
+ function isInterrupt(value) {
49117
+ return value.trimStart().startsWith(INTERRUPT_PREFIX2);
49118
+ }
49119
+ function asRecord5(value) {
49120
+ return value && typeof value === "object" ? value : null;
49121
+ }
49122
+
49123
+ // src/commands/sessions/shared/extractLastUserMessage.ts
49124
+ function extractLastUserMessage(entries, maxLength) {
49125
+ for (let i = entries.length - 1; i >= 0; i--) {
49126
+ const message3 = renderUserPrompt(entries[i], maxLength);
49127
+ if (message3) return message3;
49128
+ }
49129
+ return void 0;
49130
+ }
49131
+
49132
+ // src/commands/sessions/daemon/updateLastMessages.ts
49133
+ function update3(session, field, message3, label) {
49134
+ if (message3 === session[field]) return false;
49135
+ session[field] = message3;
49066
49136
  daemonLog(
49067
- `session ${session.id} last user message: ${JSON.stringify(message3?.slice(0, 120) ?? null)}`
49137
+ `session ${session.id} last ${label} message: ${JSON.stringify(message3?.slice(0, 120) ?? null)}`
49068
49138
  );
49069
- notify2?.();
49139
+ return true;
49070
49140
  }
49141
+ function updateLastMessages(session, entries, notify2) {
49142
+ const user = update3(
49143
+ session,
49144
+ "lastUserMessage",
49145
+ extractLastUserMessage(entries),
49146
+ "user"
49147
+ );
49148
+ const assistant = update3(
49149
+ session,
49150
+ "lastAssistantMessage",
49151
+ extractLastAssistantMessage(entries),
49152
+ "assistant"
49153
+ );
49154
+ if (user || assistant) notify2?.();
49155
+ }
49156
+
49157
+ // src/commands/sessions/daemon/reconcileTranscriptStatus.ts
49071
49158
  function isFinished(session) {
49072
49159
  return session.status === "done" || session.status === "error" || session.status === "stopped";
49073
49160
  }
@@ -49079,7 +49166,7 @@ async function reconcileTranscriptStatus(session, onStatusChange, notify2) {
49079
49166
  if (fingerprint !== null && fingerprint === session.transcriptFingerprint)
49080
49167
  return;
49081
49168
  session.transcriptFingerprint = fingerprint ?? void 0;
49082
- updateLastUserMessage(session, entries, notify2);
49169
+ updateLastMessages(session, entries, notify2);
49083
49170
  if (isFinished(session)) return;
49084
49171
  const derived = deriveTranscriptStatus(entries, {
49085
49172
  permissionActive: session.permissionActive
@@ -49312,7 +49399,7 @@ var sessionLimits = {
49312
49399
  `refusing to spawn: at ceiling of ${ceiling} live sessions (sessions.maxLive)`
49313
49400
  );
49314
49401
  throw new Error(
49315
- `session ceiling of ${ceiling} reached \u2014 raise it with: assist config set sessions.maxLive <n> -g`
49402
+ `session ceiling of ${ceiling} reached \u2014 raise it with: assist config set sessions.maxLive <n> --node`
49316
49403
  );
49317
49404
  }
49318
49405
  return String(counter.next++);
@@ -50063,7 +50150,7 @@ function restoreBase(id, persisted) {
50063
50150
  }
50064
50151
 
50065
50152
  // src/commands/sessions/daemon/deferredSession.ts
50066
- function deferredSession(id, persisted, cap2) {
50153
+ function deferredSession(id, persisted, cap3) {
50067
50154
  return {
50068
50155
  ...restoreBase(id, persisted),
50069
50156
  status: "stopped",
@@ -50077,13 +50164,13 @@ function deferredSession(id, persisted, cap2) {
50077
50164
  runArgs: persisted.runArgs,
50078
50165
  activity: persisted.activity,
50079
50166
  restored: false,
50080
- scrollback: deferralNotice(persisted, cap2)
50167
+ scrollback: deferralNotice(persisted, cap3)
50081
50168
  };
50082
50169
  }
50083
- function deferralNotice(persisted, cap2) {
50170
+ function deferralNotice(persisted, cap3) {
50084
50171
  return [
50085
50172
  `\r
50086
- \x1B[33mNot resumed: restore was already at its cap of ${cap2} session(s) (raise it with: assist config set sessions.maxLive <n> -g)\x1B[0m\r
50173
+ \x1B[33mNot resumed: restore was already at its cap of ${cap3} session(s) (raise it with: assist config set sessions.maxLive <n> --node)\x1B[0m\r
50087
50174
  `,
50088
50175
  `This card is the handle for it. Its command, working directory (${persisted.cwd})\r
50089
50176
  `,
@@ -50639,23 +50726,23 @@ function logRestoreError(persisted, error) {
50639
50726
  // src/commands/sessions/daemon/restoreAll.ts
50640
50727
  function restoreAll(spawner, sessions) {
50641
50728
  const persisted = dropUnrestorableSessions(loadPersistedSessions());
50642
- const cap2 = sessionLimits.maxRestore();
50729
+ const cap3 = sessionLimits.maxRestore();
50643
50730
  const restoredIds = /* @__PURE__ */ new Map();
50644
- const names = persisted.slice(0, cap2).map((entry) => {
50731
+ const names = persisted.slice(0, cap3).map((entry) => {
50645
50732
  const id = restoreOne(entry, spawner, sessions);
50646
50733
  if (entry.id != null && id != null) restoredIds.set(entry.id, id);
50647
50734
  return entry.name;
50648
50735
  });
50649
- for (const over of persisted.slice(cap2))
50650
- deferOne(over, spawner.recoveryCard, cap2);
50736
+ for (const over of persisted.slice(cap3))
50737
+ deferOne(over, spawner.recoveryCard, cap3);
50651
50738
  remapLaunchedFrom(sessions, restoredIds);
50652
50739
  return names;
50653
50740
  }
50654
- function deferOne(persisted, spawnRecoveryCard, cap2) {
50741
+ function deferOne(persisted, spawnRecoveryCard, cap3) {
50655
50742
  try {
50656
- const id = spawnRecoveryCard((sid) => deferredSession(sid, persisted, cap2));
50743
+ const id = spawnRecoveryCard((sid) => deferredSession(sid, persisted, cap3));
50657
50744
  daemonLog(
50658
- `restore capped at ${cap2} (sessions.maxLive): ${describePersistedSession(persisted)} deferred to stopped card ${id}`
50745
+ `restore capped at ${cap3} (sessions.maxLive): ${describePersistedSession(persisted)} deferred to stopped card ${id}`
50659
50746
  );
50660
50747
  } catch (error) {
50661
50748
  logDroppedSession(persisted, error);