@staff0rd/assist 0.381.0 → 0.382.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.381.0",
9
+ version: "0.382.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -432,10 +432,10 @@ function getTranscriptConfig() {
432
432
  // src/shared/db/schema.ts
433
433
  import {
434
434
  foreignKey,
435
- integer as integer8,
436
- pgTable as pgTable8,
437
- primaryKey as primaryKey5,
438
- text as text7
435
+ integer as integer9,
436
+ pgTable as pgTable9,
437
+ primaryKey as primaryKey6,
438
+ text as text8
439
439
  } from "drizzle-orm/pg-core";
440
440
 
441
441
  // src/shared/db/backups.ts
@@ -536,82 +536,96 @@ var phaseUsage = pgTable6(
536
536
  (t) => [primaryKey3({ columns: [t.itemId, t.phaseIdx] })]
537
537
  );
538
538
 
539
+ // src/shared/db/phaseUsageMessages.ts
540
+ import { integer as integer7, pgTable as pgTable7, primaryKey as primaryKey4, text as text6 } from "drizzle-orm/pg-core";
541
+ var phaseUsageMessages = pgTable7(
542
+ "phase_usage_messages",
543
+ {
544
+ itemId: integer7("item_id").notNull(),
545
+ phaseIdx: integer7("phase_idx").notNull(),
546
+ messageId: text6("message_id").notNull()
547
+ },
548
+ (t) => [primaryKey4({ columns: [t.itemId, t.phaseIdx, t.messageId] })]
549
+ );
550
+
539
551
  // src/shared/db/usagePeaks.ts
540
552
  import {
541
553
  bigint as bigint3,
542
554
  boolean as boolean2,
543
555
  doublePrecision,
544
- integer as integer7,
545
- pgTable as pgTable7,
546
- primaryKey as primaryKey4,
547
- text as text6,
556
+ integer as integer8,
557
+ pgTable as pgTable8,
558
+ primaryKey as primaryKey5,
559
+ text as text7,
548
560
  timestamp as timestamp4
549
561
  } from "drizzle-orm/pg-core";
