@staff0rd/assist 0.299.1 → 0.301.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.299.1",
9
+ version: "0.301.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -1074,8 +1074,8 @@ var options = [
1074
1074
  ];
1075
1075
 
1076
1076
  // src/commands/verify/init/getAvailableOptions/index.ts
1077
- function resolveDescription(desc4, setup2) {
1078
- return typeof desc4 === "function" ? desc4(setup2) : desc4;
1077
+ function resolveDescription(desc5, setup2) {
1078
+ return typeof desc5 === "function" ? desc5(setup2) : desc5;
1079
1079
  }
1080
1080
  function toVerifyOption(def, setup2) {
1081
1081
  return {
@@ -1665,8 +1665,8 @@ var MACHINE_DIRECTIVES = [
1665
1665
  "v8 ignore",
1666
1666
  "c8 ignore"
1667
1667
  ];
1668
- function isCommentExempt(text4, markers) {
1669
- const lower = text4.toLowerCase();
1668
+ function isCommentExempt(text5, markers) {
1669
+ const lower = text5.toLowerCase();
1670
1670
  if (MACHINE_DIRECTIVES.some((d) => lower.includes(d))) return true;
1671
1671
  return markers.some((m) => lower.includes(m.toLowerCase()));
1672
1672
  }
@@ -1709,8 +1709,8 @@ function parseDiffAddedLines(diff2) {
1709
1709
 
1710
1710
  // src/commands/verify/commentPolicy/findAddedComments.ts
1711
1711
  var SOURCE_EXTENSIONS = [".ts", ".tsx", ".cts", ".mts", ".js", ".jsx"];
1712
- function toSingleLine(text4) {
1713
- return text4.replace(/\s+/g, " ").trim();
1712
+ function toSingleLine(text5) {
1713
+ return text5.replace(/\s+/g, " ").trim();
1714
1714
  }
1715
1715
  function shouldScan(file, ignoreGlobs) {
1716
1716
  if (!SOURCE_EXTENSIONS.some((ext) => file.endsWith(ext))) return false;
@@ -1731,11 +1731,11 @@ function findAddedComments(options2) {
1731
1731
  for (const [file, lines] of addedLines) {
1732
1732
  if (!shouldScan(file, options2.ignoreGlobs)) continue;
1733
1733
  const sourceFile = project.addSourceFileAtPath(file);
1734
- for (const { pos, text: text4 } of collectComments(sourceFile)) {
1734
+ for (const { pos, text: text5 } of collectComments(sourceFile)) {
1735
1735
  const { line } = sourceFile.getLineAndColumnAtPos(pos);
1736
1736
  if (!lines.has(line)) continue;
1737
- if (isCommentExempt(text4, options2.markers)) continue;
1738
- findings.push({ file, line, text: toSingleLine(text4) });
1737
+ if (isCommentExempt(text5, options2.markers)) continue;
1738
+ findings.push({ file, line, text: toSingleLine(text5) });
1739
1739
  }
1740
1740
  }
1741
1741
  findings.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line);
@@ -1754,8 +1754,8 @@ function commentPolicy() {
1754
1754
  process.exit(0);
1755
1755
  }
1756
1756
  console.log("Comments added on changed lines:\n");
1757
- for (const { file, line, text: text4 } of findings) {
1758
- console.log(`${file}:${line} \u2192 ${text4}`);
1757
+ for (const { file, line, text: text5 } of findings) {
1758
+ console.log(`${file}:${line} \u2192 ${text5}`);
1759
1759
  }
1760
1760
  console.log(`
1761
1761
  Total: ${findings.length} comment(s)`);
@@ -2865,9 +2865,9 @@ import {
2865
2865
  foreignKey,
2866
2866
  index as index2,
2867
2867
  integer as integer2,
2868
- pgTable as pgTable2,
2869
- primaryKey,
2870
- text as text2
2868
+ pgTable as pgTable3,
2869
+ primaryKey as primaryKey2,
2870
+ text as text3
2871
2871
  } from "drizzle-orm/pg-core";
2872
2872
 
2873
2873
  // src/shared/db/handovers.ts
@@ -2885,73 +2885,91 @@ var handovers = pgTable(
2885
2885
  (t) => [index("handovers_origin_idx").on(t.origin)]
2886
2886
  );
2887
2887
 
2888
+ // src/shared/db/usagePeaks.ts
2889
+ import {
2890
+ bigint,
2891
+ doublePrecision,
2892
+ pgTable as pgTable2,
2893
+ primaryKey,
2894
+ text as text2
2895
+ } from "drizzle-orm/pg-core";
2896
+ var usagePeaks = pgTable2(
2897
+ "usage_peaks",
2898
+ {
2899
+ window: text2().$type().notNull(),
2900
+ resetsAt: bigint("resets_at", { mode: "number" }).notNull(),
2901
+ usedPercentage: doublePrecision("used_percentage").notNull()
2902
+ },
2903
+ (t) => [primaryKey({ columns: [t.window, t.resetsAt] })]
2904
+ );
2905
+
2888
2906
  // src/shared/db/schema.ts
2889
- var items = pgTable2(
2907
+ var items = pgTable3(
2890
2908
  "items",
2891
2909
  {
2892
2910
  id: integer2().generatedByDefaultAsIdentity().primaryKey(),
2893
- origin: text2().notNull(),
2894
- type: text2().notNull().default("story"),
2895
- name: text2().notNull(),
2896
- description: text2(),
2897
- acceptanceCriteria: text2("acceptance_criteria").notNull().default("[]"),
2898
- status: text2().notNull().default("todo"),
2911
+ origin: text3().notNull(),
2912
+ type: text3().notNull().default("story"),
2913
+ name: text3().notNull(),
2914
+ description: text3(),
2915
+ acceptanceCriteria: text3("acceptance_criteria").notNull().default("[]"),
2916
+ status: text3().notNull().default("todo"),
2899
2917
  currentPhase: integer2("current_phase"),
2900
2918
  starred: boolean().notNull().default(false)
2901
2919
  },
2902
2920
  (t) => [index2("items_origin_idx").on(t.origin)]
2903
2921
  );
2904
- var comments = pgTable2("comments", {
2922
+ var comments = pgTable3("comments", {
2905
2923
  id: integer2().generatedByDefaultAsIdentity().primaryKey(),
2906
2924
  itemId: integer2("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
2907
2925
  idx: integer2().notNull(),
2908
- text: text2().notNull(),
2926
+ text: text3().notNull(),
2909
2927
  phase: integer2(),
2910
- timestamp: text2().notNull(),
2911
- type: text2().notNull().default("comment")
2928
+ timestamp: text3().notNull(),
2929
+ type: text3().notNull().default("comment")
2912
2930
  });
2913
- var links = pgTable2(
2931
+ var links = pgTable3(
2914
2932
  "links",
2915
2933
  {
2916
2934
  itemId: integer2("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
2917
- type: text2().notNull(),
2935
+ type: text3().notNull(),
2918
2936
  targetId: integer2("target_id").notNull()
2919
2937
  },
2920
- (t) => [primaryKey({ columns: [t.itemId, t.type, t.targetId] })]
2938
+ (t) => [primaryKey2({ columns: [t.itemId, t.type, t.targetId] })]
2921
2939
  );
2922
- var planPhases = pgTable2(
2940
+ var planPhases = pgTable3(
2923
2941
  "plan_phases",
2924
2942
  {
2925
2943
  itemId: integer2("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
2926
2944
  idx: integer2().notNull(),
2927
- name: text2().notNull(),
2928
- manualChecks: text2("manual_checks")
2945
+ name: text3().notNull(),
2946
+ manualChecks: text3("manual_checks")
2929
2947
  },
2930
- (t) => [primaryKey({ columns: [t.itemId, t.idx] })]
2948
+ (t) => [primaryKey2({ columns: [t.itemId, t.idx] })]
2931
2949
  );
2932
- var planTasks = pgTable2(
2950
+ var planTasks = pgTable3(
2933
2951
  "plan_tasks",
2934
2952
  {
2935
2953
  itemId: integer2("item_id").notNull(),
2936
2954
  phaseIdx: integer2("phase_idx").notNull(),
2937
2955
  idx: integer2().notNull(),
2938
- task: text2().notNull()
2956
+ task: text3().notNull()
2939
2957
  },
2940
2958
  (t) => [
2941
- primaryKey({ columns: [t.itemId, t.phaseIdx, t.idx] }),
2959
+ primaryKey2({ columns: [t.itemId, t.phaseIdx, t.idx] }),
2942
2960
  foreignKey({
2943
2961
  columns: [t.itemId, t.phaseIdx],
2944
2962
  foreignColumns: [planPhases.itemId, planPhases.idx]
2945
2963
  }).onDelete("cascade")
2946
2964
  ]
2947
2965
  );
2948
- var metadata = pgTable2("metadata", {
2949
- key: text2().primaryKey(),
2950
- value: text2().notNull()
2966
+ var metadata = pgTable3("metadata", {
2967
+ key: text3().primaryKey(),
2968
+ value: text3().notNull()
2951
2969
  });
2952
- var feeds = pgTable2("feeds", {
2970
+ var feeds = pgTable3("feeds", {
2953
2971
  id: integer2().generatedByDefaultAsIdentity().primaryKey(),
2954
- url: text2().notNull().unique()
2972
+ url: text3().notNull().unique()
2955
2973
  });
2956
2974
  var schema = {
2957
2975
  items,
@@ -2961,7 +2979,8 @@ var schema = {
2961
2979
  planTasks,
2962
2980
  metadata,
2963
2981
  feeds,
2964
- handovers
2982
+ handovers,
2983
+ usagePeaks
2965
2984
  };
2966
2985
 
2967
2986
  // src/commands/backlog/itemColumns.ts
@@ -3402,6 +3421,13 @@ var SCHEMA = `
3402
3421
  );
3403
3422
 
3404
3423
  CREATE INDEX IF NOT EXISTS handovers_origin_idx ON handovers (origin);
3424
+
3425
+ CREATE TABLE IF NOT EXISTS usage_peaks (
3426
+ "window" TEXT NOT NULL,
3427
+ resets_at BIGINT NOT NULL,
3428
+ used_percentage DOUBLE PRECISION NOT NULL,
3429
+ PRIMARY KEY ("window", resets_at)
3430
+ );
3405
3431
  `;
3406
3432
  async function ensureSchema(exec3) {
3407
3433
  await exec3(SCHEMA);
@@ -3449,7 +3475,7 @@ function getDb() {
3449
3475
  );
3450
3476
  });
3451
3477
  _pool = pool;
3452
- await ensureSchema((sql5) => pool.query(sql5));
3478
+ await ensureSchema((sql6) => pool.query(sql6));
3453
3479
  _orm = makeOrmFromPool(pool);
3454
3480
  await seedNewsFeeds(_orm);
3455
3481
  return _orm;
@@ -4217,11 +4243,11 @@ import chalk37 from "chalk";
4217
4243
 
4218
4244
  // src/commands/backlog/appendComment.ts
4219
4245
  import { sql as sql2 } from "drizzle-orm";
4220
- async function appendComment(orm, itemId, text4, opts = {}) {
4246
+ async function appendComment(orm, itemId, text5, opts = {}) {
4221
4247
  await orm.insert(comments).values({
4222
4248
  itemId,
4223
4249
  idx: sql2`(SELECT COALESCE(MAX(${comments.idx}) + 1, 0) FROM ${comments} WHERE ${comments.itemId} = ${itemId})`,
4224
- text: text4,
4250
+ text: text5,
4225
4251
  phase: opts.phase ?? null,
4226
4252
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4227
4253
  type: opts.type ?? "comment"
@@ -5234,9 +5260,9 @@ function excerpt(xml, ...tags) {
5234
5260
  for (const tag of tags) {
5235
5261
  const raw = extractText(xml, tag);
5236
5262
  if (!raw) continue;
5237
- const text4 = stripHtml(raw);
5238
- if (text4.length <= MAX_EXCERPT) return text4;
5239
- return `${text4.slice(0, MAX_EXCERPT)}\u2026`;
5263
+ const text5 = stripHtml(raw);
5264
+ if (text5.length <= MAX_EXCERPT) return text5;
5265
+ return `${text5.slice(0, MAX_EXCERPT)}\u2026`;
5240
5266
  }
5241
5267
  return "";
5242
5268
  }
@@ -5313,6 +5339,17 @@ async function listNewsItems(_req, res) {
5313
5339
  respondJson(res, 200, cachedItems);
5314
5340
  }
5315
5341
 
5342
+ // src/shared/db/listUsagePeaks.ts
5343
+ import { desc } from "drizzle-orm";
5344
+ async function listUsagePeaks(db) {
5345
+ return db.select().from(usagePeaks).orderBy(desc(usagePeaks.resetsAt), usagePeaks.window);
5346
+ }
5347
+
5348
+ // src/commands/sessions/web/listUsageHistory.ts
5349
+ async function listUsageHistory(_req, res) {
5350
+ respondJson(res, 200, await listUsagePeaks(await getDb()));
5351
+ }
5352
+
5316
5353
  // src/commands/sessions/web/openInCode.ts
5317
5354
  import { exec } from "child_process";
5318
5355
  import { promisify as promisify2 } from "util";
@@ -5462,7 +5499,8 @@ var routes2 = {
5462
5499
  "POST /api/restart": restartWeb,
5463
5500
  "GET /api/github-url": githubUrl,
5464
5501
  "GET /api/git-status": gitStatus,
5465
- "GET /api/news/items": listNewsItems
5502
+ "GET /api/news/items": listNewsItems,
5503
+ "GET /api/usage/history": listUsageHistory
5466
5504
  };
5467
5505
  var handleRequest = createFallbackHandler(
5468
5506
  routes2,
@@ -5877,10 +5915,10 @@ async function web2(options2) {
5877
5915
 
5878
5916
  // src/commands/backlog/comment/index.ts
5879
5917
  import chalk48 from "chalk";
5880
- async function comment(id2, text4) {
5918
+ async function comment(id2, text5) {
5881
5919
  const found = await findOneItem(id2);
5882
5920
  if (!found) process.exit(1);
5883
- await appendComment(found.orm, found.item.id, text4);
5921
+ await appendComment(found.orm, found.item.id, text5);
5884
5922
  console.log(chalk48.green(`Comment added to item #${id2}.`));
5885
5923
  }
5886
5924
 
@@ -5993,8 +6031,8 @@ async function buildDump(copyOut) {
5993
6031
  // src/commands/backlog/dump/copyTableOut.ts
5994
6032
  import { to as copyTo } from "pg-copy-streams";
5995
6033
  async function copyTableOut(client, table) {
5996
- const sql5 = `COPY ${table.name} (${table.columns.join(", ")}) TO STDOUT`;
5997
- const stream = client.query(copyTo(sql5));
6034
+ const sql6 = `COPY ${table.name} (${table.columns.join(", ")}) TO STDOUT`;
6035
+ const stream = client.query(copyTo(sql6));
5998
6036
  const chunks = [];
5999
6037
  for await (const chunk of stream) {
6000
6038
  chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
@@ -6050,9 +6088,9 @@ function readLine(dump, start3) {
6050
6088
  return { text: dump.subarray(start3, eol).toString("utf8"), next: eol + 1 };
6051
6089
  }
6052
6090
  function parseHeader(dump) {
6053
- const { text: text4, next: next3 } = readLine(dump, 0);
6091
+ const { text: text5, next: next3 } = readLine(dump, 0);
6054
6092
  try {
6055
- return { header: JSON.parse(text4), bodyStart: next3 };
6093
+ return { header: JSON.parse(text5), bodyStart: next3 };
6056
6094
  } catch {
6057
6095
  return invalid("header is not valid JSON.");
6058
6096
  }
@@ -6061,9 +6099,9 @@ function parseSections(dump, bodyStart) {
6061
6099
  const sections = /* @__PURE__ */ new Map();
6062
6100
  let cursor = bodyStart;
6063
6101
  while (cursor < dump.length) {
6064
- const { text: text4, next: next3 } = readLine(dump, cursor);
6065
- const match = text4.match(/^@table (\S+) (\d+)$/);
6066
- if (!match) invalid(`malformed table marker "${text4}".`);
6102
+ const { text: text5, next: next3 } = readLine(dump, cursor);
6103
+ const match = text5.match(/^@table (\S+) (\d+)$/);
6104
+ if (!match) invalid(`malformed table marker "${text5}".`);
6067
6105
  const [, name, bytes] = match;
6068
6106
  const end = next3 + Number(bytes);
6069
6107
  if (end > dump.length) invalid(`section "${name}" overruns the dump.`);
@@ -6144,8 +6182,8 @@ async function readStdinBuffer() {
6144
6182
  import { finished } from "stream/promises";
6145
6183
  import { from as copyFrom } from "pg-copy-streams";
6146
6184
  async function copyTableIn(client, table, data) {
6147
- const sql5 = `COPY ${table.name} (${table.columns.join(", ")}) FROM STDIN`;
6148
- const stream = client.query(copyFrom(sql5));
6185
+ const sql6 = `COPY ${table.name} (${table.columns.join(", ")}) FROM STDIN`;
6186
+ const stream = client.query(copyFrom(sql6));
6149
6187
  stream.end(data);
6150
6188
  await finished(stream);
6151
6189
  }
@@ -6314,9 +6352,9 @@ import chalk56 from "chalk";
6314
6352
  import { count, eq as eq17 } from "drizzle-orm";
6315
6353
 
6316
6354
  // src/commands/backlog/shiftPhasesUp.ts
6317
- import { and as and3, desc, eq as eq16, gte } from "drizzle-orm";
6355
+ import { and as and3, desc as desc2, eq as eq16, gte } from "drizzle-orm";
6318
6356
  async function shiftPhasesUp(db, itemId, fromIdx) {
6319
- const toShift = await db.select({ idx: planPhases.idx }).from(planPhases).where(and3(eq16(planPhases.itemId, itemId), gte(planPhases.idx, fromIdx))).orderBy(desc(planPhases.idx));
6357
+ const toShift = await db.select({ idx: planPhases.idx }).from(planPhases).where(and3(eq16(planPhases.itemId, itemId), gte(planPhases.idx, fromIdx))).orderBy(desc2(planPhases.idx));
6320
6358
  for (const p of toShift) {
6321
6359
  await db.update(planTasks).set({ phaseIdx: p.idx + 1 }).where(and3(eq16(planTasks.itemId, itemId), eq16(planTasks.phaseIdx, p.idx)));
6322
6360
  await db.update(planPhases).set({ idx: p.idx + 1 }).where(and3(eq16(planPhases.itemId, itemId), eq16(planPhases.idx, p.idx)));
@@ -7191,8 +7229,8 @@ function applyAcMutations(current, options2) {
7191
7229
  // src/commands/backlog/update/buildUpdateValues.ts
7192
7230
  import chalk78 from "chalk";
7193
7231
  function buildUpdateValues(options2) {
7194
- const { name, desc: desc4, type, ac } = options2;
7195
- if (!name && !desc4 && !type && !ac) {
7232
+ const { name, desc: desc5, type, ac } = options2;
7233
+ if (!name && !desc5 && !type && !ac) {
7196
7234
  console.log(chalk78.red("Nothing to update. Provide at least one flag."));
7197
7235
  process.exitCode = 1;
7198
7236
  return void 0;
@@ -7208,8 +7246,8 @@ function buildUpdateValues(options2) {
7208
7246
  set.name = name;
7209
7247
  fieldNames.push("name");
7210
7248
  }
7211
- if (desc4) {
7212
- set.description = desc4;
7249
+ if (desc5) {
7250
+ set.description = desc5;
7213
7251
  fieldNames.push("description");
7214
7252
  }
7215
7253
  if (type) {
@@ -8325,8 +8363,8 @@ function formatHuman(cli, commands) {
8325
8363
  `];
8326
8364
  for (const cmd of sorted) {
8327
8365
  const full = `${cli} ${cmd.path.join(" ")}`;
8328
- const text4 = cmd.description ? `${full} \u2014 ${cmd.description}` : full;
8329
- lines.push(`${prefix(classifyVerb(cmd.path))}${text4}`);
8366
+ const text5 = cmd.description ? `${full} \u2014 ${cmd.description}` : full;
8367
+ lines.push(`${prefix(classifyVerb(cmd.path))}${text5}`);
8330
8368
  }
8331
8369
  return lines.join("\n");
8332
8370
  }
@@ -10722,13 +10760,13 @@ async function load(options2 = {}) {
10722
10760
  }
10723
10761
 
10724
10762
  // src/commands/handover/listPendingHandovers.ts
10725
- import { and as and11, desc as desc2, eq as eq30, isNull as isNull2 } from "drizzle-orm";
10763
+ import { and as and11, desc as desc3, eq as eq30, isNull as isNull2 } from "drizzle-orm";
10726
10764
  async function listPendingHandovers(orm, origin) {
10727
10765
  return orm.select({
10728
10766
  id: handovers.id,
10729
10767
  summary: handovers.summary,
10730
10768
  createdAt: handovers.createdAt
10731
- }).from(handovers).where(and11(eq30(handovers.origin, origin), isNull2(handovers.recalledAt))).orderBy(desc2(handovers.createdAt), desc2(handovers.id));
10769
+ }).from(handovers).where(and11(eq30(handovers.origin, origin), isNull2(handovers.recalledAt))).orderBy(desc3(handovers.createdAt), desc3(handovers.id));
10732
10770
  }
10733
10771
 
10734
10772
  // src/commands/handover/printPendingHandovers.ts
@@ -10741,7 +10779,7 @@ async function printPendingHandovers() {
10741
10779
  }
10742
10780
 
10743
10781
  // src/commands/handover/recallHandover.ts
10744
- import { and as and12, desc as desc3, eq as eq31, isNull as isNull3 } from "drizzle-orm";
10782
+ import { and as and12, desc as desc4, eq as eq31, isNull as isNull3 } from "drizzle-orm";
10745
10783
  async function recallHandover(orm, origin, id2) {
10746
10784
  const [row] = await orm.select().from(handovers).where(
10747
10785
  and12(
@@ -10749,7 +10787,7 @@ async function recallHandover(orm, origin, id2) {
10749
10787
  isNull3(handovers.recalledAt),
10750
10788
  ...id2 === void 0 ? [] : [eq31(handovers.id, id2)]
10751
10789
  )
10752
- ).orderBy(desc3(handovers.createdAt), desc3(handovers.id)).limit(1);
10790
+ ).orderBy(desc4(handovers.createdAt), desc4(handovers.id)).limit(1);
10753
10791
  if (!row) return void 0;
10754
10792
  await orm.update(handovers).set({ recalledAt: /* @__PURE__ */ new Date() }).where(eq31(handovers.id, row.id));
10755
10793
  return row.content;
@@ -10805,9 +10843,9 @@ import chalk115 from "chalk";
10805
10843
 
10806
10844
  // src/commands/jira/adfToText.ts
10807
10845
  function renderInline(node) {
10808
- const text4 = node.text ?? "";
10809
- if (node.marks?.some((m) => m.type === "code")) return `\`${text4}\``;
10810
- return text4;
10846
+ const text5 = node.text ?? "";
10847
+ if (node.marks?.some((m) => m.type === "code")) return `\`${text5}\``;
10848
+ return text5;
10811
10849
  }
10812
10850
  function renderChildren(node, indent2) {
10813
10851
  return renderNodes(node.content ?? [], indent2);
@@ -10849,8 +10887,8 @@ function isListNode(node) {
10849
10887
  function renderListChild(child, indent2, pad, marker, isFirst) {
10850
10888
  if (isListNode(child)) return renderNodes([child], indent2 + 1);
10851
10889
  if (child.type !== "paragraph") return renderNode(child, indent2);
10852
- const text4 = renderChildren(child, indent2);
10853
- return isFirst ? `${pad}${marker} ${text4}` : `${pad} ${text4}`;
10890
+ const text5 = renderChildren(child, indent2);
10891
+ return isFirst ? `${pad}${marker} ${text5}` : `${pad} ${text5}`;
10854
10892
  }
10855
10893
  function renderListItem(node, indent2, marker) {
10856
10894
  const pad = " ".repeat(indent2);
@@ -11540,8 +11578,8 @@ function isWallOfText(lines) {
11540
11578
  if (lines.some(isListLine)) {
11541
11579
  return false;
11542
11580
  }
11543
- const text4 = lines.join(" ").trim();
11544
- return text4.length > MAX_PARAGRAPH_CHARS || countSentences(text4) > MAX_PARAGRAPH_SENTENCES;
11581
+ const text5 = lines.join(" ").trim();
11582
+ return text5.length > MAX_PARAGRAPH_CHARS || countSentences(text5) > MAX_PARAGRAPH_SENTENCES;
11545
11583
  }
11546
11584
  function validatePrContent(title, body) {
11547
11585
  if (title.toLowerCase().includes("claude")) {
@@ -13202,9 +13240,9 @@ function resolveImports(target, dependencies, sourceFile, statements = []) {
13202
13240
  function extractTexts(target, allFunctions, statements) {
13203
13241
  const stmtTexts = statements.map((v) => v.getFullText().trim());
13204
13242
  const fnTexts = allFunctions.map((fn) => {
13205
- const text4 = fn.getFullText().trim();
13206
- if (fn === target && !text4.startsWith("export ")) return `export ${text4}`;
13207
- return text4;
13243
+ const text5 = fn.getFullText().trim();
13244
+ if (fn === target && !text5.startsWith("export ")) return `export ${text5}`;
13245
+ return text5;
13208
13246
  });
13209
13247
  return [...stmtTexts, ...fnTexts];
13210
13248
  }
@@ -15040,9 +15078,9 @@ var MultiSpinner = class {
15040
15078
  elapsedPrefix: prefix2
15041
15079
  });
15042
15080
  }
15043
- failRemaining(text4) {
15081
+ failRemaining(text5) {
15044
15082
  for (const entry of this.entries) {
15045
- if (entry.state === "running") this.resolve(entry, "failed", text4);
15083
+ if (entry.state === "running") this.resolve(entry, "failed", text5);
15046
15084
  }
15047
15085
  }
15048
15086
  add(entry) {
@@ -15055,14 +15093,14 @@ var MultiSpinner = class {
15055
15093
  set text(value) {
15056
15094
  entry.text = value;
15057
15095
  },
15058
- succeed: (text4) => this.resolve(entry, "succeeded", text4),
15059
- fail: (text4) => this.resolve(entry, "failed", text4)
15096
+ succeed: (text5) => this.resolve(entry, "succeeded", text5),
15097
+ fail: (text5) => this.resolve(entry, "failed", text5)
15060
15098
  };
15061
15099
  }
15062
- resolve(entry, state, text4) {
15100
+ resolve(entry, state, text5) {
15063
15101
  if (entry.state !== "running") return;
15064
15102
  entry.state = state;
15065
- if (text4 !== void 0) entry.text = text4;
15103
+ if (text5 !== void 0) entry.text = text5;
15066
15104
  entry.elapsedStart = void 0;
15067
15105
  this.render();
15068
15106
  this.maybeFinish();
@@ -15137,12 +15175,12 @@ function skippedCodexResult(outputPath) {
15137
15175
  // src/commands/review/formatReviewerFailure.ts
15138
15176
  var FAST_FAIL_MS = 1e3;
15139
15177
  var STDOUT_TAIL_LINES = 20;
15140
- function indent(text4) {
15141
- return text4.split(/\r?\n/).map((line) => ` ${line}`);
15178
+ function indent(text5) {
15179
+ return text5.split(/\r?\n/).map((line) => ` ${line}`);
15142
15180
  }
15143
- function tailLines(text4, maxLines) {
15144
- const lines = text4.split(/\r?\n/);
15145
- return lines.length <= maxLines ? text4 : lines.slice(-maxLines).join("\n");
15181
+ function tailLines(text5, maxLines) {
15182
+ const lines = text5.split(/\r?\n/);
15183
+ return lines.length <= maxLines ? text5 : lines.slice(-maxLines).join("\n");
15146
15184
  }
15147
15185
  function isFastFail(input) {
15148
15186
  return input.exitCode !== 0 && input.elapsedMs !== void 0 && input.elapsedMs < FAST_FAIL_MS;
@@ -16062,8 +16100,8 @@ function filterToSql(filter) {
16062
16100
  // src/commands/seq/fetchSeqData.ts
16063
16101
  function buildDataParams(filter, count6, from, to) {
16064
16102
  const sqlFilter = filterToSql(filter);
16065
- const sql5 = `select @Timestamp, @Level, @Exception, @Message from stream where ${sqlFilter} order by @Timestamp desc limit ${count6}`;
16066
- const params = new URLSearchParams({ q: sql5 });
16103
+ const sql6 = `select @Timestamp, @Level, @Exception, @Message from stream where ${sqlFilter} order by @Timestamp desc limit ${count6}`;
16104
+ const params = new URLSearchParams({ q: sql6 });
16067
16105
  if (from) params.set("rangeStartUtc", from);
16068
16106
  if (to) params.set("rangeEndUtc", to);
16069
16107
  return params;
@@ -16323,26 +16361,26 @@ import chalk159 from "chalk";
16323
16361
  // src/commands/sql/loadConnections.ts
16324
16362
  function loadConnections3() {
16325
16363
  const raw = loadGlobalConfigRaw();
16326
- const sql5 = raw.sql;
16327
- return sql5?.connections ?? [];
16364
+ const sql6 = raw.sql;
16365
+ return sql6?.connections ?? [];
16328
16366
  }
16329
16367
  function saveConnections3(connections) {
16330
16368
  const raw = loadGlobalConfigRaw();
16331
- const sql5 = raw.sql ?? {};
16332
- sql5.connections = connections;
16333
- raw.sql = sql5;
16369
+ const sql6 = raw.sql ?? {};
16370
+ sql6.connections = connections;
16371
+ raw.sql = sql6;
16334
16372
  saveGlobalConfig(raw);
16335
16373
  }
16336
16374
  function getDefaultConnection2() {
16337
16375
  const raw = loadGlobalConfigRaw();
16338
- const sql5 = raw.sql;
16339
- return sql5?.defaultConnection;
16376
+ const sql6 = raw.sql;
16377
+ return sql6?.defaultConnection;
16340
16378
  }
16341
16379
  function setDefaultConnection2(name) {
16342
16380
  const raw = loadGlobalConfigRaw();
16343
- const sql5 = raw.sql ?? {};
16344
- sql5.defaultConnection = name;
16345
- raw.sql = sql5;
16381
+ const sql6 = raw.sql ?? {};
16382
+ sql6.defaultConnection = name;
16383
+ raw.sql = sql6;
16346
16384
  saveGlobalConfig(raw);
16347
16385
  }
16348
16386
 
@@ -16478,11 +16516,11 @@ var MUTATION_PATTERN = new RegExp(
16478
16516
  `\\b(${MUTATION_KEYWORDS.join("|")})\\b`,
16479
16517
  "i"
16480
16518
  );
16481
- function stripComments(sql5) {
16482
- return sql5.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/--[^\n]*/g, " ");
16519
+ function stripComments(sql6) {
16520
+ return sql6.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/--[^\n]*/g, " ");
16483
16521
  }
16484
- function isMutation(sql5) {
16485
- const stripped = stripComments(sql5);
16522
+ function isMutation(sql6) {
16523
+ const stripped = stripComments(sql6);
16486
16524
  if (MUTATION_PATTERN.test(stripped)) return true;
16487
16525
  return /\bSELECT\b[\s\S]+\bINTO\s+\w/i.test(stripped);
16488
16526
  }
@@ -16778,8 +16816,8 @@ import { existsSync as existsSync38, mkdirSync as mkdirSync14, readFileSync as r
16778
16816
  import { basename as basename9, dirname as dirname21, join as join41 } from "path";
16779
16817
 
16780
16818
  // src/commands/transcript/cleanText.ts
16781
- function cleanText(text4) {
16782
- const words = text4.split(/\s+/);
16819
+ function cleanText(text5) {
16820
+ const words = text5.split(/\s+/);
16783
16821
  const cleaned = [];
16784
16822
  for (let i = 0; i < words.length; i++) {
16785
16823
  let isRepeat = false;
@@ -16799,8 +16837,8 @@ function cleanText(text4) {
16799
16837
  }
16800
16838
 
16801
16839
  // src/commands/transcript/format/processVttFile/parseVtt/deduplicateCues/removeSubstringDuplicates.ts
16802
- function normalizeText(text4) {
16803
- return text4.toLowerCase().trim();
16840
+ function normalizeText(text5) {
16841
+ return text5.toLowerCase().trim();
16804
16842
  }
16805
16843
  function checkSubstringRelation(textI, textJ) {
16806
16844
  if (textI.includes(textJ) && textI.length > textJ.length)
@@ -16929,13 +16967,13 @@ function parseTimestampLine(line) {
16929
16967
  return { startMs: parseTimestamp(startStr), endMs: parseTimestamp(endStr) };
16930
16968
  }
16931
16969
  function buildCue(startMs, endMs, fullText) {
16932
- const { speaker, text: text4 } = extractSpeaker(fullText);
16933
- return text4 ? { startMs, endMs, speaker, text: text4 } : null;
16970
+ const { speaker, text: text5 } = extractSpeaker(fullText);
16971
+ return text5 ? { startMs, endMs, speaker, text: text5 } : null;
16934
16972
  }
16935
16973
  function parseCueLine(lines, i) {
16936
16974
  const { startMs, endMs } = parseTimestampLine(lines[i]);
16937
- const { text: text4, nextIndex: nextIndex2 } = collectTextLines(lines, i + 1);
16938
- return { cue: buildCue(startMs, endMs, text4), nextIndex: nextIndex2 };
16975
+ const { text: text5, nextIndex: nextIndex2 } = collectTextLines(lines, i + 1);
16976
+ return { cue: buildCue(startMs, endMs, text5), nextIndex: nextIndex2 };
16939
16977
  }
16940
16978
  function isCueSeparator(line) {
16941
16979
  return line.trim().includes("-->");
@@ -17604,8 +17642,8 @@ async function exchangeToken(params) {
17604
17642
  body: body.toString()
17605
17643
  });
17606
17644
  if (!response.ok) {
17607
- const text4 = await response.text();
17608
- throw new Error(`Token exchange failed (${response.status}): ${text4}`);
17645
+ const text5 = await response.text();
17646
+ throw new Error(`Token exchange failed (${response.status}): ${text5}`);
17609
17647
  }
17610
17648
  return response.json();
17611
17649
  }
@@ -18551,12 +18589,50 @@ function broadcastSessions(sessions, clients, windowsSessions = [], active) {
18551
18589
  });
18552
18590
  }
18553
18591
 
18592
+ // src/shared/db/recordUsagePeak.ts
18593
+ import { sql as sql5 } from "drizzle-orm";
18594
+ var WINDOWS = ["five_hour", "seven_day"];
18595
+ async function recordUsagePeak(db, rateLimits) {
18596
+ const rows = WINDOWS.flatMap((window) => {
18597
+ const w = rateLimits[window];
18598
+ if (!w) return [];
18599
+ if (typeof w.resets_at !== "number" || typeof w.used_percentage !== "number")
18600
+ return [];
18601
+ return [
18602
+ { window, resetsAt: w.resets_at, usedPercentage: w.used_percentage }
18603
+ ];
18604
+ });
18605
+ if (rows.length === 0) return;
18606
+ await db.insert(usagePeaks).values(rows).onConflictDoUpdate({
18607
+ target: [usagePeaks.window, usagePeaks.resetsAt],
18608
+ set: {
18609
+ usedPercentage: sql5`GREATEST(${usagePeaks.usedPercentage}, excluded.used_percentage)`
18610
+ }
18611
+ });
18612
+ }
18613
+
18614
+ // src/commands/sessions/daemon/persistUsagePeak.ts
18615
+ async function persistUsagePeak(rateLimits) {
18616
+ try {
18617
+ if (!process.env.ASSIST_DATABASE_URL && !loadConfig().database.url) return;
18618
+ await recordUsagePeak(await getDb(), rateLimits);
18619
+ } catch (error) {
18620
+ daemonLog(`usage-peak persist failed: ${String(error)}`);
18621
+ }
18622
+ }
18623
+
18554
18624
  // src/commands/sessions/daemon/ClientHub.ts
18555
18625
  var ClientHub = class extends Set {
18626
+ // why: the daemon injects a best-effort persister; left undefined elsewhere so `new ClientHub()` works and broadcasting never depends on it.
18627
+ constructor(persistPeak) {
18628
+ super();
18629
+ this.persistPeak = persistPeak;
18630
+ }
18556
18631
  latestLimits;
18557
18632
  updateLimits(rateLimits) {
18558
18633
  this.latestLimits = rateLimits;
18559
18634
  broadcast(this, { type: "limits", rateLimits });
18635
+ this.persistPeak?.(rateLimits);
18560
18636
  }
18561
18637
  greet(client) {
18562
18638
  if (this.latestLimits) {
@@ -19540,8 +19616,8 @@ function deriveSessionType(commandName, name) {
19540
19616
  }
19541
19617
 
19542
19618
  // src/commands/sessions/shared/backlogRunMarkers.ts
19543
- function backlogRunMarkers(text4) {
19544
- const match = text4.match(/backlog item #(\d+):[ \t]*([^\n]*)/);
19619
+ function backlogRunMarkers(text5) {
19620
+ const match = text5.match(/backlog item #(\d+):[ \t]*([^\n]*)/);
19545
19621
  if (!match) return { commandName: "", commandArgs: "" };
19546
19622
  const title = match[2].trim();
19547
19623
  return {
@@ -19595,11 +19671,11 @@ function messageText(entry) {
19595
19671
  const content = msg?.content;
19596
19672
  return typeof content === "string" ? content : Array.isArray(content) ? content.find((c) => c.type === "text")?.text ?? "" : "";
19597
19673
  }
19598
- function stripMarkers(text4) {
19599
- return text4.replace(/<command-[^>]*>[^<]*<\/command-[^>]*>/g, "").trim().slice(0, 80);
19674
+ function stripMarkers(text5) {
19675
+ return text5.replace(/<command-[^>]*>[^<]*<\/command-[^>]*>/g, "").trim().slice(0, 80);
19600
19676
  }
19601
- function matchMarker(text4, tag) {
19602
- const match = text4.match(new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`));
19677
+ function matchMarker(text5, tag) {
19678
+ const match = text5.match(new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`));
19603
19679
  return match ? match[1].trim() : "";
19604
19680
  }
19605
19681
 
@@ -19809,7 +19885,7 @@ var SessionManager = class {
19809
19885
  sessions = /* @__PURE__ */ new Map();
19810
19886
  // why: dispatch calls active.set() on card click; broadcasts include active.toJSON()
19811
19887
  active = new ActiveSelection(() => this.notify());
19812
- clients = new ClientHub();
19888
+ clients = new ClientHub(persistUsagePeak);
19813
19889
  nextId = 1;
19814
19890
  shuttingDown = false;
19815
19891
  // why: dispatch calls windowsProxy.route() to forward windows-origin sessions
@@ -19921,17 +19997,17 @@ function entryMessages(entry) {
19921
19997
  return [];
19922
19998
  }
19923
19999
  function userMessages(content) {
19924
- if (typeof content === "string") return text3("user", cleanUserText(content));
20000
+ if (typeof content === "string") return text4("user", cleanUserText(content));
19925
20001
  if (!Array.isArray(content)) return [];
19926
- return content.filter((c) => c.type === "text").flatMap((c) => text3("user", cleanUserText(c.text ?? "")));
20002
+ return content.filter((c) => c.type === "text").flatMap((c) => text4("user", cleanUserText(c.text ?? "")));
19927
20003
  }
19928
20004
  function assistantMessages(content) {
19929
- if (typeof content === "string") return text3("assistant", content.trim());
20005
+ if (typeof content === "string") return text4("assistant", content.trim());
19930
20006
  if (!Array.isArray(content)) return [];
19931
20007
  return content.flatMap((c) => assistantItem(c));
19932
20008
  }
19933
20009
  function assistantItem(c) {
19934
- if (c.type === "text") return text3("assistant", (c.text ?? "").trim());
20010
+ if (c.type === "text") return text4("assistant", (c.text ?? "").trim());
19935
20011
  if (c.type === "tool_use")
19936
20012
  return [
19937
20013
  {
@@ -19942,7 +20018,7 @@ function assistantItem(c) {
19942
20018
  ];
19943
20019
  return [];
19944
20020
  }
19945
- function text3(role, value) {
20021
+ function text4(role, value) {
19946
20022
  return value ? [{ role, text: value }] : [];
19947
20023
  }
19948
20024
  function cleanUserText(value) {
@@ -20273,16 +20349,16 @@ function parseUserLine(line) {
20273
20349
  if (entry.type !== "user") return void 0;
20274
20350
  const msg = entry.message;
20275
20351
  const c = msg?.content;
20276
- let text4;
20352
+ let text5;
20277
20353
  if (typeof c === "string") {
20278
- text4 = c;
20354
+ text5 = c;
20279
20355
  } else if (Array.isArray(c)) {
20280
20356
  const collected = c.filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n");
20281
- text4 = collected || void 0;
20357
+ text5 = collected || void 0;
20282
20358
  }
20283
- if (!text4) return void 0;
20359
+ if (!text5) return void 0;
20284
20360
  return {
20285
- text: text4,
20361
+ text: text5,
20286
20362
  entrypoint: typeof entry.entrypoint === "string" ? entry.entrypoint : void 0
20287
20363
  };
20288
20364
  }
@@ -20311,13 +20387,13 @@ function* iterateUserMessages(filePath, maxBytes = 65536) {
20311
20387
 
20312
20388
  // src/commands/sessions/summarise/extractFirstUserMessage.ts
20313
20389
  function extractFirstUserMessage(filePath) {
20314
- for (const text4 of iterateUserMessages(filePath)) {
20315
- return truncate3(text4);
20390
+ for (const text5 of iterateUserMessages(filePath)) {
20391
+ return truncate3(text5);
20316
20392
  }
20317
20393
  return void 0;
20318
20394
  }
20319
- function truncate3(text4, maxChars = 500) {
20320
- const trimmed = text4.trim();
20395
+ function truncate3(text5, maxChars = 500) {
20396
+ const trimmed = text5.trim();
20321
20397
  if (trimmed.length <= maxChars) return trimmed;
20322
20398
  return `${trimmed.slice(0, maxChars)}\u2026`;
20323
20399
  }
@@ -20325,28 +20401,28 @@ function truncate3(text4, maxChars = 500) {
20325
20401
  // src/commands/sessions/summarise/scanSessionBacklogRefs.ts
20326
20402
  function scanSessionBacklogRefs(filePath) {
20327
20403
  const ids = /* @__PURE__ */ new Set();
20328
- for (const text4 of iterateUserMessages(filePath, Number.MAX_SAFE_INTEGER)) {
20329
- for (const id2 of extractBacklogIds(text4)) {
20404
+ for (const text5 of iterateUserMessages(filePath, Number.MAX_SAFE_INTEGER)) {
20405
+ for (const id2 of extractBacklogIds(text5)) {
20330
20406
  ids.add(id2);
20331
20407
  }
20332
20408
  }
20333
20409
  return [...ids].sort((a, b) => a - b);
20334
20410
  }
20335
- function extractBacklogIds(text4) {
20411
+ function extractBacklogIds(text5) {
20336
20412
  const ids = [];
20337
- for (const m of text4.matchAll(/backlog\s+run\s+(\d+)/gi)) {
20413
+ for (const m of text5.matchAll(/backlog\s+run\s+(\d+)/gi)) {
20338
20414
  ids.push(Number.parseInt(m[1], 10));
20339
20415
  }
20340
- for (const m of text4.matchAll(/backlog\s+(?:item\s+)?#(\d+)/gi)) {
20416
+ for (const m of text5.matchAll(/backlog\s+(?:item\s+)?#(\d+)/gi)) {
20341
20417
  ids.push(Number.parseInt(m[1], 10));
20342
20418
  }
20343
- for (const m of text4.matchAll(/backlog\s+phase-done\s+(\d+)/gi)) {
20419
+ for (const m of text5.matchAll(/backlog\s+phase-done\s+(\d+)/gi)) {
20344
20420
  ids.push(Number.parseInt(m[1], 10));
20345
20421
  }
20346
- for (const m of text4.matchAll(/backlog\s+comment\s+(\d+)/gi)) {
20422
+ for (const m of text5.matchAll(/backlog\s+comment\s+(\d+)/gi)) {
20347
20423
  ids.push(Number.parseInt(m[1], 10));
20348
20424
  }
20349
- for (const m of text4.matchAll(/(?:^|[\s(])#(\d{1,4})(?=[\s).,;:!?]|$)/gm)) {
20425
+ for (const m of text5.matchAll(/(?:^|[\s(])#(\d{1,4})(?=[\s).,;:!?]|$)/gm)) {
20350
20426
  ids.push(Number.parseInt(m[1], 10));
20351
20427
  }
20352
20428
  return ids;