@staff0rd/assist 0.955.2 → 0.956.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command } from "commander";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@staff0rd/assist",
9
- version: "0.955.2",
9
+ version: "0.956.1",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -91,6 +91,7 @@ var package_default = {
91
91
  devDependencies: {
92
92
  "@capacitor/core": "^8.6.0",
93
93
  "@capacitor/preferences": "^8.0.1",
94
+ "@capacitor/status-bar": "^8.0.4",
94
95
  "@electric-sql/pglite": "^0.4.6",
95
96
  "@emotion/cache": "^11.14.0",
96
97
  "@emotion/react": "^11.14.0",
@@ -605,16 +606,87 @@ function mergeRawConfigs(globalRaw, projectRaw) {
605
606
  return merged;
606
607
  }
607
608
 
609
+ // src/shared/readRepoConfigCache.ts
610
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
611
+ import { join as join6 } from "path";
612
+
613
+ // src/shared/loadJson.ts
614
+ import {
615
+ existsSync as existsSync3,
616
+ mkdirSync as mkdirSync2,
617
+ readFileSync as readFileSync3,
618
+ renameSync,
619
+ rmSync as rmSync2,
620
+ writeFileSync
621
+ } from "fs";
622
+ import { homedir as homedir3 } from "os";
623
+ import { join as join5 } from "path";
624
+ function getStoreDir() {
625
+ return process.env.ASSIST_STORE_DIR || join5(homedir3(), ".assist");
626
+ }
627
+ function getStorePath(filename) {
628
+ return join5(getStoreDir(), filename);
629
+ }
630
+ function loadJson(filename) {
631
+ const path108 = getStorePath(filename);
632
+ if (existsSync3(path108)) {
633
+ try {
634
+ return JSON.parse(readFileSync3(path108, "utf8"));
635
+ } catch {
636
+ return {};
637
+ }
638
+ }
639
+ return {};
640
+ }
641
+ function saveJson(filename, data) {
642
+ const dir = getStoreDir();
643
+ if (!existsSync3(dir)) {
644
+ mkdirSync2(dir, { recursive: true });
645
+ }
646
+ const path108 = getStorePath(filename);
647
+ const tmp = `${path108}.${process.pid}.tmp`;
648
+ try {
649
+ writeFileSync(tmp, JSON.stringify(data, null, 2));
650
+ renameSync(tmp, path108);
651
+ } catch (error) {
652
+ rmSync2(tmp, { force: true });
653
+ throw error;
654
+ }
655
+ }
656
+
657
+ // src/shared/readRepoConfigCache.ts
658
+ function repoConfigCachePath() {
659
+ return join6(getStoreDir(), "repo-config-cache.json");
660
+ }
661
+ function readConfigCacheFile() {
662
+ const path108 = repoConfigCachePath();
663
+ if (!existsSync4(path108)) return {};
664
+ try {
665
+ const parsed = JSON.parse(readFileSync4(path108, "utf8"));
666
+ return parsed ?? {};
667
+ } catch {
668
+ return {};
669
+ }
670
+ }
671
+ function readRepoConfigCache() {
672
+ return readConfigCacheFile().repos ?? {};
673
+ }
674
+
675
+ // src/shared/readUserConfigCache.ts
676
+ function readUserConfigCache() {
677
+ return readConfigCacheFile().user ?? {};
678
+ }
679
+
608
680
  // src/shared/resolveRepoLayer.ts
609
681
  import chalk2 from "chalk";
610
682
 
611
683
  // src/shared/findRepoRoot.ts
612
- import { existsSync as existsSync3 } from "fs";
684
+ import { existsSync as existsSync5 } from "fs";
613
685
  import path from "path";
614
686
  function findRepoRoot(dir) {
615
687
  let current = dir;
616
688
  while (current !== path.dirname(current)) {
617
- if (existsSync3(path.join(current, ".git"))) {
689
+ if (existsSync5(path.join(current, ".git"))) {
618
690
  return current;
619
691
  }
620
692
  current = path.dirname(current);
@@ -703,69 +775,6 @@ function getCachedOrigin(cwd) {
703
775
  return origin;
704
776
  }
705
777
 
706
- // src/shared/readRepoConfigCache.ts
707
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
708
- import { join as join6 } from "path";
709
-
710
- // src/shared/loadJson.ts
711
- import {
712
- existsSync as existsSync4,
713
- mkdirSync as mkdirSync2,
714
- readFileSync as readFileSync3,
715
- renameSync,
716
- rmSync as rmSync2,
717
- writeFileSync
718
- } from "fs";
719
- import { homedir as homedir3 } from "os";
720
- import { join as join5 } from "path";
721
- function getStoreDir() {
722
- return process.env.ASSIST_STORE_DIR || join5(homedir3(), ".assist");
723
- }
724
- function getStorePath(filename) {
725
- return join5(getStoreDir(), filename);
726
- }
727
- function loadJson(filename) {
728
- const path108 = getStorePath(filename);
729
- if (existsSync4(path108)) {
730
- try {
731
- return JSON.parse(readFileSync3(path108, "utf8"));
732
- } catch {
733
- return {};
734
- }
735
- }
736
- return {};
737
- }
738
- function saveJson(filename, data) {
739
- const dir = getStoreDir();
740
- if (!existsSync4(dir)) {
741
- mkdirSync2(dir, { recursive: true });
742
- }
743
- const path108 = getStorePath(filename);
744
- const tmp = `${path108}.${process.pid}.tmp`;
745
- try {
746
- writeFileSync(tmp, JSON.stringify(data, null, 2));
747
- renameSync(tmp, path108);
748
- } catch (error) {
749
- rmSync2(tmp, { force: true });
750
- throw error;
751
- }
752
- }
753
-
754
- // src/shared/readRepoConfigCache.ts
755
- function repoConfigCachePath() {
756
- return join6(getStoreDir(), "repo-config-cache.json");
757
- }
758
- function readRepoConfigCache() {
759
- const path108 = repoConfigCachePath();
760
- if (!existsSync5(path108)) return {};
761
- try {
762
- const parsed = JSON.parse(readFileSync4(path108, "utf8"));
763
- return parsed?.repos ?? {};
764
- } catch {
765
- return {};
766
- }
767
- }
768
-
769
778
  // src/commands/backlog/originDisplayName.ts
770
779
  function originDisplayName(origin) {
771
780
  if (origin.startsWith("local:")) {
@@ -1444,7 +1453,7 @@ function loadConfigFrom(cwd, globalConfigPath = getGlobalConfigPath()) {
1444
1453
  const globalRaw = loadRawYaml(globalConfigPath);
1445
1454
  const projectRaw = loadRawYaml(projectConfigPathFrom(cwd));
1446
1455
  const globalWithRepo = mergeRawConfigs(
1447
- globalRaw,
1456
+ mergeRawConfigs(readUserConfigCache(), globalRaw),
1448
1457
  resolveRepoLayer(globalRaw, cwd).override
1449
1458
  );
1450
1459
  const merged = stripLegacyConfigKeys(
@@ -1995,7 +2004,7 @@ function expandTilde(value) {
1995
2004
  import { asc, eq } from "drizzle-orm";
1996
2005
 
1997
2006
  // src/shared/db/schema.ts
1998
- import { integer as integer13, pgTable as pgTable15, text as text15 } from "drizzle-orm/pg-core";
2007
+ import { integer as integer13, pgTable as pgTable16, text as text15 } from "drizzle-orm/pg-core";
1999
2008
 
2000
2009
  // src/shared/db/backups.ts
2001
2010
  import { bigint, integer, pgTable, text, timestamp } from "drizzle-orm/pg-core";
@@ -2251,12 +2260,20 @@ var usagePeaks = pgTable14(
2251
2260
  (t) => [primaryKey10({ columns: [t.window, t.resetsAt, t.segment] })]
2252
2261
  );
2253
2262
 
2263
+ // src/shared/db/userConfig.ts
2264
+ import { jsonb as jsonb2, pgTable as pgTable15, smallint, timestamp as timestamp7 } from "drizzle-orm/pg-core";
2265
+ var userConfig = pgTable15("user_config", {
2266
+ id: smallint().primaryKey().default(1),
2267
+ config: jsonb2().$type().notNull(),
2268
+ updatedAt: timestamp7("updated_at", { withTimezone: true }).notNull().defaultNow()
2269
+ });
2270
+
2254
2271
  // src/shared/db/schema.ts
2255
- var metadata = pgTable15("metadata", {
2272
+ var metadata = pgTable16("metadata", {
2256
2273
  key: text15().primaryKey(),
2257
2274
  value: text15().notNull()
2258
2275
  });
2259
- var feeds = pgTable15("feeds", {
2276
+ var feeds = pgTable16("feeds", {
2260
2277
  id: integer13().generatedByDefaultAsIdentity().primaryKey(),
2261
2278
  url: text15().notNull().unique()
2262
2279
  });
@@ -2276,7 +2293,8 @@ var schema = {
2276
2293
  phaseCycleContext,
2277
2294
  phaseSessions,
2278
2295
  repoConfigs,
2279
- inboxMessages
2296
+ inboxMessages,
2297
+ userConfig
2280
2298
  };
2281
2299
 
2282
2300
  // src/shared/db/planPhaseCounts.ts
@@ -3366,6 +3384,20 @@ var migration0009InboxMessages = {
3366
3384
  sql: sql12
3367
3385
  };
3368
3386
 
3387
+ // src/shared/db/migrations/migration0010UserConfig.ts
3388
+ var sql13 = `
3389
+ CREATE TABLE IF NOT EXISTS user_config (
3390
+ id SMALLINT PRIMARY KEY DEFAULT 1 CHECK (id = 1),
3391
+ config JSONB NOT NULL,
3392
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
3393
+ );
3394
+ `;
3395
+ var migration0010UserConfig = {
3396
+ id: 10,
3397
+ name: "user-config",
3398
+ sql: sql13
3399
+ };
3400
+
3369
3401
  // src/shared/db/migrations.ts
3370
3402
  var migrations = [
3371
3403
  migration0001Baseline,
@@ -3376,7 +3408,8 @@ var migrations = [
3376
3408
  migration0006BackfillPhaseSessions,
3377
3409
  migration0007GithubIssue,
3378
3410
  migration0008RepoConfigs,
3379
- migration0009InboxMessages
3411
+ migration0009InboxMessages,
3412
+ migration0010UserConfig
3380
3413
  ];
3381
3414
  var latestMigrationId = migrations.reduce(
3382
3415
  (max, m) => Math.max(max, m.id),
@@ -3459,8 +3492,8 @@ async function assertMigrationsInSync(exec3) {
3459
3492
  // src/shared/db/migrations/MigrationExecutor.ts
3460
3493
  function pgExecutor(queryable) {
3461
3494
  return {
3462
- exec: (sql29) => queryable.query(sql29),
3463
- query: async (sql29, params) => (await queryable.query(sql29, params)).rows
3495
+ exec: (sql30) => queryable.query(sql30),
3496
+ query: async (sql30, params) => (await queryable.query(sql30, params)).rows
3464
3497
  };
3465
3498
  }
3466
3499
 
@@ -3614,13 +3647,13 @@ async function loadItem(orm, id) {
3614
3647
  }
3615
3648
 
3616
3649
  // src/commands/backlog/saveAllItems.ts
3617
- import { eq as eq8, sql as sql13 } from "drizzle-orm";
3650
+ import { eq as eq8, sql as sql14 } from "drizzle-orm";
3618
3651
 
3619
3652
  // src/commands/backlog/deleteItemRelations.ts
3620
3653
  import { eq as eq7 } from "drizzle-orm";
3621
3654
 
3622
3655
  // src/commands/backlog/searchItemIds.ts
3623
- import { and as and3, asc as asc5, eq as eq9, ilike, or, sql as sql14 } from "drizzle-orm";
3656
+ import { and as and3, asc as asc5, eq as eq9, ilike, or, sql as sql15 } from "drizzle-orm";
3624
3657
  async function searchItemIds(orm, query, origin) {
3625
3658
  const pattern2 = `%${query}%`;
3626
3659
  const numberPattern = `%${query.replace(/^#/, "")}%`;
@@ -3634,7 +3667,7 @@ async function searchItemIds(orm, query, origin) {
3634
3667
  ilike(items.jiraKey, pattern2),
3635
3668
  ilike(comments.text, pattern2),
3636
3669
  ilike(planPhases.name, pattern2),
3637
- ilike(sql14`${items.id}::text`, numberPattern)
3670
+ ilike(sql15`${items.id}::text`, numberPattern)
3638
3671
  )
3639
3672
  )
3640
3673
  ).orderBy(asc5(items.id));
@@ -4052,7 +4085,7 @@ import * as fs4 from "fs";
4052
4085
  function extractSessionMeta(lines2) {
4053
4086
  let sessionId = "";
4054
4087
  let cwd = "";
4055
- let timestamp7 = "";
4088
+ let timestamp8 = "";
4056
4089
  let name = "";
4057
4090
  let commandName = "";
4058
4091
  let commandArgs = "";
@@ -4060,14 +4093,14 @@ function extractSessionMeta(lines2) {
4060
4093
  const entry = safeParse(line);
4061
4094
  if (!entry) continue;
4062
4095
  sessionId ||= strField(entry, "sessionId");
4063
- timestamp7 ||= strField(entry, "timestamp");
4096
+ timestamp8 ||= strField(entry, "timestamp");
4064
4097
  cwd ||= strField(entry, "cwd");
4065
4098
  if (entry.type === "user" && !entry.isMeta) {
4066
4099
  ({ name, commandName, commandArgs } = parseFirstUserEntry(entry));
4067
4100
  break;
4068
4101
  }
4069
4102
  }
4070
- return { sessionId, cwd, timestamp: timestamp7, name, commandName, commandArgs };
4103
+ return { sessionId, cwd, timestamp: timestamp8, name, commandName, commandArgs };
4071
4104
  }
4072
4105
  function parseFirstUserEntry(entry) {
4073
4106
  const raw = messageText(entry);
@@ -4120,13 +4153,13 @@ async function parseSessionFile(filePath, origin = "wsl") {
4120
4153
  handle = await fs4.promises.open(filePath, "r");
4121
4154
  const meta = extractSessionMeta(await readHeadLines(handle));
4122
4155
  if (!meta.sessionId) return null;
4123
- const timestamp7 = meta.timestamp || (await fs4.promises.stat(filePath)).mtime.toISOString();
4156
+ const timestamp8 = meta.timestamp || (await fs4.promises.stat(filePath)).mtime.toISOString();
4124
4157
  return {
4125
4158
  sessionId: meta.sessionId,
4126
4159
  name: meta.name || `Session ${meta.sessionId.slice(0, 8)}`,
4127
4160
  project: deriveProject(meta.cwd, filePath, origin),
4128
4161
  cwd: meta.cwd,
4129
- timestamp: timestamp7,
4162
+ timestamp: timestamp8,
4130
4163
  origin,
4131
4164
  ...deriveHistoryFields(meta.commandName, meta.commandArgs, meta.name)
4132
4165
  };
@@ -5917,6 +5950,7 @@ function readRawConfigLayers(cwd, globalConfigPath = getGlobalConfigPath()) {
5917
5950
  return {
5918
5951
  project: loadRawYaml(projectConfigPathFrom(cwd)),
5919
5952
  global,
5953
+ user: readUserConfigCache(),
5920
5954
  repoOverride: repo.override,
5921
5955
  ...repo.key === void 0 ? {} : { repoKey: repo.key },
5922
5956
  ...repo.source === void 0 ? {} : { repoSource: repo.source }
@@ -6022,7 +6056,8 @@ function getNestedValue(obj, path108) {
6022
6056
  var LAYERS = [
6023
6057
  ["project", "project"],
6024
6058
  ["repo", "repoOverride"],
6025
- ["global", "global"]
6059
+ ["global", "global"],
6060
+ ["user", "user"]
6026
6061
  ];
6027
6062
  function configEntryLayers(key, layers, node) {
6028
6063
  const entryLayers = {};
@@ -6831,6 +6866,7 @@ function resolveConfigSources(key, layers) {
6831
6866
  if (getNestedValue(layers.repoOverride, key) !== void 0)
6832
6867
  sources.push("repo");
6833
6868
  if (getNestedValue(layers.global, key) !== void 0) sources.push("global");
6869
+ if (getNestedValue(layers.user, key) !== void 0) sources.push("user");
6834
6870
  return sources;
6835
6871
  }
6836
6872
 
@@ -7533,11 +7569,11 @@ async function deleteItemComment(res, itemId2, commentId) {
7533
7569
  }
7534
7570
 
7535
7571
  // src/commands/backlog/appendComment.ts
7536
- import { sql as sql15 } from "drizzle-orm";
7572
+ import { sql as sql16 } from "drizzle-orm";
7537
7573
  async function appendComment(orm, itemId2, text18, opts = {}) {
7538
7574
  await orm.insert(comments).values({
7539
7575
  itemId: itemId2,
7540
- idx: sql15`(SELECT COALESCE(MAX(${comments.idx}) + 1, 0) FROM ${comments} WHERE ${comments.itemId} = ${itemId2})`,
7576
+ idx: sql16`(SELECT COALESCE(MAX(${comments.idx}) + 1, 0) FROM ${comments} WHERE ${comments.itemId} = ${itemId2})`,
7541
7577
  text: text18,
7542
7578
  phase: opts.phase ?? null,
7543
7579
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -8030,11 +8066,11 @@ async function historySearch(req, res) {
8030
8066
  }
8031
8067
 
8032
8068
  // src/shared/db/listInboxMessages.ts
8033
- import { desc as desc2, isNull as isNull2, sql as sql16 } from "drizzle-orm";
8069
+ import { desc as desc2, isNull as isNull2, sql as sql17 } from "drizzle-orm";
8034
8070
  function listInboxMessages(orm) {
8035
8071
  return orm.select().from(inboxMessages).where(isNull2(inboxMessages.dismissedAt)).orderBy(
8036
8072
  desc2(
8037
- sql16`coalesce(${inboxMessages.receivedAt}, ${inboxMessages.flaggedAt})`
8073
+ sql17`coalesce(${inboxMessages.receivedAt}, ${inboxMessages.flaggedAt})`
8038
8074
  )
8039
8075
  );
8040
8076
  }
@@ -9180,12 +9216,12 @@ async function countUsagePeaks(db, window) {
9180
9216
  import { and as and7, desc as desc3, eq as eq18, getTableColumns } from "drizzle-orm";
9181
9217
 
9182
9218
  // src/shared/db/avgContextPerCycle.ts
9183
- import { count as count2, sql as sql17 } from "drizzle-orm";
9219
+ import { count as count2, sql as sql18 } from "drizzle-orm";
9184
9220
  function avgContextPerCycle(db) {
9185
9221
  return db.select({
9186
9222
  window: phaseCycleContext.window,
9187
9223
  resetsAt: phaseCycleContext.resetsAt,
9188
- avgContextPct: sql17`cast(avg(${phaseCycleContext.peakContextPct}) as double precision)`.as(
9224
+ avgContextPct: sql18`cast(avg(${phaseCycleContext.peakContextPct}) as double precision)`.as(
9189
9225
  "avg_context_pct"
9190
9226
  ),
9191
9227
  phaseCount: count2().as("phase_count")
@@ -9274,9 +9310,9 @@ function listUsageHistory(req, res) {
9274
9310
  }
9275
9311
 
9276
9312
  // src/shared/db/countItemUsageByOrigin.ts
9277
- import { asc as asc8, desc as desc4, eq as eq19, sql as sql18 } from "drizzle-orm";
9313
+ import { asc as asc8, desc as desc4, eq as eq19, sql as sql19 } from "drizzle-orm";
9278
9314
  async function countItemUsageByOrigin(db) {
9279
- const count10 = sql18`count(distinct ${phaseUsage.itemId})::int`;
9315
+ const count10 = sql19`count(distinct ${phaseUsage.itemId})::int`;
9280
9316
  const rows = await db.select({ origin: items.origin, count: count10 }).from(items).innerJoin(phaseUsage, eq19(phaseUsage.itemId, items.id)).groupBy(items.origin).orderBy(desc4(count10), asc8(items.origin));
9281
9317
  return rows.map((row3) => ({ origin: row3.origin, count: Number(row3.count) }));
9282
9318
  }
@@ -9318,22 +9354,22 @@ function parseItemUsageSort(field, direction) {
9318
9354
  }
9319
9355
 
9320
9356
  // src/shared/db/itemUsageStats.ts
9321
- import { eq as eq21, sql as sql19 } from "drizzle-orm";
9357
+ import { eq as eq21, sql as sql20 } from "drizzle-orm";
9322
9358
  function median(value) {
9323
- return sql19`coalesce(percentile_cont(0.5) within group (order by (${value})::double precision), 0)`;
9359
+ return sql20`coalesce(percentile_cont(0.5) within group (order by (${value})::double precision), 0)`;
9324
9360
  }
9325
9361
  async function itemUsageStats(db, options2) {
9326
9362
  const totals = phaseUsageTotals(db);
9327
9363
  const planned = planPhaseCounts(db);
9328
9364
  const [row3] = await db.select({
9329
- itemCount: sql19`count(*)::int`,
9330
- doneCount: sql19`count(*) filter (where ${items.status} = 'done')::int`,
9331
- repoCount: sql19`count(distinct ${items.origin})::int`,
9365
+ itemCount: sql20`count(*)::int`,
9366
+ doneCount: sql20`count(*) filter (where ${items.status} = 'done')::int`,
9367
+ repoCount: sql20`count(distinct ${items.origin})::int`,
9332
9368
  medianPhases: median(
9333
- sql19`coalesce(${planned.count}, ${totals.recordedPhases})`
9369
+ sql20`coalesce(${planned.count}, ${totals.recordedPhases})`
9334
9370
  ),
9335
9371
  medianActiveMs: median(totals.activeMs),
9336
- medianTokens: median(sql19`${totals.tokensUp} + ${totals.tokensDown}`)
9372
+ medianTokens: median(sql20`${totals.tokensUp} + ${totals.tokensDown}`)
9337
9373
  }).from(items).innerJoin(totals, eq21(totals.itemId, items.id)).leftJoin(planned, eq21(planned.itemId, items.id)).where(itemUsageWhere(options2));
9338
9374
  return {
9339
9375
  itemCount: Number(row3?.itemCount ?? 0),
@@ -9349,27 +9385,27 @@ async function itemUsageStats(db, options2) {
9349
9385
  import { eq as eq22 } from "drizzle-orm";
9350
9386
 
9351
9387
  // src/shared/db/itemUsageOrderBy.ts
9352
- import { desc as desc5, sql as sql20 } from "drizzle-orm";
9388
+ import { desc as desc5, sql as sql21 } from "drizzle-orm";
9353
9389
  function itemUsageOrderBy(joins, sort = defaultItemUsageSort) {
9354
9390
  const { totals, planned, lastPhase } = joins;
9355
9391
  const sortable = {
9356
- phases: sql20`coalesce(${planned.count}, ${totals.recordedPhases})`,
9357
- active: sql20`${totals.activeMs}`,
9358
- tokens: sql20`${totals.tokensUp} + ${totals.tokensDown}`,
9359
- peakContext: sql20`${totals.peakContextPct}`,
9360
- lastPhase: sql20`${lastPhase.at}`
9392
+ phases: sql21`coalesce(${planned.count}, ${totals.recordedPhases})`,
9393
+ active: sql21`${totals.activeMs}`,
9394
+ tokens: sql21`${totals.tokensUp} + ${totals.tokensDown}`,
9395
+ peakContext: sql21`${totals.peakContextPct}`,
9396
+ lastPhase: sql21`${lastPhase.at}`
9361
9397
  };
9362
- const order = sort.direction === "asc" ? sql20`asc` : sql20`desc`;
9363
- return [sql20`${sortable[sort.field]} ${order} nulls last`, desc5(items.id)];
9398
+ const order = sort.direction === "asc" ? sql21`asc` : sql21`desc`;
9399
+ return [sql21`${sortable[sort.field]} ${order} nulls last`, desc5(items.id)];
9364
9400
  }
9365
9401
 
9366
9402
  // src/shared/db/lastPhaseActivity.ts
9367
- import { sql as sql21 } from "drizzle-orm";
9403
+ import { sql as sql22 } from "drizzle-orm";
9368
9404
  function lastPhaseActivity(db) {
9369
9405
  return db.select({
9370
9406
  itemId: phaseSessions.itemId,
9371
- at: sql21`max(${phaseSessions.createdAt})`.as("last_phase_at"),
9372
- atIso: sql21`to_char(max(${phaseSessions.createdAt}) at time zone 'utc', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')`.as(
9407
+ at: sql22`max(${phaseSessions.createdAt})`.as("last_phase_at"),
9408
+ atIso: sql22`to_char(max(${phaseSessions.createdAt}) at time zone 'utc', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')`.as(
9373
9409
  "last_phase_at_iso"
9374
9410
  )
9375
9411
  }).from(phaseSessions).groupBy(phaseSessions.itemId).as("last_phase_activity");
@@ -11206,15 +11242,22 @@ async function saveRepoConfig(orm, repoKey, config) {
11206
11242
  });
11207
11243
  }
11208
11244
 
11245
+ // src/shared/db/readUserConfig.ts
11246
+ async function readUserConfig(orm) {
11247
+ const [row3] = await orm.select().from(userConfig);
11248
+ return row3?.config ?? {};
11249
+ }
11250
+
11209
11251
  // src/shared/writeRepoConfigCache.ts
11210
11252
  import { mkdirSync as mkdirSync6, renameSync as renameSync3, writeFileSync as writeFileSync5 } from "fs";
11211
11253
  import { dirname as dirname12 } from "path";
11212
- function writeRepoConfigCache(repos2) {
11254
+ function writeRepoConfigCache(repos2, user = {}) {
11213
11255
  const path108 = repoConfigCachePath();
11214
11256
  mkdirSync6(dirname12(path108), { recursive: true });
11215
11257
  const cache7 = {
11216
11258
  refreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
11217
- repos: repos2
11259
+ repos: repos2,
11260
+ user
11218
11261
  };
11219
11262
  const tmp = `${path108}.${process.pid}.tmp`;
11220
11263
  writeFileSync5(tmp, JSON.stringify(cache7, null, 2));
@@ -11223,8 +11266,9 @@ function writeRepoConfigCache(repos2) {
11223
11266
 
11224
11267
  // src/shared/refreshRepoConfigCache.ts
11225
11268
  async function refreshRepoConfigCache(orm) {
11226
- const repos2 = await listRepoConfigs(orm ?? await getDb());
11227
- writeRepoConfigCache(repos2);
11269
+ const db = orm ?? await getDb();
11270
+ const repos2 = await listRepoConfigs(db);
11271
+ writeRepoConfigCache(repos2, await readUserConfig(db));
11228
11272
  return Object.keys(repos2).length;
11229
11273
  }
11230
11274
 
@@ -11247,12 +11291,66 @@ async function applySharedRepoConfigSet(key, coerced, repoName, cwd = process.cw
11247
11291
  return { ok: true, target: "repo", label };
11248
11292
  }
11249
11293
 
11294
+ // src/shared/db/updateUserConfig.ts
11295
+ import { eq as eq24 } from "drizzle-orm";
11296
+ async function updateUserConfig(orm, update3) {
11297
+ return orm.transaction(async (tx) => {
11298
+ const [row3] = await tx.select().from(userConfig).where(eq24(userConfig.id, 1)).for("update");
11299
+ const outcome = update3(row3?.config ?? {});
11300
+ if (!outcome.ok) return outcome;
11301
+ if (Object.keys(outcome.config).length === 0) {
11302
+ await tx.delete(userConfig).where(eq24(userConfig.id, 1));
11303
+ return outcome;
11304
+ }
11305
+ await tx.insert(userConfig).values({ id: 1, config: outcome.config }).onConflictDoUpdate({
11306
+ target: userConfig.id,
11307
+ set: { config: outcome.config, updatedAt: /* @__PURE__ */ new Date() }
11308
+ });
11309
+ return outcome;
11310
+ });
11311
+ }
11312
+
11313
+ // src/commands/config/userDbKeyError.ts
11314
+ var DB_EXCLUDED_ROOTS = ["repos", "database"];
11315
+ function isNodeOnlyKey(key) {
11316
+ const root2 = key.split(".")[0];
11317
+ return isGlobalOnlyConfigKey(key) || DB_EXCLUDED_ROOTS.includes(root2);
11318
+ }
11319
+ function userDbKeyError(key, verb) {
11320
+ if (!isNodeOnlyKey(key)) return void 0;
11321
+ return {
11322
+ ok: false,
11323
+ errors: [
11324
+ `"${key}" can't be stored in user (db); it stays in user (node). ${verb} it in ~/.assist.yml with -g`
11325
+ ]
11326
+ };
11327
+ }
11328
+
11329
+ // src/commands/config/applyUserConfigSet.ts
11330
+ async function applyUserConfigSet(key, coerced) {
11331
+ const nodeOnly = userDbKeyError(key, "Set");
11332
+ if (nodeOnly) return nodeOnly;
11333
+ const orm = await getDb();
11334
+ const outcome = await updateUserConfig(orm, (current) => {
11335
+ const config = setNestedValue(current, key, coerced);
11336
+ const validation = validateConfig(config, key);
11337
+ return validation.ok ? { ok: true, config, result: void 0 } : validation;
11338
+ });
11339
+ if (!outcome.ok) return outcome;
11340
+ await refreshRepoConfigCache(orm);
11341
+ return { ok: true, target: "user" };
11342
+ }
11343
+
11250
11344
  // src/commands/sessions/web/applyScopedConfigSet.ts
11251
11345
  async function applyScopedConfigSet(key, value, cwd, scope, globalConfigPath) {
11252
11346
  if (scope === "repo") {
11253
11347
  const result2 = await applySharedRepoConfigSet(key, value, void 0, cwd);
11254
11348
  return result2.ok ? { ok: true, payload: { target: result2.target, repoKey: result2.label } } : result2;
11255
11349
  }
11350
+ if (scope === "user") {
11351
+ const result2 = await applyUserConfigSet(key, value);
11352
+ return result2.ok ? { ok: true, payload: { target: result2.target } } : result2;
11353
+ }
11256
11354
  const result = applyConfigSet(
11257
11355
  key,
11258
11356
  value,
@@ -11265,7 +11363,7 @@ async function applyScopedConfigSet(key, value, cwd, scope, globalConfigPath) {
11265
11363
 
11266
11364
  // src/commands/config/ConfigWriteScope.ts
11267
11365
  function isConfigWriteScope(value) {
11268
- return value === "project" || value === "global" || value === "repo";
11366
+ return value === "project" || value === "global" || value === "repo" || value === "user";
11269
11367
  }
11270
11368
 
11271
11369
  // src/commands/sessions/web/parseConfigWriteRequest.ts
@@ -11452,6 +11550,22 @@ async function applySharedRepoConfigUnset(key, repoName, cwd = process.cwd()) {
11452
11550
  return { ok: true, target: "repo", label, removed: unset.removed };
11453
11551
  }
11454
11552
 
11553
+ // src/commands/config/applyUserConfigUnset.ts
11554
+ async function applyUserConfigUnset(key) {
11555
+ const nodeOnly = userDbKeyError(key, "Unset");
11556
+ if (nodeOnly) return nodeOnly;
11557
+ const orm = await getDb();
11558
+ const outcome = await updateUserConfig(orm, (current) => {
11559
+ const { config, removed } = unsetNestedValue(current, key);
11560
+ if (!removed) return { ok: true, config: current, result: false };
11561
+ const validation = validateConfig(config, key);
11562
+ return validation.ok ? { ok: true, config, result: true } : validation;
11563
+ });
11564
+ if (!outcome.ok) return outcome;
11565
+ if (outcome.result) await refreshRepoConfigCache(orm);
11566
+ return { ok: true, target: "user", removed: outcome.result };
11567
+ }
11568
+
11455
11569
  // src/commands/sessions/web/applyScopedConfigUnset.ts
11456
11570
  async function applyScopedConfigUnset(key, cwd, scope, globalConfigPath) {
11457
11571
  if (scope === "repo") {
@@ -11465,6 +11579,13 @@ async function applyScopedConfigUnset(key, cwd, scope, globalConfigPath) {
11465
11579
  }
11466
11580
  } : result2;
11467
11581
  }
11582
+ if (scope === "user") {
11583
+ const result2 = await applyUserConfigUnset(key);
11584
+ return result2.ok ? {
11585
+ ok: true,
11586
+ payload: { target: result2.target, removed: result2.removed }
11587
+ } : result2;
11588
+ }
11468
11589
  const result = applyConfigUnset(
11469
11590
  key,
11470
11591
  scope === "global",
@@ -14963,8 +15084,8 @@ async function buildDump(tables, copyOut) {
14963
15084
  // src/commands/backlog/dump/copyTableOut.ts
14964
15085
  import { to as copyTo } from "pg-copy-streams";
14965
15086
  async function copyTableOut(client, table) {
14966
- const sql29 = `COPY ${table.name} (${table.columns.join(", ")}) TO STDOUT`;
14967
- const stream = client.query(copyTo(sql29));
15087
+ const sql30 = `COPY ${table.name} (${table.columns.join(", ")}) TO STDOUT`;
15088
+ const stream = client.query(copyTo(sql30));
14968
15089
  const chunks = [];
14969
15090
  for await (const chunk of stream) {
14970
15091
  chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
@@ -15076,8 +15197,8 @@ async function backup({ out }) {
15076
15197
  const dir = expandTilde(out ?? loadConfig().backup.dir);
15077
15198
  await mkdir3(dir, { recursive: true });
15078
15199
  const start2 = Date.now();
15079
- const timestamp7 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
15080
- const filePath = resolve15(join42(dir, `backup-${timestamp7}.dump`));
15200
+ const timestamp8 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
15201
+ const filePath = resolve15(join42(dir, `backup-${timestamp8}.dump`));
15081
15202
  await exportBacklog(filePath);
15082
15203
  const { size } = await stat4(filePath);
15083
15204
  const durationMs = Date.now() - start2;
@@ -15428,11 +15549,11 @@ function registerActivityCommands(cmd) {
15428
15549
 
15429
15550
  // src/commands/backlog/associateGithub.ts
15430
15551
  import chalk43 from "chalk";
15431
- import { eq as eq25 } from "drizzle-orm";
15552
+ import { eq as eq26 } from "drizzle-orm";
15432
15553
 
15433
15554
  // src/commands/backlog/beginAssociation.ts
15434
15555
  import chalk42 from "chalk";
15435
- import { eq as eq24 } from "drizzle-orm";
15556
+ import { eq as eq25 } from "drizzle-orm";
15436
15557
  async function beginAssociation(id, options2, clearPatch, label) {
15437
15558
  const found = await findOneItem(id);
15438
15559
  if (!found) {
@@ -15442,7 +15563,7 @@ async function beginAssociation(id, options2, clearPatch, label) {
15442
15563
  const { orm } = found;
15443
15564
  const itemId2 = found.item.id;
15444
15565
  if (options2.clear) {
15445
- await orm.update(items).set(clearPatch).where(eq24(items.id, itemId2));
15566
+ await orm.update(items).set(clearPatch).where(eq25(items.id, itemId2));
15446
15567
  console.log(
15447
15568
  chalk42.green(
15448
15569
  `Cleared ${label} association on item ${formatItemId(itemId2)}.`
@@ -15510,7 +15631,7 @@ async function associateGithub(id, issue, options2) {
15510
15631
  return;
15511
15632
  }
15512
15633
  const title = fetchGithubIssueTitle(normalized);
15513
- await orm.update(items).set({ githubIssue: normalized, jiraKey: null }).where(eq25(items.id, itemId2));
15634
+ await orm.update(items).set({ githubIssue: normalized, jiraKey: null }).where(eq26(items.id, itemId2));
15514
15635
  console.log(
15515
15636
  chalk43.green(`Associated ${normalized} with item ${formatItemId(itemId2)}.`),
15516
15637
  title ? chalk43.dim(`(${title})`) : ""
@@ -15524,7 +15645,7 @@ function registerAssociateGithubCommand(cmd) {
15524
15645
 
15525
15646
  // src/commands/backlog/associateJira.ts
15526
15647
  import chalk45 from "chalk";
15527
- import { eq as eq26 } from "drizzle-orm";
15648
+ import { eq as eq27 } from "drizzle-orm";
15528
15649
 
15529
15650
  // src/commands/jira/fetchIssue.ts
15530
15651
  import { execSync as execSync17 } from "child_process";
@@ -15589,7 +15710,7 @@ async function associateJira(id, key, options2) {
15589
15710
  const parsed = fetchIssue(normalized, "summary");
15590
15711
  const fields = parsed?.fields;
15591
15712
  const summary = fields?.summary;
15592
- await orm.update(items).set({ jiraKey: normalized, githubIssue: null }).where(eq26(items.id, itemId2));
15713
+ await orm.update(items).set({ jiraKey: normalized, githubIssue: null }).where(eq27(items.id, itemId2));
15593
15714
  console.log(
15594
15715
  chalk45.green(`Associated ${normalized} with item ${formatItemId(itemId2)}.`),
15595
15716
  summary ? chalk45.dim(`(${summary})`) : ""
@@ -15871,8 +15992,8 @@ async function confirmReplace(client, tables, incoming, fromStdin) {
15871
15992
  import { finished } from "stream/promises";
15872
15993
  import { from as copyFrom } from "pg-copy-streams";
15873
15994
  async function copyTableIn(client, table, data) {
15874
- const sql29 = `COPY ${table.name} (${table.columns.join(", ")}) FROM STDIN`;
15875
- const stream = client.query(copyFrom(sql29));
15995
+ const sql30 = `COPY ${table.name} (${table.columns.join(", ")}) FROM STDIN`;
15996
+ const stream = client.query(copyFrom(sql30));
15876
15997
  stream.end(data);
15877
15998
  await finished(stream);
15878
15999
  }
@@ -15974,15 +16095,15 @@ function isClaudeCode() {
15974
16095
  }
15975
16096
 
15976
16097
  // src/commands/backlog/insertPhaseAt.ts
15977
- import { eq as eq28 } from "drizzle-orm";
16098
+ import { eq as eq29 } from "drizzle-orm";
15978
16099
 
15979
16100
  // src/commands/backlog/shiftPhasesUp.ts
15980
- import { and as and9, desc as desc6, eq as eq27, gte } from "drizzle-orm";
16101
+ import { and as and9, desc as desc6, eq as eq28, gte } from "drizzle-orm";
15981
16102
  async function shiftPhasesUp(db, itemId2, fromIdx) {
15982
- const toShift = await db.select({ idx: planPhases.idx }).from(planPhases).where(and9(eq27(planPhases.itemId, itemId2), gte(planPhases.idx, fromIdx))).orderBy(desc6(planPhases.idx));
16103
+ const toShift = await db.select({ idx: planPhases.idx }).from(planPhases).where(and9(eq28(planPhases.itemId, itemId2), gte(planPhases.idx, fromIdx))).orderBy(desc6(planPhases.idx));
15983
16104
  for (const p of toShift) {
15984
- await db.update(planTasks).set({ phaseIdx: p.idx + 1 }).where(and9(eq27(planTasks.itemId, itemId2), eq27(planTasks.phaseIdx, p.idx)));
15985
- await db.update(planPhases).set({ idx: p.idx + 1 }).where(and9(eq27(planPhases.itemId, itemId2), eq27(planPhases.idx, p.idx)));
16105
+ await db.update(planTasks).set({ phaseIdx: p.idx + 1 }).where(and9(eq28(planTasks.itemId, itemId2), eq28(planTasks.phaseIdx, p.idx)));
16106
+ await db.update(planPhases).set({ idx: p.idx + 1 }).where(and9(eq28(planPhases.itemId, itemId2), eq28(planPhases.idx, p.idx)));
15986
16107
  }
15987
16108
  }
15988
16109
 
@@ -15995,7 +16116,7 @@ async function insertPhaseAt(orm, itemId2, phaseIdx, name, tasks, manualChecks,
15995
16116
  await tx.insert(planTasks).values(tasks.map((task, i) => ({ itemId: itemId2, phaseIdx, idx: i, task })));
15996
16117
  }
15997
16118
  if (currentPhase !== void 0 && currentPhase - 1 >= phaseIdx) {
15998
- await tx.update(items).set({ currentPhase: phaseIdx + 1 }).where(eq28(items.id, itemId2));
16119
+ await tx.update(items).set({ currentPhase: phaseIdx + 1 }).where(eq29(items.id, itemId2));
15999
16120
  }
16000
16121
  });
16001
16122
  }
@@ -16152,9 +16273,9 @@ import chalk56 from "chalk";
16152
16273
 
16153
16274
  // src/commands/backlog/resolveInsertPosition.ts
16154
16275
  import chalk55 from "chalk";
16155
- import { count as count3, eq as eq29 } from "drizzle-orm";
16276
+ import { count as count3, eq as eq30 } from "drizzle-orm";
16156
16277
  async function resolveInsertPosition(orm, itemId2, position) {
16157
- const [row3] = await orm.select({ cnt: count3() }).from(planPhases).where(eq29(planPhases.itemId, itemId2));
16278
+ const [row3] = await orm.select({ cnt: count3() }).from(planPhases).where(eq30(planPhases.itemId, itemId2));
16158
16279
  const phaseCount = row3?.cnt ?? 0;
16159
16280
  if (position === void 0) return phaseCount;
16160
16281
  const pos = Number.parseInt(position, 10);
@@ -16574,9 +16695,9 @@ function hasCycle(adjacency, fromId, toId) {
16574
16695
  }
16575
16696
 
16576
16697
  // src/commands/backlog/loadDependencyGraph.ts
16577
- import { eq as eq30 } from "drizzle-orm";
16698
+ import { eq as eq31 } from "drizzle-orm";
16578
16699
  async function loadDependencyGraph(orm) {
16579
- const rows = await orm.select({ itemId: links.itemId, targetId: links.targetId }).from(links).where(eq30(links.type, "depends-on"));
16700
+ const rows = await orm.select({ itemId: links.itemId, targetId: links.targetId }).from(links).where(eq31(links.type, "depends-on"));
16580
16701
  const graph = /* @__PURE__ */ new Map();
16581
16702
  for (const { itemId: itemId2, targetId } of rows) {
16582
16703
  const bucket = graph.get(itemId2);
@@ -16643,7 +16764,7 @@ async function link(fromId, toId, opts) {
16643
16764
 
16644
16765
  // src/commands/backlog/unlink.ts
16645
16766
  import chalk64 from "chalk";
16646
- import { and as and10, eq as eq31 } from "drizzle-orm";
16767
+ import { and as and10, eq as eq32 } from "drizzle-orm";
16647
16768
  async function unlink(fromId, toId) {
16648
16769
  const fromNum = parseItemId(fromId);
16649
16770
  const toNum = parseItemId(toId);
@@ -16667,7 +16788,7 @@ async function unlink(fromId, toId) {
16667
16788
  );
16668
16789
  return;
16669
16790
  }
16670
- await orm.delete(links).where(and10(eq31(links.itemId, fromNum), eq31(links.targetId, toNum)));
16791
+ await orm.delete(links).where(and10(eq32(links.itemId, fromNum), eq32(links.targetId, toNum)));
16671
16792
  console.log(
16672
16793
  chalk64.green(
16673
16794
  `Removed link from ${formatItemId(fromNum)} to ${formatItemId(toNum)}.`
@@ -16687,7 +16808,7 @@ function registerLinkCommands(cmd) {
16687
16808
 
16688
16809
  // src/commands/backlog/moveRepo.ts
16689
16810
  import chalk66 from "chalk";
16690
- import { eq as eq33 } from "drizzle-orm";
16811
+ import { eq as eq34 } from "drizzle-orm";
16691
16812
 
16692
16813
  // src/commands/backlog/move-repo/confirmMove.ts
16693
16814
  import chalk65 from "chalk";
@@ -16702,9 +16823,9 @@ async function confirmMove(cnt, oldOrigin, newOrigin) {
16702
16823
  }
16703
16824
 
16704
16825
  // src/commands/backlog/move-repo/countByOrigin.ts
16705
- import { count as count4, eq as eq32 } from "drizzle-orm";
16826
+ import { count as count4, eq as eq33 } from "drizzle-orm";
16706
16827
  async function countByOrigin(orm, origin) {
16707
- const [{ cnt }] = await orm.select({ cnt: count4() }).from(items).where(eq32(items.origin, origin));
16828
+ const [{ cnt }] = await orm.select({ cnt: count4() }).from(items).where(eq33(items.origin, origin));
16708
16829
  return cnt;
16709
16830
  }
16710
16831
 
@@ -16747,7 +16868,7 @@ async function moveRepo(oldOriginRaw, newOriginRaw, options2 = {}) {
16747
16868
  console.log(chalk66.yellow("Move cancelled; no changes made."));
16748
16869
  return;
16749
16870
  }
16750
- await orm.update(items).set({ origin: newOrigin }).where(eq33(items.origin, oldOrigin));
16871
+ await orm.update(items).set({ origin: newOrigin }).where(eq34(items.origin, oldOrigin));
16751
16872
  console.log(
16752
16873
  chalk66.green(
16753
16874
  `Moved ${pluralItems(cnt)} from "${oldOrigin}" to "${newOrigin}".`
@@ -16911,9 +17032,9 @@ function processStartedAt(pid) {
16911
17032
 
16912
17033
  // src/commands/backlog/isStaleLock.ts
16913
17034
  var clockSlackMs = 5e3;
16914
- function isStaleLock(pid, timestamp7) {
16915
- if (typeof timestamp7 !== "string") return false;
16916
- const lockedAt = Date.parse(timestamp7);
17035
+ function isStaleLock(pid, timestamp8) {
17036
+ if (typeof timestamp8 !== "string") return false;
17037
+ const lockedAt = Date.parse(timestamp8);
16917
17038
  if (Number.isNaN(lockedAt)) return false;
16918
17039
  if (lockedAt < bootedAt() - clockSlackMs) return true;
16919
17040
  const startedAt = processStartedAt(pid);
@@ -17219,12 +17340,12 @@ function message(error) {
17219
17340
  import chalk70 from "chalk";
17220
17341
 
17221
17342
  // src/shared/db/findPhaseBySessionId.ts
17222
- import { and as and11, eq as eq34 } from "drizzle-orm";
17343
+ import { and as and11, eq as eq35 } from "drizzle-orm";
17223
17344
  async function findPhaseBySessionId(db, itemId2, claudeSessionId) {
17224
17345
  const rows = await db.select({ phaseIdx: phaseUsage.phaseIdx }).from(phaseUsage).where(
17225
17346
  and11(
17226
- eq34(phaseUsage.itemId, itemId2),
17227
- eq34(phaseUsage.claudeSessionId, claudeSessionId)
17347
+ eq35(phaseUsage.itemId, itemId2),
17348
+ eq35(phaseUsage.claudeSessionId, claudeSessionId)
17228
17349
  )
17229
17350
  ).limit(1);
17230
17351
  return rows[0]?.phaseIdx;
@@ -17834,7 +17955,7 @@ import { randomUUID as randomUUID6 } from "crypto";
17834
17955
  import os4 from "os";
17835
17956
 
17836
17957
  // src/shared/db/recordPhaseSession.ts
17837
- import { sql as sql22 } from "drizzle-orm";
17958
+ import { sql as sql23 } from "drizzle-orm";
17838
17959
  async function recordPhaseSession(db, itemId2, phaseIdx, claudeSessionId, hostname2, osUser) {
17839
17960
  await db.insert(phaseSessions).values({ itemId: itemId2, phaseIdx, claudeSessionId, hostname: hostname2, osUser }).onConflictDoUpdate({
17840
17961
  target: [
@@ -17843,7 +17964,7 @@ async function recordPhaseSession(db, itemId2, phaseIdx, claudeSessionId, hostna
17843
17964
  phaseSessions.claudeSessionId
17844
17965
  ],
17845
17966
  set: { hostname: hostname2, osUser },
17846
- setWhere: sql22`${phaseSessions.hostname} = 'unknown'`
17967
+ setWhere: sql23`${phaseSessions.hostname} = 'unknown'`
17847
17968
  });
17848
17969
  }
17849
17970
 
@@ -18704,10 +18825,10 @@ async function start(id) {
18704
18825
 
18705
18826
  // src/commands/backlog/stop.ts
18706
18827
  import chalk93 from "chalk";
18707
- import { and as and12, eq as eq35 } from "drizzle-orm";
18828
+ import { and as and12, eq as eq36 } from "drizzle-orm";
18708
18829
  async function stop() {
18709
18830
  const { orm } = await getReady();
18710
- const stopped = await orm.update(items).set({ status: "todo", currentPhase: 1 }).where(and12(eq35(items.status, "in-progress"), eq35(items.origin, getOrigin()))).returning({ id: items.id, name: items.name });
18831
+ const stopped = await orm.update(items).set({ status: "todo", currentPhase: 1 }).where(and12(eq36(items.status, "in-progress"), eq36(items.origin, getOrigin()))).returning({ id: items.id, name: items.name });
18711
18832
  if (stopped.length === 0) {
18712
18833
  console.log(chalk93.yellow("No in-progress items to stop."));
18713
18834
  return;
@@ -18765,20 +18886,20 @@ function registerStatusCommands(cmd) {
18765
18886
 
18766
18887
  // src/commands/backlog/movePhase.ts
18767
18888
  import chalk96 from "chalk";
18768
- import { count as count6, eq as eq38 } from "drizzle-orm";
18889
+ import { count as count6, eq as eq39 } from "drizzle-orm";
18769
18890
 
18770
18891
  // src/commands/backlog/reorderPhaseRows.ts
18771
- import { and as and14, asc as asc10, eq as eq37 } from "drizzle-orm";
18892
+ import { and as and14, asc as asc10, eq as eq38 } from "drizzle-orm";
18772
18893
 
18773
18894
  // src/commands/backlog/reindexPhases.ts
18774
- import { and as and13, asc as asc9, count as count5, eq as eq36 } from "drizzle-orm";
18895
+ import { and as and13, asc as asc9, count as count5, eq as eq37 } from "drizzle-orm";
18775
18896
  async function reindexPhases(db, itemId2) {
18776
- const remaining = await db.select({ idx: planPhases.idx }).from(planPhases).where(eq36(planPhases.itemId, itemId2)).orderBy(asc9(planPhases.idx));
18897
+ const remaining = await db.select({ idx: planPhases.idx }).from(planPhases).where(eq37(planPhases.itemId, itemId2)).orderBy(asc9(planPhases.idx));
18777
18898
  for (let i = 0; i < remaining.length; i++) {
18778
18899
  const oldIdx = remaining[i].idx;
18779
18900
  if (oldIdx === i) continue;
18780
- await db.update(planTasks).set({ phaseIdx: i }).where(and13(eq36(planTasks.itemId, itemId2), eq36(planTasks.phaseIdx, oldIdx)));
18781
- await db.update(planPhases).set({ idx: i }).where(and13(eq36(planPhases.itemId, itemId2), eq36(planPhases.idx, oldIdx)));
18901
+ await db.update(planTasks).set({ phaseIdx: i }).where(and13(eq37(planTasks.itemId, itemId2), eq37(planTasks.phaseIdx, oldIdx)));
18902
+ await db.update(planPhases).set({ idx: i }).where(and13(eq37(planPhases.itemId, itemId2), eq37(planPhases.idx, oldIdx)));
18782
18903
  }
18783
18904
  }
18784
18905
  async function adjustCurrentPhase(db, item, removedIdx) {
@@ -18786,24 +18907,24 @@ async function adjustCurrentPhase(db, item, removedIdx) {
18786
18907
  if (currentPhase === void 0) return;
18787
18908
  const currentIdx = currentPhase - 1;
18788
18909
  if (removedIdx < currentIdx) {
18789
- await db.update(items).set({ currentPhase: currentPhase - 1 }).where(eq36(items.id, item.id));
18910
+ await db.update(items).set({ currentPhase: currentPhase - 1 }).where(eq37(items.id, item.id));
18790
18911
  return;
18791
18912
  }
18792
18913
  if (removedIdx !== currentIdx) return;
18793
- const [row3] = await db.select({ cnt: count5() }).from(planPhases).where(eq36(planPhases.itemId, item.id));
18914
+ const [row3] = await db.select({ cnt: count5() }).from(planPhases).where(eq37(planPhases.itemId, item.id));
18794
18915
  const cnt = row3?.cnt ?? 0;
18795
- await db.update(items).set({ currentPhase: cnt === 0 ? null : Math.min(currentPhase, cnt) }).where(eq36(items.id, item.id));
18916
+ await db.update(items).set({ currentPhase: cnt === 0 ? null : Math.min(currentPhase, cnt) }).where(eq37(items.id, item.id));
18796
18917
  }
18797
18918
 
18798
18919
  // src/commands/backlog/reorderPhaseRows.ts
18799
18920
  async function reorderPhaseRows(orm, itemId2, fromIdx, toIdx) {
18800
18921
  await orm.transaction(async (tx) => {
18801
- const [phaseRow] = await tx.select({ name: planPhases.name, manualChecks: planPhases.manualChecks }).from(planPhases).where(and14(eq37(planPhases.itemId, itemId2), eq37(planPhases.idx, fromIdx)));
18802
- const tasks = await tx.select({ idx: planTasks.idx, task: planTasks.task }).from(planTasks).where(and14(eq37(planTasks.itemId, itemId2), eq37(planTasks.phaseIdx, fromIdx))).orderBy(asc10(planTasks.idx));
18922
+ const [phaseRow] = await tx.select({ name: planPhases.name, manualChecks: planPhases.manualChecks }).from(planPhases).where(and14(eq38(planPhases.itemId, itemId2), eq38(planPhases.idx, fromIdx)));
18923
+ const tasks = await tx.select({ idx: planTasks.idx, task: planTasks.task }).from(planTasks).where(and14(eq38(planTasks.itemId, itemId2), eq38(planTasks.phaseIdx, fromIdx))).orderBy(asc10(planTasks.idx));
18803
18924
  await tx.delete(planTasks).where(
18804
- and14(eq37(planTasks.itemId, itemId2), eq37(planTasks.phaseIdx, fromIdx))
18925
+ and14(eq38(planTasks.itemId, itemId2), eq38(planTasks.phaseIdx, fromIdx))
18805
18926
  );
18806
- await tx.delete(planPhases).where(and14(eq37(planPhases.itemId, itemId2), eq37(planPhases.idx, fromIdx)));
18927
+ await tx.delete(planPhases).where(and14(eq38(planPhases.itemId, itemId2), eq38(planPhases.idx, fromIdx)));
18807
18928
  await reindexPhases(tx, itemId2);
18808
18929
  await shiftPhasesUp(tx, itemId2, toIdx);
18809
18930
  await tx.insert(planPhases).values({
@@ -18844,7 +18965,7 @@ async function movePhase(id, from, to) {
18844
18965
  if (!found) return;
18845
18966
  const { orm, item } = found;
18846
18967
  const itemId2 = item.id;
18847
- const [row3] = await orm.select({ cnt: count6() }).from(planPhases).where(eq38(planPhases.itemId, itemId2));
18968
+ const [row3] = await orm.select({ cnt: count6() }).from(planPhases).where(eq39(planPhases.itemId, itemId2));
18848
18969
  const phaseCount = row3?.cnt ?? 0;
18849
18970
  const fromIdx = toIndex2(from, phaseCount);
18850
18971
  if (fromIdx === void 0) return;
@@ -18860,22 +18981,22 @@ async function movePhase(id, from, to) {
18860
18981
  import chalk98 from "chalk";
18861
18982
 
18862
18983
  // src/commands/backlog/applyPhaseUpdate.ts
18863
- import { and as and15, eq as eq39 } from "drizzle-orm";
18984
+ import { and as and15, eq as eq40 } from "drizzle-orm";
18864
18985
  async function applyPhaseUpdate(orm, itemId2, phaseIdx, fields) {
18865
18986
  await orm.transaction(async (tx) => {
18866
18987
  if (fields.name) {
18867
18988
  await tx.update(planPhases).set({ name: fields.name }).where(
18868
- and15(eq39(planPhases.itemId, itemId2), eq39(planPhases.idx, phaseIdx))
18989
+ and15(eq40(planPhases.itemId, itemId2), eq40(planPhases.idx, phaseIdx))
18869
18990
  );
18870
18991
  }
18871
18992
  if (fields.manualCheck) {
18872
18993
  await tx.update(planPhases).set({ manualChecks: JSON.stringify(fields.manualCheck) }).where(
18873
- and15(eq39(planPhases.itemId, itemId2), eq39(planPhases.idx, phaseIdx))
18994
+ and15(eq40(planPhases.itemId, itemId2), eq40(planPhases.idx, phaseIdx))
18874
18995
  );
18875
18996
  }
18876
18997
  if (fields.task) {
18877
18998
  await tx.delete(planTasks).where(
18878
- and15(eq39(planTasks.itemId, itemId2), eq39(planTasks.phaseIdx, phaseIdx))
18999
+ and15(eq40(planTasks.itemId, itemId2), eq40(planTasks.phaseIdx, phaseIdx))
18879
19000
  );
18880
19001
  if (fields.task.length) {
18881
19002
  await tx.insert(planTasks).values(
@@ -18893,14 +19014,14 @@ async function applyPhaseUpdate(orm, itemId2, phaseIdx, fields) {
18893
19014
 
18894
19015
  // src/commands/backlog/findPhase.ts
18895
19016
  import chalk97 from "chalk";
18896
- import { and as and16, count as count7, eq as eq40 } from "drizzle-orm";
19017
+ import { and as and16, count as count7, eq as eq41 } from "drizzle-orm";
18897
19018
  async function findPhase(id, phase) {
18898
19019
  const found = await findOneItem(id);
18899
19020
  if (!found) return void 0;
18900
19021
  const { orm, item } = found;
18901
19022
  const itemId2 = item.id;
18902
19023
  const phaseIdx = Number.parseInt(phase, 10) - 1;
18903
- const [row3] = await orm.select({ cnt: count7() }).from(planPhases).where(and16(eq40(planPhases.itemId, itemId2), eq40(planPhases.idx, phaseIdx)));
19024
+ const [row3] = await orm.select({ cnt: count7() }).from(planPhases).where(and16(eq41(planPhases.itemId, itemId2), eq41(planPhases.idx, phaseIdx)));
18904
19025
  if (!row3 || row3.cnt === 0) {
18905
19026
  console.log(
18906
19027
  chalk97.red(
@@ -19073,16 +19194,16 @@ function registerUpdatePhaseCommand(cmd) {
19073
19194
 
19074
19195
  // src/commands/backlog/removePhase.ts
19075
19196
  import chalk99 from "chalk";
19076
- import { and as and17, eq as eq41 } from "drizzle-orm";
19197
+ import { and as and17, eq as eq42 } from "drizzle-orm";
19077
19198
  async function removePhase(id, phase) {
19078
19199
  const found = await findPhase(id, phase);
19079
19200
  if (!found) return;
19080
19201
  const { item, orm, itemId: itemId2, phaseIdx } = found;
19081
19202
  await orm.transaction(async (tx) => {
19082
19203
  await tx.delete(planTasks).where(
19083
- and17(eq41(planTasks.itemId, itemId2), eq41(planTasks.phaseIdx, phaseIdx))
19204
+ and17(eq42(planTasks.itemId, itemId2), eq42(planTasks.phaseIdx, phaseIdx))
19084
19205
  );
19085
- await tx.delete(planPhases).where(and17(eq41(planPhases.itemId, itemId2), eq41(planPhases.idx, phaseIdx)));
19206
+ await tx.delete(planPhases).where(and17(eq42(planPhases.itemId, itemId2), eq42(planPhases.idx, phaseIdx)));
19086
19207
  await reindexPhases(tx, itemId2);
19087
19208
  await adjustCurrentPhase(tx, item, phaseIdx);
19088
19209
  });
@@ -19095,7 +19216,7 @@ async function removePhase(id, phase) {
19095
19216
 
19096
19217
  // src/commands/backlog/update/update.ts
19097
19218
  import chalk103 from "chalk";
19098
- import { eq as eq42 } from "drizzle-orm";
19219
+ import { eq as eq43 } from "drizzle-orm";
19099
19220
 
19100
19221
  // src/commands/backlog/update/buildUpdateValues.ts
19101
19222
  import chalk100 from "chalk";
@@ -19206,7 +19327,7 @@ async function update(id, options2) {
19206
19327
  if (!built) return;
19207
19328
  const { orm } = found;
19208
19329
  const itemId2 = found.item.id;
19209
- await orm.update(items).set(built.set).where(eq42(items.id, itemId2));
19330
+ await orm.update(items).set(built.set).where(eq43(items.id, itemId2));
19210
19331
  console.log(
19211
19332
  chalk103.green(`Updated ${built.fields} on item ${formatItemId(itemId2)}.`)
19212
19333
  );
@@ -19252,7 +19373,7 @@ function readPlanUpdate(source) {
19252
19373
  }
19253
19374
 
19254
19375
  // src/commands/backlog/updatePlan/replacePlan.ts
19255
- import { eq as eq43 } from "drizzle-orm";
19376
+ import { eq as eq44 } from "drizzle-orm";
19256
19377
 
19257
19378
  // src/commands/backlog/updatePlan/clampCurrentPhase.ts
19258
19379
  function clampCurrentPhase(currentPhase, phaseCount) {
@@ -19263,8 +19384,8 @@ function clampCurrentPhase(currentPhase, phaseCount) {
19263
19384
  // src/commands/backlog/updatePlan/replacePlan.ts
19264
19385
  async function replacePlan(orm, itemId2, phases, currentPhase) {
19265
19386
  await orm.transaction(async (tx) => {
19266
- await tx.delete(planTasks).where(eq43(planTasks.itemId, itemId2));
19267
- await tx.delete(planPhases).where(eq43(planPhases.itemId, itemId2));
19387
+ await tx.delete(planTasks).where(eq44(planTasks.itemId, itemId2));
19388
+ await tx.delete(planPhases).where(eq44(planPhases.itemId, itemId2));
19268
19389
  if (phases.length > 0) {
19269
19390
  await tx.insert(planPhases).values(
19270
19391
  phases.map((phase, idx) => ({
@@ -19282,7 +19403,7 @@ async function replacePlan(orm, itemId2, phases, currentPhase) {
19282
19403
  if (currentPhase === void 0) return;
19283
19404
  const clamped = clampCurrentPhase(currentPhase, phases.length);
19284
19405
  if (clamped === currentPhase) return;
19285
- await tx.update(items).set({ currentPhase: clamped }).where(eq43(items.id, itemId2));
19406
+ await tx.update(items).set({ currentPhase: clamped }).where(eq44(items.id, itemId2));
19286
19407
  });
19287
19408
  }
19288
19409
 
@@ -23018,8 +23139,8 @@ async function setDbBackedConfigKey(key, value, useRepo) {
23018
23139
 
23019
23140
  // src/commands/config/setFileConfigKey.ts
23020
23141
  import chalk134 from "chalk";
23021
- async function setFileConfigKey({ key, useRepo, repoName, global }, coerced) {
23022
- const target = useRepo ? `repo: ${await applyRepoOrExit(key, coerced, repoName)}` : applyOrExit(key, coerced, global);
23142
+ async function setFileConfigKey({ key, useRepo, repoName, global, user }, coerced) {
23143
+ const target = useRepo ? `repo: ${await applyRepoOrExit(key, coerced, repoName)}` : user ? await applyUserOrExit(key, coerced) : applyOrExit(key, coerced, global);
23023
23144
  const shown = JSON.stringify(maskConfigKeySecrets(key, coerced));
23024
23145
  console.log(chalk134.green(`Set ${key} = ${shown} (${target})`));
23025
23146
  }
@@ -23028,20 +23149,30 @@ function applyOrExit(key, coerced, global) {
23028
23149
  if (!result.ok) exitWithConfigErrors(result.errors);
23029
23150
  return result.target;
23030
23151
  }
23152
+ async function applyUserOrExit(key, coerced) {
23153
+ const result = await applyUserConfigSet(key, coerced);
23154
+ if (!result.ok) exitWithConfigErrors(result.errors);
23155
+ return "user (db)";
23156
+ }
23031
23157
  async function applyRepoOrExit(key, coerced, repoName) {
23032
23158
  const result = await applySharedRepoConfigSet(key, coerced, repoName);
23033
23159
  if (!result.ok) exitWithConfigErrors(result.errors);
23034
23160
  return result.label;
23035
23161
  }
23036
23162
 
23163
+ // src/commands/config/scopeOptionsError.ts
23164
+ function scopeOptionsError(options2, repoAction) {
23165
+ if (options2.user && (options2.global || options2.repo !== void 0))
23166
+ return "--user writes user (db); drop -g and --repo";
23167
+ if (options2.repo !== void 0 && !options2.global)
23168
+ return `--repo ${repoAction} the global config; add -g (e.g. -g --repo)`;
23169
+ return void 0;
23170
+ }
23171
+
23037
23172
  // src/commands/config/configSet.ts
23038
23173
  async function configSet(key, value, options2 = {}) {
23039
- if (options2.repo !== void 0 && !options2.global) {
23040
- console.error(
23041
- chalk135.red("--repo writes to the global config; add -g (e.g. -g --repo)")
23042
- );
23043
- process.exit(1);
23044
- }
23174
+ const scopeError = scopeOptionsError(options2, "writes to");
23175
+ if (scopeError) exitWithConfigErrors([scopeError]);
23045
23176
  const resolved = resolveRepoTarget(key, value, options2.repo);
23046
23177
  if (resolved.value === void 0) {
23047
23178
  console.error(chalk135.red(`Missing required argument for '${resolved.key}'`));
@@ -23052,7 +23183,11 @@ async function configSet(key, value, options2 = {}) {
23052
23183
  if (isDbBackedConfigKey(resolved.key))
23053
23184
  return setDbBackedConfigKey(resolved.key, coercion.value, resolved.useRepo);
23054
23185
  await setFileConfigKey(
23055
- { ...resolved, global: options2.global ?? false },
23186
+ {
23187
+ ...resolved,
23188
+ global: options2.global ?? false,
23189
+ user: options2.user ?? false
23190
+ },
23056
23191
  coercion.value
23057
23192
  );
23058
23193
  }
@@ -23086,9 +23221,10 @@ async function unsetFileConfigKey({
23086
23221
  key,
23087
23222
  useRepo,
23088
23223
  repoName,
23089
- global
23224
+ global,
23225
+ user
23090
23226
  }) {
23091
- const result = useRepo ? await applySharedRepoConfigUnset(key, repoName) : applyConfigUnset(key, global);
23227
+ const result = useRepo ? await applySharedRepoConfigUnset(key, repoName) : user ? await applyUserConfigUnset(key) : applyConfigUnset(key, global);
23092
23228
  if (!result.ok) exitWithConfigErrors(result.errors);
23093
23229
  if (!result.removed) {
23094
23230
  console.log(chalk137.yellow(`${key} is not set in ${whereLabel(result)}`));
@@ -23097,22 +23233,20 @@ async function unsetFileConfigKey({
23097
23233
  console.log(chalk137.green(`Unset ${key} (${targetLabel(result)})`));
23098
23234
  }
23099
23235
  function whereLabel(result) {
23100
- return result.target === "repo" ? `repos.${result.label}` : `the ${result.target} config`;
23236
+ if (result.target === "repo") return `repos.${result.label}`;
23237
+ if (result.target === "user") return "user (db)";
23238
+ return `the ${result.target} config`;
23101
23239
  }
23102
23240
  function targetLabel(result) {
23103
- return result.target === "repo" ? `repo: ${result.label}` : result.target;
23241
+ if (result.target === "repo") return `repo: ${result.label}`;
23242
+ if (result.target === "user") return "user (db)";
23243
+ return result.target;
23104
23244
  }
23105
23245
 
23106
23246
  // src/commands/config/configUnset.ts
23107
23247
  async function configUnset(key, options2 = {}) {
23108
- if (options2.repo !== void 0 && !options2.global) {
23109
- console.error(
23110
- chalk138.red(
23111
- "--repo removes from the global config; add -g (e.g. -g --repo)"
23112
- )
23113
- );
23114
- process.exit(1);
23115
- }
23248
+ const scopeError = scopeOptionsError(options2, "removes from");
23249
+ if (scopeError) exitWithConfigErrors([scopeError]);
23116
23250
  const resolved = resolveRepoUnsetTarget(key, options2.repo);
23117
23251
  if (resolved.key === void 0) {
23118
23252
  console.error(chalk138.red("Missing required argument 'key'"));
@@ -23125,25 +23259,34 @@ async function configUnset(key, options2 = {}) {
23125
23259
  key: resolved.key,
23126
23260
  useRepo: resolved.useRepo,
23127
23261
  repoName: resolved.repoName,
23128
- global: options2.global ?? false
23262
+ global: options2.global ?? false,
23263
+ user: options2.user ?? false
23129
23264
  });
23130
23265
  }
23131
23266
 
23132
- // src/commands/registerConfig.ts
23133
- function registerConfig(program) {
23134
- const configCommand = configHelp(
23135
- program.command("config").description("View and modify assist.yml configuration"),
23136
- configConfigHelp,
23137
- "Run 'assist config keys [filter]' to list every config key with its default,\nwhat it does, and the command that sets it."
23138
- );
23267
+ // src/commands/config/registerConfigWrites.ts
23268
+ function registerConfigWrites(configCommand) {
23139
23269
  configCommand.command("set <key> [value]").description("Set a config value (e.g. commit.push true)").option("-g, --global", "Write to global ~/.assist.yml").option(
23140
23270
  "-r, --repo [name]",
23141
23271
  "Requires -g: write a repo's override to the shared db, seen by every node (defaults to the current repo)"
23272
+ ).option(
23273
+ "-u, --user",
23274
+ "Write to user (db): the shared db, applied to every repo on every node"
23142
23275
  ).action((key, value, options2) => configSet(key, value, options2));
23143
23276
  configCommand.command("unset [key]").description("Remove a config value (e.g. commit.push)").option("-g, --global", "Remove from global ~/.assist.yml").option(
23144
23277
  "-r, --repo [name]",
23145
23278
  "Requires -g: remove the key from a repo's shared db override (defaults to the current repo)"
23146
- ).action((key, options2) => configUnset(key, options2));
23279
+ ).option("-u, --user", "Remove from user (db) in the shared db").action((key, options2) => configUnset(key, options2));
23280
+ }
23281
+
23282
+ // src/commands/registerConfig.ts
23283
+ function registerConfig(program) {
23284
+ const configCommand = configHelp(
23285
+ program.command("config").description("View and modify assist.yml configuration"),
23286
+ configConfigHelp,
23287
+ "Run 'assist config keys [filter]' to list every config key with its default,\nwhat it does, and the command that sets it."
23288
+ );
23289
+ registerConfigWrites(configCommand);
23147
23290
  configCommand.command("import-repos").description(
23148
23291
  "Move this machine's ~/.assist.yml repos: overrides into the shared db, showing a per-key diff and asking first"
23149
23292
  ).action(configImportRepos);
@@ -23246,8 +23389,8 @@ import { homedir as homedir21 } from "os";
23246
23389
  import { join as join54 } from "path";
23247
23390
  function snapshotPath(tabUrl, now) {
23248
23391
  const host = new URL(tabUrl).host.replace(/[^\w.-]/g, "-");
23249
- const timestamp7 = now.toISOString().replace(/[:.]/g, "-");
23250
- return join54(homedir21(), ".assist", "browser", `${host}-${timestamp7}.html`);
23392
+ const timestamp8 = now.toISOString().replace(/[:.]/g, "-");
23393
+ return join54(homedir21(), ".assist", "browser", `${host}-${timestamp8}.html`);
23251
23394
  }
23252
23395
 
23253
23396
  // src/commands/browser/browserSnapshot.ts
@@ -27746,10 +27889,10 @@ async function list3() {
27746
27889
  import chalk173 from "chalk";
27747
27890
 
27748
27891
  // src/shared/db/readInboxLastScan.ts
27749
- import { eq as eq44 } from "drizzle-orm";
27892
+ import { eq as eq45 } from "drizzle-orm";
27750
27893
  var INBOX_LAST_SCAN_KEY = "inbox.lastScanAt";
27751
27894
  async function readInboxLastScan(orm) {
27752
- const [row3] = await orm.select().from(metadata).where(eq44(metadata.key, INBOX_LAST_SCAN_KEY));
27895
+ const [row3] = await orm.select().from(metadata).where(eq45(metadata.key, INBOX_LAST_SCAN_KEY));
27753
27896
  return row3 ? new Date(row3.value) : void 0;
27754
27897
  }
27755
27898
 
@@ -30486,9 +30629,9 @@ async function list4() {
30486
30629
  import chalk189 from "chalk";
30487
30630
 
30488
30631
  // src/commands/backlog/removeFeed.ts
30489
- import { eq as eq45 } from "drizzle-orm";
30632
+ import { eq as eq46 } from "drizzle-orm";
30490
30633
  async function removeFeed(db, url) {
30491
- const deleted = await db.delete(feeds).where(eq45(feeds.url, url)).returning({ id: feeds.id });
30634
+ const deleted = await db.delete(feeds).where(eq46(feeds.url, url)).returning({ id: feeds.id });
30492
30635
  return deleted.length > 0;
30493
30636
  }
30494
30637
 
@@ -32601,8 +32744,8 @@ function bucketPr(pr) {
32601
32744
  }
32602
32745
 
32603
32746
  // src/commands/prs/status/describeAge.ts
32604
- function describeAge(timestamp7, now = Date.now()) {
32605
- const parsed = new Date(timestamp7).getTime();
32747
+ function describeAge(timestamp8, now = Date.now()) {
32748
+ const parsed = new Date(timestamp8).getTime();
32606
32749
  if (Number.isNaN(parsed)) return { hours: null, label: "unknown" };
32607
32750
  const hours = Math.max(0, Math.floor((now - parsed) / 36e5));
32608
32751
  if (hours < 1) return { hours, label: "<1h" };
@@ -34173,8 +34316,8 @@ function buildOutputPath(outputDir, processName) {
34173
34316
  if (!existsSync65(outputDir)) {
34174
34317
  mkdirSync24(outputDir, { recursive: true });
34175
34318
  }
34176
- const timestamp7 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
34177
- return resolve24(outputDir, `${processName}-${timestamp7}.png`);
34319
+ const timestamp8 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
34320
+ return resolve24(outputDir, `${processName}-${timestamp8}.png`);
34178
34321
  }
34179
34322
  function runPowerShellScript(processName, outputPath) {
34180
34323
  const scriptPath = join83(tmpdir7(), `assist-screenshot-${Date.now()}.ps1`);
@@ -41132,8 +41275,8 @@ function filterToSql(filter) {
41132
41275
  // src/commands/seq/fetchSeqData.ts
41133
41276
  function buildDataParams(filter, count10, from, to) {
41134
41277
  const sqlFilter = filterToSql(filter);
41135
- const sql29 = `select @Timestamp, @Level, @Exception, @Message from stream where ${sqlFilter} order by @Timestamp desc limit ${count10}`;
41136
- const params = new URLSearchParams({ q: sql29 });
41278
+ const sql30 = `select @Timestamp, @Level, @Exception, @Message from stream where ${sqlFilter} order by @Timestamp desc limit ${count10}`;
41279
+ const params = new URLSearchParams({ q: sql30 });
41137
41280
  if (from) params.set("rangeStartUtc", from);
41138
41281
  if (to) params.set("rangeEndUtc", to);
41139
41282
  return params;
@@ -41647,26 +41790,26 @@ import chalk259 from "chalk";
41647
41790
  // src/commands/sql/loadConnections.ts
41648
41791
  function loadConnections3() {
41649
41792
  const raw = loadGlobalConfigRaw();
41650
- const sql29 = raw.sql;
41651
- return sql29?.connections ?? [];
41793
+ const sql30 = raw.sql;
41794
+ return sql30?.connections ?? [];
41652
41795
  }
41653
41796
  function saveConnections3(connections) {
41654
41797
  const raw = loadGlobalConfigRaw();
41655
- const sql29 = raw.sql ?? {};
41656
- sql29.connections = connections;
41657
- raw.sql = sql29;
41798
+ const sql30 = raw.sql ?? {};
41799
+ sql30.connections = connections;
41800
+ raw.sql = sql30;
41658
41801
  saveGlobalConfig(raw);
41659
41802
  }
41660
41803
  function getDefaultConnection2() {
41661
41804
  const raw = loadGlobalConfigRaw();
41662
- const sql29 = raw.sql;
41663
- return sql29?.defaultConnection;
41805
+ const sql30 = raw.sql;
41806
+ return sql30?.defaultConnection;
41664
41807
  }
41665
41808
  function setDefaultConnection2(name) {
41666
41809
  const raw = loadGlobalConfigRaw();
41667
- const sql29 = raw.sql ?? {};
41668
- sql29.defaultConnection = name;
41669
- raw.sql = sql29;
41810
+ const sql30 = raw.sql ?? {};
41811
+ sql30.defaultConnection = name;
41812
+ raw.sql = sql30;
41670
41813
  saveGlobalConfig(raw);
41671
41814
  }
41672
41815
 
@@ -41737,9 +41880,9 @@ function resolveConnection3(name) {
41737
41880
  }
41738
41881
 
41739
41882
  // src/commands/sql/sqlConnect.ts
41740
- import sql23 from "mssql";
41883
+ import sql24 from "mssql";
41741
41884
  async function sqlConnect(conn) {
41742
- return await sql23.connect({
41885
+ return await sql24.connect({
41743
41886
  server: conn.server,
41744
41887
  port: conn.port,
41745
41888
  user: conn.user,
@@ -41802,11 +41945,11 @@ var MUTATION_PATTERN = new RegExp(
41802
41945
  `\\b(${MUTATION_KEYWORDS.join("|")})\\b`,
41803
41946
  "i"
41804
41947
  );
41805
- function stripComments(sql29) {
41806
- return sql29.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/--[^\n]*/g, " ");
41948
+ function stripComments(sql30) {
41949
+ return sql30.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/--[^\n]*/g, " ");
41807
41950
  }
41808
- function isMutation(sql29) {
41809
- const stripped = stripComments(sql29);
41951
+ function isMutation(sql30) {
41952
+ const stripped = stripComments(sql30);
41810
41953
  if (MUTATION_PATTERN.test(stripped)) return true;
41811
41954
  return /\bSELECT\b[\s\S]+\bINTO\s+\w/i.test(stripped);
41812
41955
  }
@@ -44830,12 +44973,12 @@ function parseEnvPairs(pairs) {
44830
44973
  if (pairs.length === 0) return void 0;
44831
44974
  const env = {};
44832
44975
  for (const pair of pairs) {
44833
- const eq48 = pair.indexOf("=");
44834
- if (eq48 <= 0) {
44976
+ const eq49 = pair.indexOf("=");
44977
+ if (eq49 <= 0) {
44835
44978
  console.error(`--env must be KEY=VALUE, got "${pair}"`);
44836
44979
  process.exit(1);
44837
44980
  }
44838
- env[pair.slice(0, eq48)] = pair.slice(eq48 + 1);
44981
+ env[pair.slice(0, eq49)] = pair.slice(eq49 + 1);
44839
44982
  }
44840
44983
  return env;
44841
44984
  }
@@ -44848,11 +44991,11 @@ function fail9(spec) {
44848
44991
  process.exit(1);
44849
44992
  }
44850
44993
  function parseParamSpec(spec) {
44851
- const eq48 = spec.indexOf("=");
44852
- const name = eq48 === -1 ? spec : spec.slice(0, eq48);
44853
- if (eq48 !== -1) {
44854
- if (eq48 === 0 || name.endsWith("!")) fail9(spec);
44855
- return { name, default: spec.slice(eq48 + 1) };
44994
+ const eq49 = spec.indexOf("=");
44995
+ const name = eq49 === -1 ? spec : spec.slice(0, eq49);
44996
+ if (eq49 !== -1) {
44997
+ if (eq49 === 0 || name.endsWith("!")) fail9(spec);
44998
+ return { name, default: spec.slice(eq49 + 1) };
44856
44999
  }
44857
45000
  if (name.endsWith("!")) {
44858
45001
  if (name.length === 1) fail9(spec);
@@ -45362,6 +45505,9 @@ function createAutoExit(exit, graceMs = DEFAULT_GRACE_MS) {
45362
45505
 
45363
45506
  // src/commands/sessions/daemon/pollRepoConfigCache.ts
45364
45507
  var POLL_MS = 6e4;
45508
+ function cacheSnapshot() {
45509
+ return JSON.stringify([readRepoConfigCache(), readUserConfigCache()]);
45510
+ }
45365
45511
  function pollRepoConfigCache(intervalMs = POLL_MS) {
45366
45512
  let inFlight2 = false;
45367
45513
  let failing = false;
@@ -45369,11 +45515,11 @@ function pollRepoConfigCache(intervalMs = POLL_MS) {
45369
45515
  if (inFlight2) return;
45370
45516
  inFlight2 = true;
45371
45517
  try {
45372
- const before = JSON.stringify(readRepoConfigCache());
45518
+ const before = cacheSnapshot();
45373
45519
  const count10 = await refreshRepoConfigCache();
45374
45520
  if (failing) daemonLog("repo config cache poll recovered");
45375
45521
  failing = false;
45376
- if (JSON.stringify(readRepoConfigCache()) !== before)
45522
+ if (cacheSnapshot() !== before)
45377
45523
  daemonLog(`repo config cache updated (${count10} repos)`);
45378
45524
  } catch (error) {
45379
45525
  if (!failing)
@@ -47509,13 +47655,13 @@ function drainSessions(sessions, notify2) {
47509
47655
  }
47510
47656
 
47511
47657
  // src/shared/db/recordPhaseActiveMs.ts
47512
- import { sql as sql24 } from "drizzle-orm";
47658
+ import { sql as sql25 } from "drizzle-orm";
47513
47659
  async function recordPhaseActiveMs(db, itemId2, phaseIdx, activeMs) {
47514
47660
  if (activeMs <= 0) return;
47515
47661
  await db.insert(phaseUsage).values({ itemId: itemId2, phaseIdx, activeMs }).onConflictDoUpdate({
47516
47662
  target: [phaseUsage.itemId, phaseUsage.phaseIdx],
47517
47663
  set: {
47518
- activeMs: sql24`${phaseUsage.activeMs} + excluded.active_ms`
47664
+ activeMs: sql25`${phaseUsage.activeMs} + excluded.active_ms`
47519
47665
  }
47520
47666
  });
47521
47667
  }
@@ -48116,10 +48262,10 @@ function applyReviewPause(session, activity2) {
48116
48262
  }
48117
48263
 
48118
48264
  // src/shared/db/getPhaseActiveMs.ts
48119
- import { and as and18, eq as eq46 } from "drizzle-orm";
48265
+ import { and as and18, eq as eq47 } from "drizzle-orm";
48120
48266
  async function getPhaseActiveMs(db, itemId2, phaseIdx) {
48121
48267
  const [row3] = await db.select({ activeMs: phaseUsage.activeMs }).from(phaseUsage).where(
48122
- and18(eq46(phaseUsage.itemId, itemId2), eq46(phaseUsage.phaseIdx, phaseIdx))
48268
+ and18(eq47(phaseUsage.itemId, itemId2), eq47(phaseUsage.phaseIdx, phaseIdx))
48123
48269
  );
48124
48270
  return row3?.activeMs ?? 0;
48125
48271
  }
@@ -48904,7 +49050,7 @@ async function readTranscriptUsage(transcriptPath2) {
48904
49050
  }
48905
49051
 
48906
49052
  // src/shared/db/recordPhaseCycleContext.ts
48907
- import { sql as sql25 } from "drizzle-orm";
49053
+ import { sql as sql26 } from "drizzle-orm";
48908
49054
  async function recordPhaseCycleContext(db, itemId2, phaseIdx, window, resetsAt, pct) {
48909
49055
  if (pct <= 0) return;
48910
49056
  await db.insert(phaseCycleContext).values({ itemId: itemId2, phaseIdx, window, resetsAt, peakContextPct: pct }).onConflictDoUpdate({
@@ -48915,24 +49061,24 @@ async function recordPhaseCycleContext(db, itemId2, phaseIdx, window, resetsAt,
48915
49061
  phaseCycleContext.resetsAt
48916
49062
  ],
48917
49063
  set: {
48918
- peakContextPct: sql25`GREATEST(${phaseCycleContext.peakContextPct}, ${pct})`
49064
+ peakContextPct: sql26`GREATEST(${phaseCycleContext.peakContextPct}, ${pct})`
48919
49065
  }
48920
49066
  });
48921
49067
  }
48922
49068
 
48923
49069
  // src/shared/db/recordPhasePeakContext.ts
48924
- import { sql as sql26 } from "drizzle-orm";
49070
+ import { sql as sql27 } from "drizzle-orm";
48925
49071
  async function recordPhasePeakContext(db, itemId2, phaseIdx, pct) {
48926
49072
  await db.insert(phaseUsage).values({ itemId: itemId2, phaseIdx, peakContextPct: pct }).onConflictDoUpdate({
48927
49073
  target: [phaseUsage.itemId, phaseUsage.phaseIdx],
48928
49074
  set: {
48929
- peakContextPct: sql26`GREATEST(${phaseUsage.peakContextPct}, ${pct})`
49075
+ peakContextPct: sql27`GREATEST(${phaseUsage.peakContextPct}, ${pct})`
48930
49076
  }
48931
49077
  });
48932
49078
  }
48933
49079
 
48934
49080
  // src/shared/db/recordPhaseTranscriptUsage.ts
48935
- import { sql as sql27 } from "drizzle-orm";
49081
+ import { sql as sql28 } from "drizzle-orm";
48936
49082
  async function recordPhaseTranscriptUsage(db, itemId2, phaseIdx, responses) {
48937
49083
  const byId = /* @__PURE__ */ new Map();
48938
49084
  for (const r of responses) byId.set(r.messageId, r);
@@ -48952,8 +49098,8 @@ async function recordPhaseTranscriptUsage(db, itemId2, phaseIdx, responses) {
48952
49098
  await tx.insert(phaseUsage).values({ itemId: itemId2, phaseIdx, tokensUp, tokensDown }).onConflictDoUpdate({
48953
49099
  target: [phaseUsage.itemId, phaseUsage.phaseIdx],
48954
49100
  set: {
48955
- tokensUp: sql27`${phaseUsage.tokensUp} + ${tokensUp}`,
48956
- tokensDown: sql27`${phaseUsage.tokensDown} + ${tokensDown}`
49101
+ tokensUp: sql28`${phaseUsage.tokensUp} + ${tokensUp}`,
49102
+ tokensDown: sql28`${phaseUsage.tokensDown} + ${tokensDown}`
48957
49103
  }
48958
49104
  });
48959
49105
  return { tokensUp, tokensDown };
@@ -48961,17 +49107,17 @@ async function recordPhaseTranscriptUsage(db, itemId2, phaseIdx, responses) {
48961
49107
  }
48962
49108
 
48963
49109
  // src/shared/db/recordWindowTokens.ts
48964
- import { and as and19, eq as eq47, sql as sql28 } from "drizzle-orm";
49110
+ import { and as and19, eq as eq48, sql as sql29 } from "drizzle-orm";
48965
49111
  async function recordWindowTokens(db, window, resetsAt, tokensUp, tokensDown) {
48966
49112
  if (tokensUp <= 0 && tokensDown <= 0) return;
48967
49113
  await db.update(usagePeaks).set({
48968
- tokensUp: sql28`${usagePeaks.tokensUp} + ${tokensUp}`,
48969
- tokensDown: sql28`${usagePeaks.tokensDown} + ${tokensDown}`
49114
+ tokensUp: sql29`${usagePeaks.tokensUp} + ${tokensUp}`,
49115
+ tokensDown: sql29`${usagePeaks.tokensDown} + ${tokensDown}`
48970
49116
  }).where(
48971
49117
  and19(
48972
- eq47(usagePeaks.window, window),
48973
- eq47(usagePeaks.resetsAt, resetsAt),
48974
- eq47(usagePeaks.segment, 0)
49118
+ eq48(usagePeaks.window, window),
49119
+ eq48(usagePeaks.resetsAt, resetsAt),
49120
+ eq48(usagePeaks.segment, 0)
48975
49121
  )
48976
49122
  );
48977
49123
  }