550
- var usagePeaks = pgTable7(
562
+ var usagePeaks = pgTable8(
551
563
  "usage_peaks",
552
564
  {
553
- window: text6().$type().notNull(),
565
+ window: text7().$type().notNull(),
554
566
  resetsAt: bigint3("resets_at", { mode: "number" }).notNull(),
555
- segment: integer7().notNull().default(0),
567
+ segment: integer8().notNull().default(0),
556
568
  usedPercentage: doublePrecision("used_percentage").notNull(),
557
569
  resetDetected: boolean2("reset_detected").notNull().default(false),
570
+ tokensUp: bigint3("tokens_up", { mode: "number" }).notNull().default(0),
571
+ tokensDown: bigint3("tokens_down", { mode: "number" }).notNull().default(0),
558
572
  createdAt: timestamp4("created_at", { withTimezone: true }).notNull().defaultNow()
559
573
  },
560
- (t) => [primaryKey4({ columns: [t.window, t.resetsAt, t.segment] })]
574
+ (t) => [primaryKey5({ columns: [t.window, t.resetsAt, t.segment] })]
561
575
  );
562
576
 
563
577
  // src/shared/db/schema.ts
564
- var comments = pgTable8("comments", {
565
- id: integer8().generatedByDefaultAsIdentity().primaryKey(),
566
- itemId: integer8("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
567
- idx: integer8().notNull(),
568
- text: text7().notNull(),
569
- phase: integer8(),
570
- timestamp: text7().notNull(),
571
- type: text7().notNull().default("comment")
578
+ var comments = pgTable9("comments", {
579
+ id: integer9().generatedByDefaultAsIdentity().primaryKey(),
580
+ itemId: integer9("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
581
+ idx: integer9().notNull(),
582
+ text: text8().notNull(),
583
+ phase: integer9(),
584
+ timestamp: text8().notNull(),
585
+ type: text8().notNull().default("comment")
572
586
  });
573
- var links = pgTable8(
587
+ var links = pgTable9(
574
588
  "links",
575
589
  {
576
- itemId: integer8("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
577
- type: text7().notNull(),
578
- targetId: integer8("target_id").notNull()
590
+ itemId: integer9("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
591
+ type: text8().notNull(),
592
+ targetId: integer9("target_id").notNull()
579
593
  },
580
- (t) => [primaryKey5({ columns: [t.itemId, t.type, t.targetId] })]
594
+ (t) => [primaryKey6({ columns: [t.itemId, t.type, t.targetId] })]
581
595
  );
582
- var planPhases = pgTable8(
596
+ var planPhases = pgTable9(
583
597
  "plan_phases",
584
598
  {
585
- itemId: integer8("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
586
- idx: integer8().notNull(),
587
- name: text7().notNull(),
588
- manualChecks: text7("manual_checks")
599
+ itemId: integer9("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
600
+ idx: integer9().notNull(),
601
+ name: text8().notNull(),
602
+ manualChecks: text8("manual_checks")
589
603
  },
590
- (t) => [primaryKey5({ columns: [t.itemId, t.idx] })]
604
+ (t) => [primaryKey6({ columns: [t.itemId, t.idx] })]
591
605
  );
592
- var planTasks = pgTable8(
606
+ var planTasks = pgTable9(
593
607
  "plan_tasks",
594
608
  {
595
- itemId: integer8("item_id").notNull(),
596
- phaseIdx: integer8("phase_idx").notNull(),
597
- idx: integer8().notNull(),
598
- task: text7().notNull()
609
+ itemId: integer9("item_id").notNull(),
610
+ phaseIdx: integer9("phase_idx").notNull(),
611
+ idx: integer9().notNull(),
612
+ task: text8().notNull()
599
613
  },
600
614
  (t) => [
601
- primaryKey5({ columns: [t.itemId, t.phaseIdx, t.idx] }),
615
+ primaryKey6({ columns: [t.itemId, t.phaseIdx, t.idx] }),
602
616
  foreignKey({
603
617
  columns: [t.itemId, t.phaseIdx],
604
618
  foreignColumns: [planPhases.itemId, planPhases.idx]
605
619
  }).onDelete("cascade")
606
620
  ]
607
621
  );
608
- var metadata = pgTable8("metadata", {
609
- key: text7().primaryKey(),
610
- value: text7().notNull()
622
+ var metadata = pgTable9("metadata", {
623
+ key: text8().primaryKey(),
624
+ value: text8().notNull()
611
625
  });
612
- var feeds = pgTable8("feeds", {
613
- id: integer8().generatedByDefaultAsIdentity().primaryKey(),
614
- url: text7().notNull().unique()
626
+ var feeds = pgTable9("feeds", {
627
+ id: integer9().generatedByDefaultAsIdentity().primaryKey(),
628
+ url: text8().notNull().unique()
615
629
  });
616
630
  var schema = {
617
631
  items,
@@ -626,7 +640,8 @@ var schema = {
626
640
  handovers,
627
641
  usagePeaks,
628
642
  backups,
629
- phaseUsage
643
+ phaseUsage,
644
+ phaseUsageMessages
630
645
  };
631
646
 
632
647
  // src/commands/backlog/addFeed.ts
@@ -662,30 +677,27 @@ async function seedNewsFeeds(db) {
662
677
  import { and as and2, eq as eq2 } from "drizzle-orm";
663
678
 
664
679
  // src/shared/db/recordWindowPeak.ts
665
- import { and, eq, gt, sql } from "drizzle-orm";
680
+ import { sql } from "drizzle-orm";
681
+
682
+ // src/shared/db/applyReading.ts
683
+ import { and, eq, gt } from "drizzle-orm";
666
684
  var RESET_DROP_THRESHOLD = 1;
667
685
  var at = ({ window, resetsAt }, segment) => segment === void 0 ? and(eq(usagePeaks.window, window), eq(usagePeaks.resetsAt, resetsAt)) : and(
668
686
  eq(usagePeaks.window, window),
669
687
  eq(usagePeaks.resetsAt, resetsAt),
670
688
  eq(usagePeaks.segment, segment)
671
689
  );
672
- var lock = ({ tx, window, resetsAt }) => tx.execute(
673
- sql`SELECT pg_advisory_xact_lock(hashtextextended(${`${window}:${resetsAt}`}, 0))`
674
- );
675
- var load = (c) => c.tx.select().from(usagePeaks).where(at(c)).orderBy(usagePeaks.segment);
676
690
  var insertSegment = (c, segment, usedPercentage) => c.tx.insert(usagePeaks).values({
677
691
  window: c.window,
678
692
  resetsAt: c.resetsAt,
679
693
  segment,
680
694
  usedPercentage
681
695
  });
682
- async function collapseToReached(c, reached, usedPercentage) {
683
- await c.tx.delete(usagePeaks).where(and(at(c), gt(usagePeaks.segment, reached.segment)));
684
- await c.tx.update(usagePeaks).set({ usedPercentage, resetDetected: false }).where(at(c, reached.segment));
685
- }
686
- async function openReset(c, active, usedPercentage) {
687
- await c.tx.update(usagePeaks).set({ resetDetected: true }).where(at(c, active.segment));
688
- await insertSegment(c, active.segment + 1, usedPercentage);
696
+ var load = (c) => c.tx.select().from(usagePeaks).where(at(c)).orderBy(usagePeaks.segment);
697
+ async function applyReading(c, usedPercentage) {
698
+ const segments = await load(c);
699
+ if (segments.length === 0) await insertSegment(c, 0, usedPercentage);
700
+ else await reconcile(c, segments, usedPercentage);
689
701
  }
690
702
  async function reconcile(c, segments, usedPercentage) {
691
703
  const reached = segments.find((s) => usedPercentage >= s.usedPercentage);
@@ -698,13 +710,24 @@ async function reconcile(c, segments, usedPercentage) {
698
710
  await openReset(c, active, usedPercentage);
699
711
  }
700
712
  }
713
+ async function collapseToReached(c, reached, usedPercentage) {
714
+ await c.tx.delete(usagePeaks).where(and(at(c), gt(usagePeaks.segment, reached.segment)));
715
+ await c.tx.update(usagePeaks).set({ usedPercentage, resetDetected: false }).where(at(c, reached.segment));
716
+ }
717
+ async function openReset(c, active, usedPercentage) {
718
+ await c.tx.update(usagePeaks).set({ resetDetected: true }).where(at(c, active.segment));
719
+ await insertSegment(c, active.segment + 1, usedPercentage);
720
+ }
721
+
722
+ // src/shared/db/recordWindowPeak.ts
723
+ var lock = ({ tx, window, resetsAt }) => tx.execute(
724
+ sql`SELECT pg_advisory_xact_lock(hashtextextended(${`${window}:${resetsAt}`}, 0))`
725
+ );
701
726
  async function recordWindowPeak(db, window, resetsAt, usedPercentage) {
702
727
  await db.transaction(async (tx) => {
703
728
  const c = { tx, window, resetsAt };
704
729
  await lock(c);
705
- const segments = await load(c);
706
- if (segments.length === 0) await insertSegment(c, 0, usedPercentage);
707
- else await reconcile(c, segments, usedPercentage);
730
+ await applyReading(c, usedPercentage);
708
731
  });
709
732
  }
710
733
 
@@ -859,6 +882,8 @@ var SCHEMA = `
859
882
  segment INTEGER NOT NULL DEFAULT 0,
860
883
  used_percentage DOUBLE PRECISION NOT NULL,
861
884
  reset_detected BOOLEAN NOT NULL DEFAULT false,
885
+ tokens_up BIGINT NOT NULL DEFAULT 0,
886
+ tokens_down BIGINT NOT NULL DEFAULT 0,
862
887
  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
863
888
  PRIMARY KEY ("window", resets_at, segment)
864
889
  );
@@ -867,6 +892,8 @@ var SCHEMA = `
867
892
  ALTER TABLE usage_peaks ADD COLUMN IF NOT EXISTS reset_detected BOOLEAN NOT NULL DEFAULT false;
868
893
  ALTER TABLE usage_peaks ADD COLUMN IF NOT EXISTS segment INTEGER NOT NULL DEFAULT 0;
869
894
  ALTER TABLE usage_peaks ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ NOT NULL DEFAULT now();
895
+ ALTER TABLE usage_peaks ADD COLUMN IF NOT EXISTS tokens_up BIGINT NOT NULL DEFAULT 0;
896
+ ALTER TABLE usage_peaks ADD COLUMN IF NOT EXISTS tokens_down BIGINT NOT NULL DEFAULT 0;
870
897
 
871
898
  -- Widen the primary key to include segment so a cycle can hold both its
872
899
  -- pre-reset peak and its post-reset continuation. Idempotent: only fires
@@ -907,6 +934,16 @@ var SCHEMA = `
907
934
  PRIMARY KEY (item_id, phase_idx)
908
935
  );
909
936
 
937
+ ALTER TABLE phase_usage ADD COLUMN IF NOT EXISTS last_total_in BIGINT;
938
+ ALTER TABLE phase_usage ADD COLUMN IF NOT EXISTS last_total_out BIGINT;
939
+
940
+ CREATE TABLE IF NOT EXISTS phase_usage_messages (
941
+ item_id INTEGER NOT NULL REFERENCES items(id) ON DELETE CASCADE,
942
+ phase_idx INTEGER NOT NULL,
943
+ message_id TEXT NOT NULL,
944
+ PRIMARY KEY (item_id, phase_idx, message_id)
945
+ );
946
+
910
947
  -- Git activity (branch/commit/PR) snapshotted for an item during a run. The
911
948
  -- (item_id, kind, ref) primary key makes re-snapshotting the same ref a no-op
912
949
  -- upsert rather than a duplicate row.
@@ -973,7 +1010,7 @@ function createPool() {
973
1010
  return pool;
974
1011
  }
975
1012
  async function initSchema(pool) {
976
- await ensureSchema((sql11) => pool.query(sql11));
1013
+ await ensureSchema((sql13) => pool.query(sql13));
977
1014
  const orm = makeOrmFromPool(pool);
978
1015
  await seedNewsFeeds(orm);
979
1016
  await runUsagePeakCleanup(orm);
@@ -2564,8 +2601,8 @@ function isHashCommentFile(filePath) {
2564
2601
  import fs13 from "fs";
2565
2602
 
2566
2603
  // src/commands/verify/blockComments/collectHashComments.ts
2567
- function blankNonNewline(text9) {
2568
- return text9.replace(/[^\n]/g, " ");
2604
+ function blankNonNewline(text10) {
2605
+ return text10.replace(/[^\n]/g, " ");
2569
2606
  }
2570
2607
  function isHeaderLine(line) {
2571
2608
  const trimmed = line.trim();
@@ -2642,23 +2679,23 @@ var MACHINE_DIRECTIVES = [
2642
2679
  "@vitest-environment",
2643
2680
  MAINTAINABILITY_OVERRIDE_MARKER
2644
2681
  ];
2645
- function isCommentExempt(text9) {
2646
- const lower = text9.toLowerCase();
2682
+ function isCommentExempt(text10) {
2683
+ const lower = text10.toLowerCase();
2647
2684
  return MACHINE_DIRECTIVES.some((d) => lower.includes(d));
2648
2685
  }
2649
2686
 
2650
2687
  // src/commands/verify/blockComments/collectSourceFindings.ts
2651
- function toSingleLine(text9) {
2652
- return text9.replace(/\s+/g, " ").trim();
2688
+ function toSingleLine(text10) {
2689
+ return text10.replace(/\s+/g, " ").trim();
2653
2690
  }
2654
2691
  function collectSourceFindings(file, lines, project) {
2655
2692
  const findings = [];
2656
2693
  const sourceFile = project.addSourceFileAtPath(file);
2657
- for (const { pos, text: text9 } of collectComments(sourceFile)) {
2694
+ for (const { pos, text: text10 } of collectComments(sourceFile)) {
2658
2695
  const { line } = sourceFile.getLineAndColumnAtPos(pos);
2659
2696
  if (!lines.has(line)) continue;
2660
- if (isCommentExempt(text9)) continue;
2661
- findings.push({ file, line, text: toSingleLine(text9) });
2697
+ if (isCommentExempt(text10)) continue;
2698
+ findings.push({ file, line, text: toSingleLine(text10) });
2662
2699
  }
2663
2700
  return findings;
2664
2701
  }
@@ -2678,10 +2715,10 @@ function collectYamlComments(content) {
2678
2715
  // src/commands/verify/blockComments/collectFileComments.ts
2679
2716
  function toFindings(file, lines, raw, exempt) {
2680
2717
  const findings = [];
2681
- for (const { line, text: text9 } of raw) {
2718
+ for (const { line, text: text10 } of raw) {
2682
2719
  if (!lines.has(line)) continue;
2683
- if (exempt && isCommentExempt(text9)) continue;
2684
- findings.push({ file, line, text: text9.replace(/\s+/g, " ").trim() });
2720
+ if (exempt && isCommentExempt(text10)) continue;
2721
+ findings.push({ file, line, text: text10.replace(/\s+/g, " ").trim() });
2685
2722
  }
2686
2723
  return findings;
2687
2724
  }
@@ -2779,8 +2816,8 @@ function blockComments() {
2779
2816
  process.exit(0);
2780
2817
  }
2781
2818
  console.log("Comments on changed lines:\n");
2782
- for (const { file, line, text: text9 } of findings) {
2783
- console.log(`${file}:${line} \u2192 ${text9}`);
2819
+ for (const { file, line, text: text10 } of findings) {
2820
+ console.log(`${file}:${line} \u2192 ${text10}`);
2784
2821
  }
2785
2822
  console.log(`
2786
2823
  Total: ${findings.length} comment(s)`);
@@ -4053,8 +4090,8 @@ async function buildDump(tables, copyOut) {
4053
4090
  // src/commands/backlog/dump/copyTableOut.ts
4054
4091
  import { to as copyTo } from "pg-copy-streams";
4055
4092
  async function copyTableOut(client, table) {
4056
- const sql11 = `COPY ${table.name} (${table.columns.join(", ")}) TO STDOUT`;
4057
- const stream = client.query(copyTo(sql11));
4093
+ const sql13 = `COPY ${table.name} (${table.columns.join(", ")}) TO STDOUT`;
4094
+ const stream = client.query(copyTo(sql13));
4058
4095
  const chunks = [];
4059
4096
  for await (const chunk of stream) {
4060
4097
  chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
@@ -4596,11 +4633,11 @@ import { promisify } from "util";
4596
4633
  // src/shared/withSpinner.ts
4597
4634
  var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
4598
4635
  var TICK_MS = 80;
4599
- async function withSpinner(text9, task) {
4636
+ async function withSpinner(text10, task) {
4600
4637
  if (!process.stderr.isTTY) return task();
4601
4638
  let frame = 0;
4602
4639
  const render = () => {
4603
- process.stderr.write(`\r${SPINNER_FRAMES[frame]} ${text9}`);
4640
+ process.stderr.write(`\r${SPINNER_FRAMES[frame]} ${text10}`);
4604
4641
  };
4605
4642
  render();
4606
4643
  const timer = setInterval(() => {
@@ -4937,6 +4974,18 @@ function attachSubtasks(item, rel, id) {
4937
4974
  if (subtasks.length > 0) item.subtasks = subtasks;
4938
4975
  }
4939
4976
 
4977
+ // src/commands/backlog/sumPhaseUsage.ts
4978
+ function sumPhaseUsage(usage) {
4979
+ return usage.reduce(
4980
+ (total, u) => ({
4981
+ tokensUp: total.tokensUp + u.tokensUp,
4982
+ tokensDown: total.tokensDown + u.tokensDown,
4983
+ activeMs: total.activeMs + u.activeMs
4984
+ }),
4985
+ { tokensUp: 0, tokensDown: 0, activeMs: 0 }
4986
+ );
4987
+ }
4988
+
4940
4989
  // src/commands/backlog/attachUsage.ts
4941
4990
  function rowToUsage(u) {
4942
4991
  return {
@@ -4948,7 +4997,10 @@ function rowToUsage(u) {
4948
4997
  }
4949
4998
  function attachUsage(item, rel, id) {
4950
4999
  const usage = (rel.usage.get(id) ?? []).map(rowToUsage);
4951
- if (usage.length > 0) item.phaseUsage = usage;
5000
+ if (usage.length > 0) {
5001
+ item.phaseUsage = usage;
5002
+ item.usageTotal = sumPhaseUsage(usage);
5003
+ }
4952
5004
  }
4953
5005
 
4954
5006
  // src/commands/backlog/buildPlan.ts
@@ -5062,6 +5114,11 @@ var phaseUsageSchema = z3.strictObject({
5062
5114
  tokensDown: z3.number(),
5063
5115
  activeMs: z3.number()
5064
5116
  });
5117
+ var phaseUsageTotalSchema = z3.strictObject({
5118
+ tokensUp: z3.number(),
5119
+ tokensDown: z3.number(),
5120
+ activeMs: z3.number()
5121
+ });
5065
5122
  var subtaskStatusSchema = z3.enum(["todo", "in-progress", "done"]);
5066
5123
  var subtaskSchema = z3.strictObject({
5067
5124
  title: z3.string(),
@@ -5104,6 +5161,7 @@ var backlogItemSchema = z3.strictObject({
5104
5161
  subtasks: z3.array(subtaskSchema).optional(),
5105
5162
  links: z3.array(backlogLinkSchema).optional(),
5106
5163
  phaseUsage: z3.array(phaseUsageSchema).optional(),
5164
+ usageTotal: phaseUsageTotalSchema.optional(),
5107
5165
  gitRefs: z3.array(gitRefSchema).optional(),
5108
5166
  origin: z3.string().optional(),
5109
5167
  jiraKey: z3.string().optional()
@@ -6014,11 +6072,11 @@ import chalk45 from "chalk";
6014
6072
 
6015
6073
  // src/commands/backlog/appendComment.ts
6016
6074
  import { sql as sql4 } from "drizzle-orm";
6017
- async function appendComment(orm, itemId2, text9, opts = {}) {
6075
+ async function appendComment(orm, itemId2, text10, opts = {}) {
6018
6076
  await orm.insert(comments).values({
6019
6077
  itemId: itemId2,
6020
6078
  idx: sql4`(SELECT COALESCE(MAX(${comments.idx}) + 1, 0) FROM ${comments} WHERE ${comments.itemId} = ${itemId2})`,
6021
- text: text9,
6079
+ text: text10,
6022
6080
  phase: opts.phase ?? null,
6023
6081
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6024
6082
  type: opts.type ?? "comment"
@@ -6121,7 +6179,7 @@ function formatComment(entry) {
6121
6179
  // src/shared/renderMarkdownTerminal.ts
6122
6180
  import { Marked } from "marked";
6123
6181
  import { markedTerminal } from "marked-terminal";
6124
- function renderMarkdownTerminal(text9) {
6182
+ function renderMarkdownTerminal(text10) {
6125
6183
  const width = process.stdout.columns || 80;
6126
6184
  const marked = new Marked(markedTerminal({ width, reflowText: true }));
6127
6185
  marked.use({
@@ -6134,7 +6192,7 @@ function renderMarkdownTerminal(text9) {
6134
6192
  }
6135
6193
  }
6136
6194
  });
6137
- return marked.parse(text9).trimEnd();
6195
+ return marked.parse(text10).trimEnd();
6138
6196
  }
6139
6197
 
6140
6198
  // src/commands/backlog/show/printActivity.ts
@@ -6154,9 +6212,9 @@ function groupActivityRefs(refs, commitLimit = ACTIVITY_COMMIT_LIMIT) {
6154
6212
  }
6155
6213
 
6156
6214
  // src/commands/backlog/show/printActivity.ts
6157
- function printRef(label2, text9, ref) {
6215
+ function printRef(label2, text10, ref) {
6158
6216
  const url = ref.url ? ` ${chalk48.dim(ref.url)}` : "";
6159
- console.log(` ${chalk48.cyan(label2)} ${text9}${url}`);
6217
+ console.log(` ${chalk48.cyan(label2)} ${text10}${url}`);
6160
6218
  }
6161
6219
  function printActivity(item) {
6162
6220
  const { branches, commits: commits2, prs: prs2, hiddenCommits } = groupActivityRefs(
@@ -6605,12 +6663,57 @@ function createFallbackHandler(routes3, htmlHandler2, extra) {
6605
6663
  import { eq as eq12 } from "drizzle-orm";
6606
6664
 
6607
6665
  // src/commands/backlog/loadItemSummaries.ts
6608
- import { asc as asc5, eq as eq11, ne, sql as sql5 } from "drizzle-orm";
6609
- async function loadItemSummaries(orm, origin) {
6610
- const incompleteCounts = orm.select({
6666
+ import { asc as asc5, eq as eq11 } from "drizzle-orm";
6667
+
6668
+ // src/commands/backlog/incompleteSubtaskCounts.ts
6669
+ import { ne, sql as sql5 } from "drizzle-orm";
6670
+ function incompleteSubtaskCounts(orm) {
6671
+ return orm.select({
6611
6672
  itemId: itemSubtasks.itemId,
6612
6673
  count: sql5`count(*)::int`.as("incomplete_subtasks")
6613
6674
  }).from(itemSubtasks).where(ne(itemSubtasks.status, "done")).groupBy(itemSubtasks.itemId).as("incomplete_counts");
6675
+ }
6676
+
6677
+ // src/commands/backlog/phaseUsageTotals.ts
6678
+ import { sql as sql6 } from "drizzle-orm";
6679
+ function phaseUsageTotals(orm) {
6680
+ return orm.select({
6681
+ itemId: phaseUsage.itemId,
6682
+ tokensUp: sql6`sum(${phaseUsage.tokensUp})::bigint`.as(
6683
+ "total_tokens_up"
6684
+ ),
6685
+ tokensDown: sql6`sum(${phaseUsage.tokensDown})::bigint`.as(
6686
+ "total_tokens_down"
6687
+ ),
6688
+ activeMs: sql6`sum(${phaseUsage.activeMs})::bigint`.as(
6689
+ "total_active_ms"
6690
+ )
6691
+ }).from(phaseUsage).groupBy(phaseUsage.itemId).as("usage_totals");
6692
+ }
6693
+
6694
+ // src/commands/backlog/rowToItemSummary.ts
6695
+ function rowToItemSummary(row) {
6696
+ return {
6697
+ id: row.id,
6698
+ origin: row.origin,
6699
+ type: row.type,
6700
+ name: row.name,
6701
+ status: row.status,
6702
+ starred: row.starred,
6703
+ jiraKey: row.jiraKey ?? void 0,
6704
+ incompleteSubtasks: row.incompleteSubtasks ?? 0,
6705
+ usageTotal: row.tokensUp == null ? void 0 : {
6706
+ tokensUp: Number(row.tokensUp),
6707
+ tokensDown: Number(row.tokensDown),
6708
+ activeMs: Number(row.activeMs)
6709
+ }
6710
+ };
6711
+ }
6712
+
6713
+ // src/commands/backlog/loadItemSummaries.ts
6714
+ async function loadItemSummaries(orm, origin) {
6715
+ const incompleteCounts = incompleteSubtaskCounts(orm);
6716
+ const usageTotals = phaseUsageTotals(orm);
6614
6717
  const rows = await orm.select({
6615
6718
  id: items.id,
6616
6719
  origin: items.origin,
@@ -6619,18 +6722,12 @@ async function loadItemSummaries(orm, origin) {
6619
6722
  status: items.status,
6620
6723
  starred: items.starred,
6621
6724
  jiraKey: items.jiraKey,
6622
- incompleteSubtasks: incompleteCounts.count
6623
- }).from(items).leftJoin(incompleteCounts, eq11(incompleteCounts.itemId, items.id)).where(origin === void 0 ? void 0 : eq11(items.origin, origin)).orderBy(asc5(items.id));
6624
- return rows.map((row) => ({
6625
- id: row.id,
6626
- origin: row.origin,
6627
- type: row.type,
6628
- name: row.name,
6629
- status: row.status,
6630
- starred: row.starred,
6631
- jiraKey: row.jiraKey ?? void 0,
6632
- incompleteSubtasks: row.incompleteSubtasks ?? 0
6633
- }));
6725
+ incompleteSubtasks: incompleteCounts.count,
6726
+ tokensUp: usageTotals.tokensUp,
6727
+ tokensDown: usageTotals.tokensDown,
6728
+ activeMs: usageTotals.activeMs
6729
+ }).from(items).leftJoin(incompleteCounts, eq11(incompleteCounts.itemId, items.id)).leftJoin(usageTotals, eq11(usageTotals.itemId, items.id)).where(origin === void 0 ? void 0 : eq11(items.origin, origin)).orderBy(asc5(items.id));
6730
+ return rows.map(rowToItemSummary);
6634
6731
  }
6635
6732
 
6636
6733
  // src/commands/backlog/loadBacklogSummaries.ts
@@ -7258,9 +7355,9 @@ function excerpt(xml, ...tags) {
7258
7355
  for (const tag of tags) {
7259
7356
  const raw = extractText(xml, tag);
7260
7357
  if (!raw) continue;
7261
- const text9 = stripHtml(raw);
7262
- if (text9.length <= MAX_EXCERPT) return text9;
7263
- return `${text9.slice(0, MAX_EXCERPT)}\u2026`;
7358
+ const text10 = stripHtml(raw);
7359
+ if (text10.length <= MAX_EXCERPT) return text10;
7360
+ return `${text10.slice(0, MAX_EXCERPT)}\u2026`;
7264
7361
  }
7265
7362
  return "";
7266
7363
  }
@@ -8173,10 +8270,10 @@ function registerAssociateJiraCommand(cmd) {
8173
8270
 
8174
8271
  // src/commands/backlog/comment/index.ts
8175
8272
  import chalk63 from "chalk";
8176
- async function comment(id, text9) {
8273
+ async function comment(id, text10) {
8177
8274
  const found = await findOneItem(id);
8178
8275
  if (!found) process.exit(1);
8179
- await appendComment(found.orm, found.item.id, text9);
8276
+ await appendComment(found.orm, found.item.id, text10);
8180
8277
  console.log(
8181
8278
  chalk63.green(`Comment added to item ${formatItemId(found.item.id)}.`)
8182
8279
  );
@@ -8281,9 +8378,9 @@ function readLine(dump, start3) {
8281
8378
  return { text: dump.subarray(start3, eol).toString("utf8"), next: eol + 1 };
8282
8379
  }
8283
8380
  function parseHeader(dump) {
8284
- const { text: text9, next: next3 } = readLine(dump, 0);
8381
+ const { text: text10, next: next3 } = readLine(dump, 0);
8285
8382
  try {
8286
- return { header: JSON.parse(text9), bodyStart: next3 };
8383
+ return { header: JSON.parse(text10), bodyStart: next3 };
8287
8384
  } catch {
8288
8385
  return invalid("header is not valid JSON.");
8289
8386
  }
@@ -8292,9 +8389,9 @@ function parseSections(dump, bodyStart) {
8292
8389
  const sections = /* @__PURE__ */ new Map();
8293
8390
  let cursor = bodyStart;
8294
8391
  while (cursor < dump.length) {
8295
- const { text: text9, next: next3 } = readLine(dump, cursor);
8296
- const match = text9.match(/^@table (\S+) (\d+)$/);
8297
- if (!match) invalid(`malformed table marker "${text9}".`);
8392
+ const { text: text10, next: next3 } = readLine(dump, cursor);
8393
+ const match = text10.match(/^@table (\S+) (\d+)$/);
8394
+ if (!match) invalid(`malformed table marker "${text10}".`);
8298
8395
  const [, name, bytes] = match;
8299
8396
  const end = next3 + Number(bytes);
8300
8397
  if (end > dump.length) invalid(`section "${name}" overruns the dump.`);
@@ -8370,8 +8467,8 @@ async function readStdinBuffer() {
8370
8467
  import { finished } from "stream/promises";
8371
8468
  import { from as copyFrom } from "pg-copy-streams";
8372
8469
  async function copyTableIn(client, table, data) {
8373
- const sql11 = `COPY ${table.name} (${table.columns.join(", ")}) FROM STDIN`;
8374
- const stream = client.query(copyFrom(sql11));
8470
+ const sql13 = `COPY ${table.name} (${table.columns.join(", ")}) FROM STDIN`;
8471
+ const stream = client.query(copyFrom(sql13));
8375
8472
  stream.end(data);
8376
8473
  await finished(stream);
8377
8474
  }
@@ -8416,8 +8513,8 @@ async function replaceData(client, parsed) {
8416
8513
  await copyTableIn(client, table, data);
8417
8514
  }
8418
8515
  for (const col of await introspectIdentityColumns(client)) {
8419
- const { text: text9, values } = resyncIdentitySql(col);
8420
- await client.query(text9, values);
8516
+ const { text: text10, values } = resyncIdentitySql(col);
8517
+ await client.query(text10, values);
8421
8518
  }
8422
8519
  }
8423
8520
  async function restore(client, parsed) {
@@ -8498,11 +8595,11 @@ async function insertPhaseAt(orm, itemId2, phaseIdx, name, tasks, manualChecks,
8498
8595
  }
8499
8596
 
8500
8597
  // src/commands/backlog/insertSubtask.ts
8501
- import { sql as sql6 } from "drizzle-orm";
8598
+ import { sql as sql7 } from "drizzle-orm";
8502
8599
  async function insertSubtask(orm, itemId2, title, description) {
8503
8600
  await orm.insert(itemSubtasks).values({
8504
8601
  itemId: itemId2,
8505
- idx: sql6`(SELECT COALESCE(MAX(${itemSubtasks.idx}) + 1, 0) FROM ${itemSubtasks} WHERE ${itemSubtasks.itemId} = ${itemId2})`,
8602
+ idx: sql7`(SELECT COALESCE(MAX(${itemSubtasks.idx}) + 1, 0) FROM ${itemSubtasks} WHERE ${itemSubtasks.itemId} = ${itemId2})`,
8506
8603
  title,
8507
8604
  description: description ?? null,
8508
8605
  status: "todo"
@@ -10264,8 +10361,8 @@ var RAW_BUILTIN_DENIES = [
10264
10361
  regex
10265
10362
  }))
10266
10363
  ];
10267
- function matchBuiltinDeny(text9) {
10268
- return RAW_BUILTIN_DENIES.find((rule) => rule.regex.test(text9));
10364
+ function matchBuiltinDeny(text10) {
10365
+ return RAW_BUILTIN_DENIES.find((rule) => rule.regex.test(text10));
10269
10366
  }
10270
10367
  function toDecision(rule) {
10271
10368
  if (!rule) return void 0;
@@ -11005,8 +11102,8 @@ function formatHuman(cli, commands) {
11005
11102
  `];
11006
11103
  for (const cmd of sorted) {
11007
11104
  const full = `${cli} ${cmd.path.join(" ")}`;
11008
- const text9 = cmd.description ? `${full} \u2014 ${cmd.description}` : full;
11009
- lines.push(`${prefix(classifyVerb(cmd.path))}${text9}`);
11105
+ const text10 = cmd.description ? `${full} \u2014 ${cmd.description}` : full;
11106
+ lines.push(`${prefix(classifyVerb(cmd.path))}${text10}`);
11010
11107
  }
11011
11108
  return lines.join("\n");
11012
11109
  }
@@ -11285,38 +11382,38 @@ import chalk108 from "chalk";
11285
11382
  // src/commands/codeComment/validateCommentText.ts
11286
11383
  var MAX_COMMENT_LENGTH = 50;
11287
11384
  function validateCommentText(raw, isHash = false) {
11288
- const text9 = isHash ? raw.replace(/^#\s?/, "") : raw.replace(/^\/\/\s?/, "");
11289
- if (/[\r\n]/.test(text9)) {
11385
+ const text10 = isHash ? raw.replace(/^#\s?/, "") : raw.replace(/^\/\/\s?/, "");
11386
+ if (/[\r\n]/.test(text10)) {
11290
11387
  return {
11291
11388
  ok: false,
11292
11389
  reason: "Comment must be a single line \u2014 multi-line comments are not allowed."
11293
11390
  };
11294
11391
  }
11295
- if (!isHash && (text9.includes("/*") || text9.includes("*/"))) {
11392
+ if (!isHash && (text10.includes("/*") || text10.includes("*/"))) {
11296
11393
  return {
11297
11394
  ok: false,
11298
11395
  reason: "Block comments (/* */) are not allowed \u2014 only single-line // comments."
11299
11396
  };
11300
11397
  }
11301
- if (text9.length > MAX_COMMENT_LENGTH) {
11398
+ if (text10.length > MAX_COMMENT_LENGTH) {
11302
11399
  return {
11303
11400
  ok: false,
11304
- reason: `Comment text is ${text9.length} chars; the cap is ${MAX_COMMENT_LENGTH}. Make it shorter or, better, make the code self-documenting.`
11401
+ reason: `Comment text is ${text10.length} chars; the cap is ${MAX_COMMENT_LENGTH}. Make it shorter or, better, make the code self-documenting.`
11305
11402
  };
11306
11403
  }
11307
- return { ok: true, text: text9 };
11404
+ return { ok: true, text: text10 };
11308
11405
  }
11309
11406
 
11310
11407
  // src/commands/codeComment/issuePin.ts
11311
11408
  import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync24 } from "fs";
11312
11409
  import { randomInt } from "crypto";
11313
- function issuePin(file, lineNumber, text9) {
11410
+ function issuePin(file, lineNumber, text10) {
11314
11411
  const pin = generatePin();
11315
11412
  mkdirSync13(getRestrictedDir(), { recursive: true });
11316
11413
  sweepRestrictedDir();
11317
11414
  writeFileSync24(
11318
11415
  getPinStatePath(pin),
11319
- JSON.stringify({ pin, file, line: lineNumber, text: text9 })
11416
+ JSON.stringify({ pin, file, line: lineNumber, text: text10 })
11320
11417
  );
11321
11418
  return showNotification({
11322
11419
  title: "assist code-comment pin",
@@ -11328,7 +11425,7 @@ function generatePin() {
11328
11425
  }
11329
11426
 
11330
11427
  // src/commands/codeComment/codeCommentSet.ts
11331
- function codeCommentSet(file, line, text9) {
11428
+ function codeCommentSet(file, line, text10) {
11332
11429
  const lineNumber = Number.parseInt(line, 10);
11333
11430
  if (!Number.isInteger(lineNumber) || lineNumber < 1) {
11334
11431
  console.error(chalk108.red(`Invalid line number: ${line}`));
@@ -11337,7 +11434,7 @@ function codeCommentSet(file, line, text9) {
11337
11434
  }
11338
11435
  const hash = isHashCommentFile(file);
11339
11436
  const marker = hash ? "#" : "//";
11340
- const validation = validateCommentText(text9, hash);
11437
+ const validation = validateCommentText(text10, hash);
11341
11438
  if (!validation.ok) {
11342
11439
  console.error(chalk108.red(`Refused: ${validation.reason}`));
11343
11440
  console.error(chalk108.red("No pin issued."));
@@ -13344,12 +13441,12 @@ function isSourceFile(filePath) {
13344
13441
  if (!filePath) return false;
13345
13442
  return SOURCE_EXTENSIONS.some((ext) => filePath.endsWith(ext));
13346
13443
  }
13347
- function blankNonNewline2(text9) {
13348
- return text9.replace(/[^\n]/g, " ");
13444
+ function blankNonNewline2(text10) {
13445
+ return text10.replace(/[^\n]/g, " ");
13349
13446
  }
13350
- function extractComments(text9) {
13447
+ function extractComments(text10) {
13351
13448
  const comments3 = [];
13352
- let work = text9.replace(
13449
+ let work = text10.replace(
13353
13450
  /"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\]|\\.)*`/g,
13354
13451
  blankNonNewline2
13355
13452
  );
@@ -13364,11 +13461,11 @@ function extractComments(text9) {
13364
13461
  }
13365
13462
 
13366
13463
  // src/commands/editHook/extractYamlComments.ts
13367
- function blankNonNewline3(text9) {
13368
- return text9.replace(/[^\n]/g, " ");
13464
+ function blankNonNewline3(text10) {
13465
+ return text10.replace(/[^\n]/g, " ");
13369
13466
  }
13370
- function extractYamlComments(text9) {
13371
- const work = text9.replace(
13467
+ function extractYamlComments(text10) {
13468
+ const work = text10.replace(
13372
13469
  /"(?:[^"\\]|\\.)*"|'(?:[^']|'')*'/g,
13373
13470
  blankNonNewline3
13374
13471
  );
@@ -13384,8 +13481,8 @@ function isHeaderLine2(line) {
13384
13481
  const trimmed = line.trim();
13385
13482
  return trimmed === "" || trimmed.startsWith("#");
13386
13483
  }
13387
- function extractShellComments(text9) {
13388
- const lines = text9.split("\n");
13484
+ function extractShellComments(text10) {
13485
+ const lines = text10.split("\n");
13389
13486
  let firstCodeLine = 0;
13390
13487
  while (firstCodeLine < lines.length && isHeaderLine2(lines[firstCodeLine])) {
13391
13488
  firstCodeLine++;
@@ -13755,9 +13852,9 @@ function registerGithub(program2) {
13755
13852
  }
13756
13853
 
13757
13854
  // src/commands/handover/countPendingHandovers.ts
13758
- import { and as and16, eq as eq34, isNull, sql as sql7 } from "drizzle-orm";
13855
+ import { and as and16, eq as eq34, isNull, sql as sql8 } from "drizzle-orm";
13759
13856
  async function countPendingHandovers(orm, origin) {
13760
- const [row] = await orm.select({ count: sql7`count(*)::int` }).from(handovers).where(and16(eq34(handovers.origin, origin), isNull(handovers.recalledAt)));
13857
+ const [row] = await orm.select({ count: sql8`count(*)::int` }).from(handovers).where(and16(eq34(handovers.origin, origin), isNull(handovers.recalledAt)));
13761
13858
  return row?.count ?? 0;
13762
13859
  }
13763
13860
 
@@ -13980,9 +14077,9 @@ import chalk140 from "chalk";
13980
14077
 
13981
14078
  // src/commands/jira/adfToText.ts
13982
14079
  function renderInline(node) {
13983
- const text9 = node.text ?? "";
13984
- if (node.marks?.some((m) => m.type === "code")) return `\`${text9}\``;
13985
- return text9;
14080
+ const text10 = node.text ?? "";
14081
+ if (node.marks?.some((m) => m.type === "code")) return `\`${text10}\``;
14082
+ return text10;
13986
14083
  }
13987
14084
  function renderChildren(node, indent2) {
13988
14085
  return renderNodes(node.content ?? [], indent2);
@@ -14024,8 +14121,8 @@ function isListNode(node) {
14024
14121
  function renderListChild(child, indent2, pad, marker, isFirst) {
14025
14122
  if (isListNode(child)) return renderNodes([child], indent2 + 1);
14026
14123
  if (child.type !== "paragraph") return renderNode(child, indent2);
14027
- const text9 = renderChildren(child, indent2);
14028
- return isFirst ? `${pad}${marker} ${text9}` : `${pad} ${text9}`;
14124
+ const text10 = renderChildren(child, indent2);
14125
+ return isFirst ? `${pad}${marker} ${text10}` : `${pad} ${text10}`;
14029
14126
  }
14030
14127
  function renderListItem(node, indent2, marker) {
14031
14128
  const pad = " ".repeat(indent2);
@@ -14184,9 +14281,9 @@ function registerDescriptionLaunch(program2, name, slashCommand, description, al
14184
14281
  "[description]",
14185
14282
  `Text to forward to the /${slashCommand} slash command`
14186
14283
  ).description(description).option("--once", "Exit when the initial task completes").option("--resume-session <id>", RESUME_SESSION_FLAG).action(
14187
- (text9, opts) => launchMode(slashCommand, {
14284
+ (text10, opts) => launchMode(slashCommand, {
14188
14285
  once: opts.once,
14189
- description: text9,
14286
+ description: text10,
14190
14287
  resumeSessionId: opts.resumeSession
14191
14288
  })
14192
14289
  );
@@ -14688,8 +14785,8 @@ function dedupeLinks(links2) {
14688
14785
  }
14689
14786
 
14690
14787
  // src/commands/netcap/linkifyMentions.ts
14691
- function linkifyMentions(text9, mentions) {
14692
- let out = text9;
14788
+ function linkifyMentions(text10, mentions) {
14789
+ let out = text10;
14693
14790
  for (const m of mentions) {
14694
14791
  if (m.name && out.includes(m.name)) {
14695
14792
  out = out.replace(m.name, `[${m.name}](${m.url})`);
@@ -14716,13 +14813,13 @@ function resolveAuthor(mentionMap, author) {
14716
14813
  return mentionMap.get(author) ?? { slug: author, url: profileUrl(author) };
14717
14814
  }
14718
14815
  function buildPost(raw, mentionMap, author) {
14719
- const text9 = joinText(raw.text);
14720
- if (!text9) return void 0;
14816
+ const text10 = joinText(raw.text);
14817
+ if (!text10) return void 0;
14721
14818
  const mentions = resolveMentions(raw, mentionMap, author);
14722
14819
  const relatedPosts = [...new Set(raw.related)];
14723
14820
  return {
14724
- text: text9,
14725
- markdown: linkifyMentions(text9, mentions),
14821
+ text: text10,
14822
+ markdown: linkifyMentions(text10, mentions),
14726
14823
  mentions,
14727
14824
  hashtags: [...new Set(raw.hashtags)],
14728
14825
  links: dedupeLinks(raw.links),
@@ -14863,8 +14960,8 @@ function activityUrnOf(update3) {
14863
14960
  return typeof urn === "string" ? urn : void 0;
14864
14961
  }
14865
14962
  function buildVoyagerPost(update3, profiles) {
14866
- const text9 = commentaryText(update3);
14867
- if (!text9) return void 0;
14963
+ const text10 = commentaryText(update3);
14964
+ if (!text10) return void 0;
14868
14965
  const author = resolveVoyagerAuthor(update3);
14869
14966
  const { mentions, hashtags, links: links2 } = collectVoyagerAttributes(
14870
14967
  update3,
@@ -14873,8 +14970,8 @@ function buildVoyagerPost(update3, profiles) {
14873
14970
  );
14874
14971
  const activityUrn = activityUrnOf(update3);
14875
14972
  return {
14876
- text: text9,
14877
- markdown: linkifyMentions(text9, mentions),
14973
+ text: text10,
14974
+ markdown: linkifyMentions(text10, mentions),
14878
14975
  mentions,
14879
14976
  hashtags,
14880
14977
  links: links2,
@@ -15378,8 +15475,8 @@ function isWallOfText(lines) {
15378
15475
  if (lines.some(isListLine)) {
15379
15476
  return false;
15380
15477
  }
15381
- const text9 = lines.join(" ").trim();
15382
- return text9.length > MAX_PARAGRAPH_CHARS || countSentences(text9) > MAX_PARAGRAPH_SENTENCES;
15478
+ const text10 = lines.join(" ").trim();
15479
+ return text10.length > MAX_PARAGRAPH_CHARS || countSentences(text10) > MAX_PARAGRAPH_SENTENCES;
15383
15480
  }
15384
15481
  function validatePrContent(title, body) {
15385
15482
  if (title.toLowerCase().includes("claude")) {
@@ -17133,9 +17230,9 @@ function resolveImports(target, dependencies, sourceFile, statements = []) {
17133
17230
  function extractTexts(target, allFunctions, statements) {
17134
17231
  const stmtTexts = statements.map((v) => v.getFullText().trim());
17135
17232
  const fnTexts = allFunctions.map((fn) => {
17136
- const text9 = fn.getFullText().trim();
17137
- if (fn === target && !text9.startsWith("export ")) return `export ${text9}`;
17138
- return text9;
17233
+ const text10 = fn.getFullText().trim();
17234
+ if (fn === target && !text10.startsWith("export ")) return `export ${text10}`;
17235
+ return text10;
17139
17236
  });
17140
17237
  return [...stmtTexts, ...fnTexts];
17141
17238
  }
@@ -18985,9 +19082,9 @@ var MultiSpinner = class {
18985
19082
  elapsedPrefix: prefix2
18986
19083
  });
18987
19084
  }
18988
- failRemaining(text9) {
19085
+ failRemaining(text10) {
18989
19086
  for (const entry of this.entries) {
18990
- if (entry.state === "running") this.resolve(entry, "failed", text9);
19087
+ if (entry.state === "running") this.resolve(entry, "failed", text10);
18991
19088
  }
18992
19089
  }
18993
19090
  add(entry) {
@@ -19000,14 +19097,14 @@ var MultiSpinner = class {
19000
19097
  set text(value) {
19001
19098
  entry.text = value;
19002
19099
  },
19003
- succeed: (text9) => this.resolve(entry, "succeeded", text9),
19004
- fail: (text9) => this.resolve(entry, "failed", text9)
19100
+ succeed: (text10) => this.resolve(entry, "succeeded", text10),
19101
+ fail: (text10) => this.resolve(entry, "failed", text10)
19005
19102
  };
19006
19103
  }
19007
- resolve(entry, state, text9) {
19104
+ resolve(entry, state, text10) {
19008
19105
  if (entry.state !== "running") return;
19009
19106
  entry.state = state;
19010
- if (text9 !== void 0) entry.text = text9;
19107
+ if (text10 !== void 0) entry.text = text10;
19011
19108
  entry.elapsedStart = void 0;
19012
19109
  this.render();
19013
19110
  this.maybeFinish();
@@ -19082,12 +19179,12 @@ function skippedCodexResult(outputPath) {
19082
19179
  // src/commands/review/formatReviewerFailure.ts
19083
19180
  var FAST_FAIL_MS = 1e3;
19084
19181
  var STDOUT_TAIL_LINES = 20;
19085
- function indent(text9) {
19086
- return text9.split(/\r?\n/).map((line) => ` ${line}`);
19182
+ function indent(text10) {
19183
+ return text10.split(/\r?\n/).map((line) => ` ${line}`);
19087
19184
  }
19088
- function tailLines(text9, maxLines) {
19089
- const lines = text9.split(/\r?\n/);
19090
- return lines.length <= maxLines ? text9 : lines.slice(-maxLines).join("\n");
19185
+ function tailLines(text10, maxLines) {
19186
+ const lines = text10.split(/\r?\n/);
19187
+ return lines.length <= maxLines ? text10 : lines.slice(-maxLines).join("\n");
19091
19188
  }
19092
19189
  function isFastFail(input) {
19093
19190
  return input.exitCode !== 0 && input.elapsedMs !== void 0 && input.elapsedMs < FAST_FAIL_MS;
@@ -20022,8 +20119,8 @@ function filterToSql(filter) {
20022
20119
  // src/commands/seq/fetchSeqData.ts
20023
20120
  function buildDataParams(filter, count8, from, to) {
20024
20121
  const sqlFilter = filterToSql(filter);
20025
- const sql11 = `select @Timestamp, @Level, @Exception, @Message from stream where ${sqlFilter} order by @Timestamp desc limit ${count8}`;
20026
- const params = new URLSearchParams({ q: sql11 });
20122
+ const sql13 = `select @Timestamp, @Level, @Exception, @Message from stream where ${sqlFilter} order by @Timestamp desc limit ${count8}`;
20123
+ const params = new URLSearchParams({ q: sql13 });
20027
20124
  if (from) params.set("rangeStartUtc", from);
20028
20125
  if (to) params.set("rangeEndUtc", to);
20029
20126
  return params;
@@ -20284,26 +20381,26 @@ import chalk187 from "chalk";
20284
20381
  // src/commands/sql/loadConnections.ts
20285
20382
  function loadConnections3() {
20286
20383
  const raw = loadGlobalConfigRaw();
20287
- const sql11 = raw.sql;
20288
- return sql11?.connections ?? [];
20384
+ const sql13 = raw.sql;
20385
+ return sql13?.connections ?? [];
20289
20386
  }
20290
20387
  function saveConnections3(connections) {
20291
20388
  const raw = loadGlobalConfigRaw();
20292
- const sql11 = raw.sql ?? {};
20293
- sql11.connections = connections;
20294
- raw.sql = sql11;
20389
+ const sql13 = raw.sql ?? {};
20390
+ sql13.connections = connections;
20391
+ raw.sql = sql13;
20295
20392
  saveGlobalConfig(raw);
20296
20393
  }
20297
20394
  function getDefaultConnection2() {
20298
20395
  const raw = loadGlobalConfigRaw();
20299
- const sql11 = raw.sql;
20300
- return sql11?.defaultConnection;
20396
+ const sql13 = raw.sql;
20397
+ return sql13?.defaultConnection;
20301
20398
  }
20302
20399
  function setDefaultConnection2(name) {
20303
20400
  const raw = loadGlobalConfigRaw();
20304
- const sql11 = raw.sql ?? {};
20305
- sql11.defaultConnection = name;
20306
- raw.sql = sql11;
20401
+ const sql13 = raw.sql ?? {};
20402
+ sql13.defaultConnection = name;
20403
+ raw.sql = sql13;
20307
20404
  saveGlobalConfig(raw);
20308
20405
  }
20309
20406
 
@@ -20374,9 +20471,9 @@ function resolveConnection3(name) {
20374
20471
  }
20375
20472
 
20376
20473
  // src/commands/sql/sqlConnect.ts
20377
- import sql8 from "mssql";
20474
+ import sql9 from "mssql";
20378
20475
  async function sqlConnect(conn) {
20379
- return await sql8.connect({
20476
+ return await sql9.connect({
20380
20477
  server: conn.server,
20381
20478
  port: conn.port,
20382
20479
  user: conn.user,
@@ -20453,11 +20550,11 @@ var MUTATION_PATTERN = new RegExp(
20453
20550
  `\\b(${MUTATION_KEYWORDS.join("|")})\\b`,
20454
20551
  "i"
20455
20552
  );
20456
- function stripComments(sql11) {
20457
- return sql11.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/--[^\n]*/g, " ");
20553
+ function stripComments(sql13) {
20554
+ return sql13.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/--[^\n]*/g, " ");
20458
20555
  }
20459
- function isMutation(sql11) {
20460
- const stripped = stripComments(sql11);
20556
+ function isMutation(sql13) {
20557
+ const stripped = stripComments(sql13);
20461
20558
  if (MUTATION_PATTERN.test(stripped)) return true;
20462
20559
  return /\bSELECT\b[\s\S]+\bINTO\s+\w/i.test(stripped);
20463
20560
  }
@@ -20656,8 +20753,8 @@ import {
20656
20753
  import { basename as basename10, join as join51 } from "path";
20657
20754
 
20658
20755
  // src/commands/transcript/cleanText.ts
20659
- function cleanText(text9) {
20660
- const words = text9.split(/\s+/);
20756
+ function cleanText(text10) {
20757
+ const words = text10.split(/\s+/);
20661
20758
  const cleaned = [];
20662
20759
  for (let i = 0; i < words.length; i++) {
20663
20760
  let isRepeat = false;
@@ -20677,8 +20774,8 @@ function cleanText(text9) {
20677
20774
  }
20678
20775
 
20679
20776
  // src/commands/transcript/convert/parseVtt/deduplicateCues/removeSubstringDuplicates.ts
20680
- function normalizeText(text9) {
20681
- return text9.toLowerCase().trim();
20777
+ function normalizeText(text10) {
20778
+ return text10.toLowerCase().trim();
20682
20779
  }
20683
20780
  function checkSubstringRelation(textI, textJ) {
20684
20781
  if (textI.includes(textJ) && textI.length > textJ.length)
@@ -20807,13 +20904,13 @@ function parseTimestampLine(line) {
20807
20904
  return { startMs: parseTimestamp(startStr), endMs: parseTimestamp(endStr) };
20808
20905
  }
20809
20906
  function buildCue(startMs, endMs, fullText) {
20810
- const { speaker, text: text9 } = extractSpeaker(fullText);
20811
- return text9 ? { startMs, endMs, speaker, text: text9 } : null;
20907
+ const { speaker, text: text10 } = extractSpeaker(fullText);
20908
+ return text10 ? { startMs, endMs, speaker, text: text10 } : null;
20812
20909
  }
20813
20910
  function parseCueLine(lines, i) {
20814
20911
  const { startMs, endMs } = parseTimestampLine(lines[i]);
20815
- const { text: text9, nextIndex: nextIndex2 } = collectTextLines(lines, i + 1);
20816
- return { cue: buildCue(startMs, endMs, text9), nextIndex: nextIndex2 };
20912
+ const { text: text10, nextIndex: nextIndex2 } = collectTextLines(lines, i + 1);
20913
+ return { cue: buildCue(startMs, endMs, text10), nextIndex: nextIndex2 };
20817
20914
  }
20818
20915
  function isCueSeparator(line) {
20819
20916
  return line.trim().includes("-->");
@@ -21396,8 +21493,8 @@ async function exchangeToken(params) {
21396
21493
  body: body.toString()
21397
21494
  });
21398
21495
  if (!response.ok) {
21399
- const text9 = await response.text();
21400
- throw new Error(`Token exchange failed (${response.status}): ${text9}`);
21496
+ const text10 = await response.text();
21497
+ throw new Error(`Token exchange failed (${response.status}): ${text10}`);
21401
21498
  }
21402
21499
  return response.json();
21403
21500
  }
@@ -22531,10 +22628,22 @@ function broadcastSessions(sessions, clients, windowsSessions = [], active) {
22531
22628
  });
22532
22629
  }
22533
22630
 
22534
- // src/shared/db/recordUsagePeak.ts
22631
+ // src/shared/activeWindows.ts
22535
22632
  var WINDOWS = ["five_hour", "seven_day"];
22536
- async function recordUsagePeak(db, rateLimits) {
22633
+ function activeWindows(rateLimits) {
22634
+ if (!rateLimits) return [];
22635
+ const out = [];
22537
22636
  for (const window of WINDOWS) {
22637
+ const resetsAt = rateLimits[window]?.resets_at;
22638
+ if (typeof resetsAt === "number") out.push({ window, resetsAt });
22639
+ }
22640
+ return out;
22641
+ }
22642
+
22643
+ // src/shared/db/recordUsagePeak.ts
22644
+ var WINDOWS2 = ["five_hour", "seven_day"];
22645
+ async function recordUsagePeak(db, rateLimits) {
22646
+ for (const window of WINDOWS2) {
22538
22647
  const w = rateLimits[window];
22539
22648
  if (!w || typeof w.resets_at !== "number" || typeof w.used_percentage !== "number")
22540
22649
  continue;
@@ -22567,6 +22676,9 @@ var ClientHub = class extends Set {
22567
22676
  broadcast(this, { type: "limits", rateLimits });
22568
22677
  this.persistPeak?.(rateLimits);
22569
22678
  }
22679
+ currentWindows() {
22680
+ return activeWindows(this.latestLimits);
22681
+ }
22570
22682
  greet(client) {
22571
22683
  if (this.latestLimits) {
22572
22684
  sendTo(client, { type: "limits", rateLimits: this.latestLimits });
@@ -22781,13 +22893,13 @@ function logSpawnedSession(session) {
22781
22893
  }
22782
22894
 
22783
22895
  // src/shared/db/recordPhaseActiveMs.ts
22784
- import { sql as sql9 } from "drizzle-orm";
22896
+ import { sql as sql10 } from "drizzle-orm";
22785
22897
  async function recordPhaseActiveMs(db, itemId2, phaseIdx, activeMs) {
22786
22898
  if (activeMs <= 0) return;
22787
22899
  await db.insert(phaseUsage).values({ itemId: itemId2, phaseIdx, activeMs }).onConflictDoUpdate({
22788
22900
  target: [phaseUsage.itemId, phaseUsage.phaseIdx],
22789
22901
  set: {
22790
- activeMs: sql9`${phaseUsage.activeMs} + excluded.active_ms`
22902
+ activeMs: sql10`${phaseUsage.activeMs} + excluded.active_ms`
22791
22903
  }
22792
22904
  });
22793
22905
  }
@@ -22929,38 +23041,114 @@ function makeStatusChangeHandler(dismiss, notify2, reuseForRun) {
22929
23041
  return (s, status2, exitCode) => applyStatusChange(s, status2, exitCode, dismiss, notify2, reuseForRun);
22930
23042
  }
22931
23043
 
22932
- // src/shared/db/recordPhaseTokens.ts
22933
- import { sql as sql10 } from "drizzle-orm";
22934
- async function recordPhaseTokens(db, itemId2, phaseIdx, totalIn, totalOut) {
22935
- await db.insert(phaseUsage).values({ itemId: itemId2, phaseIdx, lastTotalIn: totalIn, lastTotalOut: totalOut }).onConflictDoUpdate({
22936
- target: [phaseUsage.itemId, phaseUsage.phaseIdx],
22937
- set: {
22938
- tokensDown: sql10`${phaseUsage.tokensDown} + CASE WHEN ${phaseUsage.lastTotalIn} IS NULL THEN 0 ELSE GREATEST(excluded.last_total_in - ${phaseUsage.lastTotalIn}, 0) END`,
22939
- tokensUp: sql10`${phaseUsage.tokensUp} + CASE WHEN ${phaseUsage.lastTotalOut} IS NULL THEN 0 ELSE GREATEST(excluded.last_total_out - ${phaseUsage.lastTotalOut}, 0) END`,
22940
- lastTotalIn: sql10`excluded.last_total_in`,
22941
- lastTotalOut: sql10`excluded.last_total_out`
22942
- }
23044
+ // src/shared/db/recordPhaseTranscriptUsage.ts
23045
+ import { sql as sql11 } from "drizzle-orm";
23046
+ async function recordPhaseTranscriptUsage(db, itemId2, phaseIdx, responses) {
23047
+ const byId = /* @__PURE__ */ new Map();
23048
+ for (const r of responses) byId.set(r.messageId, r);
23049
+ const unique = [...byId.values()];
23050
+ if (unique.length === 0) return { tokensUp: 0, tokensDown: 0 };
23051
+ return db.transaction(async (tx) => {
23052
+ const inserted = await tx.insert(phaseUsageMessages).values(unique.map((r) => ({ itemId: itemId2, phaseIdx, messageId: r.messageId }))).onConflictDoNothing().returning({ messageId: phaseUsageMessages.messageId });
23053
+ let tokensUp = 0;
23054
+ let tokensDown = 0;
23055
+ for (const { messageId } of inserted) {
23056
+ const r = byId.get(messageId);
23057
+ if (!r) continue;
23058
+ tokensUp += r.outputTokens;
23059
+ tokensDown += r.inputTokens;
23060
+ }
23061
+ if (tokensUp <= 0 && tokensDown <= 0) return { tokensUp: 0, tokensDown: 0 };
23062
+ await tx.insert(phaseUsage).values({ itemId: itemId2, phaseIdx, tokensUp, tokensDown }).onConflictDoUpdate({
23063
+ target: [phaseUsage.itemId, phaseUsage.phaseIdx],
23064
+ set: {
23065
+ tokensUp: sql11`${phaseUsage.tokensUp} + ${tokensUp}`,
23066
+ tokensDown: sql11`${phaseUsage.tokensDown} + ${tokensDown}`
23067
+ }
23068
+ });
23069
+ return { tokensUp, tokensDown };
22943
23070
  });
22944
23071
  }
22945
23072
 
23073
+ // src/shared/db/recordWindowTokens.ts
23074
+ import { and as and19, eq as eq37, sql as sql12 } from "drizzle-orm";
23075
+ async function recordWindowTokens(db, window, resetsAt, tokensUp, tokensDown) {
23076
+ if (tokensUp <= 0 && tokensDown <= 0) return;
23077
+ await db.update(usagePeaks).set({
23078
+ tokensUp: sql12`${usagePeaks.tokensUp} + ${tokensUp}`,
23079
+ tokensDown: sql12`${usagePeaks.tokensDown} + ${tokensDown}`
23080
+ }).where(
23081
+ and19(
23082
+ eq37(usagePeaks.window, window),
23083
+ eq37(usagePeaks.resetsAt, resetsAt),
23084
+ eq37(usagePeaks.segment, 0)
23085
+ )
23086
+ );
23087
+ }
23088
+
23089
+ // src/commands/sessions/shared/transcriptUsage.ts
23090
+ import * as fs31 from "fs";
23091
+ function transcriptUsage(lines) {
23092
+ const byId = /* @__PURE__ */ new Map();
23093
+ for (const line of lines) {
23094
+ if (!line.trim()) continue;
23095
+ let entry;
23096
+ try {
23097
+ entry = JSON.parse(line);
23098
+ } catch {
23099
+ continue;
23100
+ }
23101
+ if (entry.type !== "assistant") continue;
23102
+ const id = entry.message?.id;
23103
+ const usage = entry.message?.usage;
23104
+ if (!id || !usage) continue;
23105
+ byId.set(id, {
23106
+ messageId: id,
23107
+ inputTokens: usage.input_tokens ?? 0,
23108
+ outputTokens: usage.output_tokens ?? 0
23109
+ });
23110
+ }
23111
+ return [...byId.values()];
23112
+ }
23113
+ async function readTranscriptUsage(transcriptPath) {
23114
+ const content = await fs31.promises.readFile(transcriptPath, "utf8");
23115
+ return transcriptUsage(content.split("\n"));
23116
+ }
23117
+
22946
23118
  // src/commands/sessions/daemon/persistPhaseTokens.ts
22947
- async function persistPhaseTokens(itemId2, phaseIdx, totalIn, totalOut) {
23119
+ async function persistPhaseTokens(itemId2, phaseIdx, transcriptPath, windows) {
22948
23120
  try {
22949
23121
  if (!process.env.ASSIST_DATABASE_URL && !loadConfig().database.url) return;
22950
- await recordPhaseTokens(await getDb(), itemId2, phaseIdx, totalIn, totalOut);
23122
+ const responses = await readTranscriptUsage(transcriptPath);
23123
+ if (responses.length === 0) return;
23124
+ const db = await getDb();
23125
+ const { tokensUp, tokensDown } = await recordPhaseTranscriptUsage(
23126
+ db,
23127
+ itemId2,
23128
+ phaseIdx,
23129
+ responses
23130
+ );
23131
+ for (const { window, resetsAt } of windows) {
23132
+ await recordWindowTokens(db, window, resetsAt, tokensUp, tokensDown);
23133
+ }
22951
23134
  } catch (error) {
22952
23135
  daemonLog(`phase-tokens persist failed: ${String(error)}`);
22953
23136
  }
22954
23137
  }
22955
23138
 
22956
23139
  // src/commands/sessions/daemon/recordSessionUsage.ts
22957
- function recordSessionUsage(sessions, claudeSessionId, totalIn, totalOut, usedPct) {
23140
+ function recordSessionUsage(sessions, claudeSessionId, transcriptPath, usedPct, windows) {
22958
23141
  for (const session of sessions) {
22959
23142
  if (session.claudeSessionId !== claudeSessionId) continue;
22960
23143
  session.usedPct = usedPct;
22961
23144
  const phase = sessionBacklogPhase(session);
22962
- if (phase)
22963
- void persistPhaseTokens(phase.itemId, phase.phaseIdx, totalIn, totalOut);
23145
+ if (phase && transcriptPath)
23146
+ void persistPhaseTokens(
23147
+ phase.itemId,
23148
+ phase.phaseIdx,
23149
+ transcriptPath,
23150
+ windows
23151
+ );
22964
23152
  return true;
22965
23153
  }
22966
23154
  return false;
@@ -22982,10 +23170,10 @@ function applyReviewPause(session, activity2) {
22982
23170
  }
22983
23171
 
22984
23172
  // src/shared/db/getPhaseActiveMs.ts
22985
- import { and as and19, eq as eq37 } from "drizzle-orm";
23173
+ import { and as and20, eq as eq38 } from "drizzle-orm";
22986
23174
  async function getPhaseActiveMs(db, itemId2, phaseIdx) {
22987
23175
  const [row] = await db.select({ activeMs: phaseUsage.activeMs }).from(phaseUsage).where(
22988
- and19(eq37(phaseUsage.itemId, itemId2), eq37(phaseUsage.phaseIdx, phaseIdx))
23176
+ and20(eq38(phaseUsage.itemId, itemId2), eq38(phaseUsage.phaseIdx, phaseIdx))
22989
23177
  );
22990
23178
  return row?.activeMs ?? 0;
22991
23179
  }
@@ -24249,13 +24437,13 @@ var SessionManager = class {
24249
24437
  }
24250
24438
  /* why: the status line relays token totals keyed by Claude's session id; join
24251
24439
  * it to the running backlog phase so the spend accumulates against that row. */
24252
- recordUsage(claudeSessionId, totalIn, totalOut, usedPct) {
24440
+ recordUsage(claudeSessionId, transcriptPath, usedPct) {
24253
24441
  if (recordSessionUsage(
24254
24442
  this.sessions.values(),
24255
24443
  claudeSessionId,
24256
- totalIn,
24257
- totalOut,
24258
- usedPct
24444
+ transcriptPath,
24445
+ usedPct,
24446
+ this.clients.currentWindows()
24259
24447
  ))
24260
24448
  this.notify();
24261
24449
  }
@@ -24279,12 +24467,12 @@ import * as net3 from "net";
24279
24467
  import { createInterface as createInterface9 } from "readline";
24280
24468
 
24281
24469
  // src/commands/sessions/shared/discoverSessions.ts
24282
- import * as fs32 from "fs";
24470
+ import * as fs33 from "fs";
24283
24471
  import * as os from "os";
24284
24472
  import * as path52 from "path";
24285
24473
 
24286
24474
  // src/commands/sessions/shared/parseSessionFile.ts
24287
- import * as fs31 from "fs";
24475
+ import * as fs32 from "fs";
24288
24476
  import * as path51 from "path";
24289
24477
 
24290
24478
  // src/commands/sessions/shared/deriveHistoryFields.ts
@@ -24315,8 +24503,8 @@ function deriveSessionType(commandName, name) {
24315
24503
  }
24316
24504
 
24317
24505
  // src/commands/sessions/shared/backlogRunMarkers.ts
24318
- function backlogRunMarkers(text9) {
24319
- const match = text9.match(/backlog item a(\d+):[ \t]*([^\n]*)/);
24506
+ function backlogRunMarkers(text10) {
24507
+ const match = text10.match(/backlog item a(\d+):[ \t]*([^\n]*)/);
24320
24508
  if (!match) return { commandName: "", commandArgs: "" };
24321
24509
  const title = match[2].trim();
24322
24510
  return {
@@ -24370,11 +24558,11 @@ function messageText(entry) {
24370
24558
  const content = msg?.content;
24371
24559
  return typeof content === "string" ? content : Array.isArray(content) ? content.find((c) => c.type === "text")?.text ?? "" : "";
24372
24560
  }
24373
- function stripMarkers(text9) {
24374
- return text9.replace(/<command-[^>]*>[^<]*<\/command-[^>]*>/g, "").trim().slice(0, 80);
24561
+ function stripMarkers(text10) {
24562
+ return text10.replace(/<command-[^>]*>[^<]*<\/command-[^>]*>/g, "").trim().slice(0, 80);
24375
24563
  }
24376
- function matchMarker(text9, tag) {
24377
- const match = text9.match(new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`));
24564
+ function matchMarker(text10, tag) {
24565
+ const match = text10.match(new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`));
24378
24566
  return match ? match[1].trim() : "";
24379
24567
  }
24380
24568
 
@@ -24382,10 +24570,10 @@ function matchMarker(text9, tag) {
24382
24570
  async function parseSessionFile(filePath, origin = "wsl") {
24383
24571
  let handle;
24384
24572
  try {
24385
- handle = await fs31.promises.open(filePath, "r");
24573
+ handle = await fs32.promises.open(filePath, "r");
24386
24574
  const meta = extractSessionMeta(await readHeadLines(handle));
24387
24575
  if (!meta.sessionId) return null;
24388
- const timestamp5 = meta.timestamp || (await fs31.promises.stat(filePath)).mtime.toISOString();
24576
+ const timestamp5 = meta.timestamp || (await fs32.promises.stat(filePath)).mtime.toISOString();
24389
24577
  return {
24390
24578
  sessionId: meta.sessionId,
24391
24579
  name: meta.name || `Session ${meta.sessionId.slice(0, 8)}`,
@@ -24431,7 +24619,7 @@ async function discoverSessionJsonlPaths() {
24431
24619
  sessionRoots().map(async ({ dir, origin }) => {
24432
24620
  let projectDirs;
24433
24621
  try {
24434
- projectDirs = await fs32.promises.readdir(dir);
24622
+ projectDirs = await fs33.promises.readdir(dir);
24435
24623
  } catch {
24436
24624
  return;
24437
24625
  }
@@ -24440,7 +24628,7 @@ async function discoverSessionJsonlPaths() {
24440
24628
  const dirPath = path52.join(dir, dirName);
24441
24629
  let entries;
24442
24630
  try {
24443
- entries = await fs32.promises.readdir(dirPath);
24631
+ entries = await fs33.promises.readdir(dirPath);
24444
24632
  } catch {
24445
24633
  return;
24446
24634
  }
@@ -24473,7 +24661,7 @@ async function discoverSessions() {
24473
24661
  }
24474
24662
 
24475
24663
  // src/commands/sessions/shared/parseTranscript.ts
24476
- import * as fs33 from "fs";
24664
+ import * as fs34 from "fs";
24477
24665
 
24478
24666
  // src/commands/sessions/shared/toolTarget.ts
24479
24667
  function toolTarget(input) {
@@ -24492,17 +24680,17 @@ function entryMessages(entry) {
24492
24680
  return [];
24493
24681
  }
24494
24682
  function userMessages(content) {
24495
- if (typeof content === "string") return text8("user", cleanUserText(content));
24683
+ if (typeof content === "string") return text9("user", cleanUserText(content));
24496
24684
  if (!Array.isArray(content)) return [];
24497
- return content.filter((c) => c.type === "text").flatMap((c) => text8("user", cleanUserText(c.text ?? "")));
24685
+ return content.filter((c) => c.type === "text").flatMap((c) => text9("user", cleanUserText(c.text ?? "")));
24498
24686
  }
24499
24687
  function assistantMessages(content) {
24500
- if (typeof content === "string") return text8("assistant", content.trim());
24688
+ if (typeof content === "string") return text9("assistant", content.trim());
24501
24689
  if (!Array.isArray(content)) return [];
24502
24690
  return content.flatMap((c) => assistantItem(c));
24503
24691
  }
24504
24692
  function assistantItem(c) {
24505
- if (c.type === "text") return text8("assistant", (c.text ?? "").trim());
24693
+ if (c.type === "text") return text9("assistant", (c.text ?? "").trim());
24506
24694
  if (c.type === "tool_use")
24507
24695
  return [
24508
24696
  {
@@ -24513,7 +24701,7 @@ function assistantItem(c) {
24513
24701
  ];
24514
24702
  return [];
24515
24703
  }
24516
- function text8(role, value) {
24704
+ function text9(role, value) {
24517
24705
  return value ? [{ role, text: value }] : [];
24518
24706
  }
24519
24707
  function cleanUserText(value) {
@@ -24540,7 +24728,7 @@ async function parseTranscript(sessionId) {
24540
24728
  const filePath = await findSessionJsonlPath(sessionId);
24541
24729
  if (!filePath) return [];
24542
24730
  try {
24543
- const raw = await fs33.promises.readFile(filePath, "utf8");
24731
+ const raw = await fs34.promises.readFile(filePath, "utf8");
24544
24732
  return parseTranscriptLines(raw.split("\n"));
24545
24733
  } catch {
24546
24734
  return [];
@@ -24638,8 +24826,7 @@ var messageHandlers = {
24638
24826
  limits: (_client, m, d) => m.clients.updateLimits(d.rateLimits),
24639
24827
  usage: (_client, m, d) => m.recordUsage(
24640
24828
  d.claudeSessionId,
24641
- d.totalIn,
24642
- d.totalOut,
24829
+ d.transcriptPath,
24643
24830
  d.usedPct
24644
24831
  ),
24645
24832
  input: routed(
@@ -24850,17 +25037,17 @@ function registerDaemon(program2) {
24850
25037
  }
24851
25038
 
24852
25039
  // src/commands/sessions/summarise/index.ts
24853
- import * as fs36 from "fs";
25040
+ import * as fs37 from "fs";
24854
25041
  import chalk193 from "chalk";
24855
25042
 
24856
25043
  // src/commands/sessions/summarise/shared.ts
24857
- import * as fs34 from "fs";
25044
+ import * as fs35 from "fs";
24858
25045
  function writeSummary(jsonlPath2, summary) {
24859
- fs34.writeFileSync(summaryPathFor(jsonlPath2), `${summary.trim()}
25046
+ fs35.writeFileSync(summaryPathFor(jsonlPath2), `${summary.trim()}
24860
25047
  `, "utf8");
24861
25048
  }
24862
25049
  function hasSummary(jsonlPath2) {
24863
- return fs34.existsSync(summaryPathFor(jsonlPath2));
25050
+ return fs35.existsSync(summaryPathFor(jsonlPath2));
24864
25051
  }
24865
25052
  function summaryPathFor(jsonlPath2) {
24866
25053
  return jsonlPath2.replace(/\.jsonl$/, ".summary");
@@ -24870,7 +25057,7 @@ function summaryPathFor(jsonlPath2) {
24870
25057
  import { execFileSync as execFileSync10 } from "child_process";
24871
25058
 
24872
25059
  // src/commands/sessions/summarise/iterateUserMessages.ts
24873
- import * as fs35 from "fs";
25060
+ import * as fs36 from "fs";
24874
25061
 
24875
25062
  // src/commands/sessions/summarise/parseUserLine.ts
24876
25063
  function parseUserLine(line) {
@@ -24883,16 +25070,16 @@ function parseUserLine(line) {
24883
25070
  if (entry.type !== "user") return void 0;
24884
25071
  const msg = entry.message;
24885
25072
  const c = msg?.content;
24886
- let text9;
25073
+ let text10;
24887
25074
  if (typeof c === "string") {
24888
- text9 = c;
25075
+ text10 = c;
24889
25076
  } else if (Array.isArray(c)) {
24890
25077
  const collected = c.filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n");
24891
- text9 = collected || void 0;
25078
+ text10 = collected || void 0;
24892
25079
  }
24893
- if (!text9) return void 0;
25080
+ if (!text10) return void 0;
24894
25081
  return {
24895
- text: text9,
25082
+ text: text10,
24896
25083
  entrypoint: typeof entry.entrypoint === "string" ? entry.entrypoint : void 0
24897
25084
  };
24898
25085
  }
@@ -24901,13 +25088,13 @@ function parseUserLine(line) {
24901
25088
  function* iterateUserMessages(filePath, maxBytes = 65536) {
24902
25089
  let content;
24903
25090
  try {
24904
- const fd = fs35.openSync(filePath, "r");
25091
+ const fd = fs36.openSync(filePath, "r");
24905
25092
  try {
24906
25093
  const buf = Buffer.alloc(maxBytes);
24907
- const bytesRead = fs35.readSync(fd, buf, 0, buf.length, 0);
25094
+ const bytesRead = fs36.readSync(fd, buf, 0, buf.length, 0);
24908
25095
  content = buf.toString("utf8", 0, bytesRead);
24909
25096
  } finally {
24910
- fs35.closeSync(fd);
25097
+ fs36.closeSync(fd);
24911
25098
  }
24912
25099
  } catch {
24913
25100
  return;
@@ -24921,13 +25108,13 @@ function* iterateUserMessages(filePath, maxBytes = 65536) {
24921
25108
 
24922
25109
  // src/commands/sessions/summarise/extractFirstUserMessage.ts
24923
25110
  function extractFirstUserMessage(filePath) {
24924
- for (const text9 of iterateUserMessages(filePath)) {
24925
- return truncate3(text9);
25111
+ for (const text10 of iterateUserMessages(filePath)) {
25112
+ return truncate3(text10);
24926
25113
  }
24927
25114
  return void 0;
24928
25115
  }
24929
- function truncate3(text9, maxChars = 500) {
24930
- const trimmed = text9.trim();
25116
+ function truncate3(text10, maxChars = 500) {
25117
+ const trimmed = text10.trim();
24931
25118
  if (trimmed.length <= maxChars) return trimmed;
24932
25119
  return `${trimmed.slice(0, maxChars)}\u2026`;
24933
25120
  }
@@ -24935,28 +25122,28 @@ function truncate3(text9, maxChars = 500) {
24935
25122
  // src/commands/sessions/summarise/scanSessionBacklogRefs.ts
24936
25123
  function scanSessionBacklogRefs(filePath) {
24937
25124
  const ids = /* @__PURE__ */ new Set();
24938
- for (const text9 of iterateUserMessages(filePath, Number.MAX_SAFE_INTEGER)) {
24939
- for (const id of extractBacklogIds(text9)) {
25125
+ for (const text10 of iterateUserMessages(filePath, Number.MAX_SAFE_INTEGER)) {
25126
+ for (const id of extractBacklogIds(text10)) {
24940
25127
  ids.add(id);
24941
25128
  }
24942
25129
  }
24943
25130
  return [...ids].sort((a, b) => a - b);
24944
25131
  }
24945
- function extractBacklogIds(text9) {
25132
+ function extractBacklogIds(text10) {
24946
25133
  const ids = [];
24947
- for (const m of text9.matchAll(/backlog\s+run\s+a(\d+)/gi)) {
25134
+ for (const m of text10.matchAll(/backlog\s+run\s+a(\d+)/gi)) {
24948
25135
  ids.push(Number.parseInt(m[1], 10));
24949
25136
  }
24950
- for (const m of text9.matchAll(/backlog\s+(?:item\s+)?a(\d+)/gi)) {
25137
+ for (const m of text10.matchAll(/backlog\s+(?:item\s+)?a(\d+)/gi)) {
24951
25138
  ids.push(Number.parseInt(m[1], 10));
24952
25139
  }
24953
- for (const m of text9.matchAll(/backlog\s+phase-done\s+a(\d+)/gi)) {
25140
+ for (const m of text10.matchAll(/backlog\s+phase-done\s+a(\d+)/gi)) {
24954
25141
  ids.push(Number.parseInt(m[1], 10));
24955
25142
  }
24956
- for (const m of text9.matchAll(/backlog\s+comment\s+a(\d+)/gi)) {
25143
+ for (const m of text10.matchAll(/backlog\s+comment\s+a(\d+)/gi)) {
24957
25144
  ids.push(Number.parseInt(m[1], 10));
24958
25145
  }
24959
- for (const m of text9.matchAll(/(?:^|[\s(])a(\d{1,4})(?=[\s).,;:!?]|$)/gm)) {
25146
+ for (const m of text10.matchAll(/(?:^|[\s(])a(\d{1,4})(?=[\s).,;:!?]|$)/gm)) {
24960
25147
  ids.push(Number.parseInt(m[1], 10));
24961
25148
  }
24962
25149
  return ids;
@@ -25027,7 +25214,7 @@ function selectCandidates(files, options2) {
25027
25214
  const candidates = options2.force ? files : files.filter((f) => !hasSummary(f));
25028
25215
  candidates.sort((a, b) => {
25029
25216
  try {
25030
- return fs36.statSync(b).mtimeMs - fs36.statSync(a).mtimeMs;
25217
+ return fs37.statSync(b).mtimeMs - fs37.statSync(a).mtimeMs;
25031
25218
  } catch {
25032
25219
  return 0;
25033
25220
  }
@@ -25207,14 +25394,13 @@ async function relayRateLimits(rateLimits) {
25207
25394
  }
25208
25395
 
25209
25396
  // src/commands/relayUsage.ts
25210
- async function relayUsage(claudeSessionId, totalIn, totalOut, usedPct) {
25397
+ async function relayUsage(claudeSessionId, transcriptPath, usedPct) {
25211
25398
  if (!claudeSessionId) return;
25212
25399
  try {
25213
25400
  await sendToDaemon({
25214
25401
  type: "usage",
25215
25402
  claudeSessionId,
25216
- totalIn,
25217
- totalOut,
25403
+ transcriptPath,
25218
25404
  usedPct
25219
25405
  });
25220
25406
  } catch {
@@ -25247,28 +25433,27 @@ async function statusLine() {
25247
25433
  await relayRateLimits(data.rate_limits);
25248
25434
  await relayUsage(
25249
25435
  data.session_id,
25250
- totalIn,
25251
- totalOut,
25436
+ data.transcript_path,
25252
25437
  data.context_window.used_percentage
25253
25438
  );
25254
25439
  }
25255
25440
 
25256
25441
  // src/commands/sync.ts
25257
- import * as fs39 from "fs";
25442
+ import * as fs40 from "fs";
25258
25443
  import * as os2 from "os";
25259
25444
  import * as path56 from "path";
25260
25445
  import { fileURLToPath as fileURLToPath7 } from "url";
25261
25446
 
25262
25447
  // src/commands/sync/syncClaudeMd.ts
25263
- import * as fs37 from "fs";
25448
+ import * as fs38 from "fs";
25264
25449
  import * as path54 from "path";
25265
25450
  import chalk196 from "chalk";
25266
25451
  async function syncClaudeMd(claudeDir, targetBase, options2) {
25267
25452
  const source = path54.join(claudeDir, "CLAUDE.md");
25268
25453
  const target = path54.join(targetBase, "CLAUDE.md");
25269
- const sourceContent = fs37.readFileSync(source, "utf8");
25270
- if (fs37.existsSync(target)) {
25271
- const targetContent = fs37.readFileSync(target, "utf8");
25454
+ const sourceContent = fs38.readFileSync(source, "utf8");
25455
+ if (fs38.existsSync(target)) {
25456
+ const targetContent = fs38.readFileSync(target, "utf8");
25272
25457
  if (sourceContent !== targetContent) {
25273
25458
  console.log(
25274
25459
  chalk196.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
@@ -25285,21 +25470,21 @@ async function syncClaudeMd(claudeDir, targetBase, options2) {
25285
25470
  }
25286
25471
  }
25287
25472
  }
25288
- fs37.copyFileSync(source, target);
25473
+ fs38.copyFileSync(source, target);
25289
25474
  console.log("Copied CLAUDE.md to ~/.claude/CLAUDE.md");
25290
25475
  }
25291
25476
 
25292
25477
  // src/commands/sync/syncSettings.ts
25293
- import * as fs38 from "fs";
25478
+ import * as fs39 from "fs";
25294
25479
  import * as path55 from "path";
25295
25480
  import chalk197 from "chalk";
25296
25481
  async function syncSettings(claudeDir, targetBase, options2) {
25297
25482
  const source = path55.join(claudeDir, "settings.json");
25298
25483
  const target = path55.join(targetBase, "settings.json");
25299
- const sourceContent = fs38.readFileSync(source, "utf8");
25484
+ const sourceContent = fs39.readFileSync(source, "utf8");
25300
25485
  const sourceSettings = JSON.parse(sourceContent);
25301
- const targetExists = fs38.existsSync(target);
25302
- const targetContent = targetExists ? fs38.readFileSync(target, "utf8") : "";
25486
+ const targetExists = fs39.existsSync(target);
25487
+ const targetContent = targetExists ? fs39.readFileSync(target, "utf8") : "";
25303
25488
  const preservedUserSettings = targetExists ? JSON.parse(targetContent) : {};
25304
25489
  const mergedContent = JSON.stringify(
25305
25490
  { ...preservedUserSettings, ...sourceSettings },
@@ -25328,7 +25513,7 @@ async function syncSettings(claudeDir, targetBase, options2) {
25328
25513
  }
25329
25514
  }
25330
25515
  }
25331
- fs38.writeFileSync(target, mergedContent);
25516
+ fs39.writeFileSync(target, mergedContent);
25332
25517
  console.log("Copied settings.json to ~/.claude/settings.json");
25333
25518
  }
25334
25519
 
@@ -25347,10 +25532,10 @@ async function sync(options2) {
25347
25532
  function syncCommands(claudeDir, targetBase) {
25348
25533
  const sourceDir = path56.join(claudeDir, "commands");
25349
25534
  const targetDir = path56.join(targetBase, "commands");
25350
- fs39.mkdirSync(targetDir, { recursive: true });
25351
- const files = fs39.readdirSync(sourceDir);
25535
+ fs40.mkdirSync(targetDir, { recursive: true });
25536
+ const files = fs40.readdirSync(sourceDir);
25352
25537
  for (const file of files) {
25353
- fs39.copyFileSync(path56.join(sourceDir, file), path56.join(targetDir, file));
25538
+ fs40.copyFileSync(path56.join(sourceDir, file), path56.join(targetDir, file));
25354
25539
  console.log(`Copied ${file} to ${targetDir}`);
25355
25540
  }
25356
25541
  console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);