@staff0rd/assist 0.227.0 → 0.228.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.
Files changed (2) hide show
  1. package/dist/index.js +819 -800
  2. package/package.json +2 -1
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.227.0",
9
+ version: "0.228.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -44,6 +44,7 @@ var package_default = {
44
44
  chalk: "^5.6.2",
45
45
  commander: "^14.0.2",
46
46
  diff: "^8.0.2",
47
+ "drizzle-orm": "^0.45.2",
47
48
  enquirer: "^2.4.1",
48
49
  entities: "^7.0.1",
49
50
  "is-wsl": "^3.1.0",
@@ -413,21 +414,93 @@ function getTranscriptConfig() {
413
414
  return config.transcript;
414
415
  }
415
416
 
416
- // src/commands/backlog/deleteItemRelations.ts
417
- async function deleteItemRelations(db, itemId) {
418
- await db.run("DELETE FROM plan_tasks WHERE item_id = ?", [itemId]);
419
- await db.run("DELETE FROM plan_phases WHERE item_id = ?", [itemId]);
420
- await db.run("DELETE FROM comments WHERE item_id = ?", [itemId]);
421
- await db.run("DELETE FROM links WHERE item_id = ?", [itemId]);
422
- }
417
+ // src/commands/backlog/deleteItem.ts
418
+ import { eq } from "drizzle-orm";
419
+
420
+ // src/commands/backlog/backlogSchema.ts
421
+ import {
422
+ foreignKey,
423
+ index,
424
+ integer,
425
+ pgTable,
426
+ primaryKey,
427
+ text
428
+ } from "drizzle-orm/pg-core";
429
+ var items = pgTable(
430
+ "items",
431
+ {
432
+ id: integer().generatedByDefaultAsIdentity().primaryKey(),
433
+ origin: text().notNull(),
434
+ type: text().notNull().default("story"),
435
+ name: text().notNull(),
436
+ description: text(),
437
+ acceptanceCriteria: text("acceptance_criteria").notNull().default("[]"),
438
+ status: text().notNull().default("todo"),
439
+ currentPhase: integer("current_phase")
440
+ },
441
+ (t) => [index("items_origin_idx").on(t.origin)]
442
+ );
443
+ var comments = pgTable("comments", {
444
+ id: integer().generatedByDefaultAsIdentity().primaryKey(),
445
+ itemId: integer("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
446
+ idx: integer().notNull(),
447
+ text: text().notNull(),
448
+ phase: integer(),
449
+ timestamp: text().notNull(),
450
+ type: text().notNull().default("comment")
451
+ });
452
+ var links = pgTable(
453
+ "links",
454
+ {
455
+ itemId: integer("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
456
+ type: text().notNull(),
457
+ targetId: integer("target_id").notNull()
458
+ },
459
+ (t) => [primaryKey({ columns: [t.itemId, t.type, t.targetId] })]
460
+ );
461
+ var planPhases = pgTable(
462
+ "plan_phases",
463
+ {
464
+ itemId: integer("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
465
+ idx: integer().notNull(),
466
+ name: text().notNull(),
467
+ manualChecks: text("manual_checks")
468
+ },
469
+ (t) => [primaryKey({ columns: [t.itemId, t.idx] })]
470
+ );
471
+ var planTasks = pgTable(
472
+ "plan_tasks",
473
+ {
474
+ itemId: integer("item_id").notNull(),
475
+ phaseIdx: integer("phase_idx").notNull(),
476
+ idx: integer().notNull(),
477
+ task: text().notNull()
478
+ },
479
+ (t) => [
480
+ primaryKey({ columns: [t.itemId, t.phaseIdx, t.idx] }),
481
+ foreignKey({
482
+ columns: [t.itemId, t.phaseIdx],
483
+ foreignColumns: [planPhases.itemId, planPhases.idx]
484
+ }).onDelete("cascade")
485
+ ]
486
+ );
487
+ var metadata = pgTable("metadata", {
488
+ key: text().primaryKey(),
489
+ value: text().notNull()
490
+ });
491
+ var backlogSchema = {
492
+ items,
493
+ comments,
494
+ links,
495
+ planPhases,
496
+ planTasks,
497
+ metadata
498
+ };
423
499
 
424
500
  // src/commands/backlog/deleteItem.ts
425
- async function deleteItem(db, id) {
426
- return db.transaction(async (tx) => {
427
- await deleteItemRelations(tx, id);
428
- const result = await tx.run("DELETE FROM items WHERE id = ?", [id]);
429
- return result.changes > 0;
430
- });
501
+ async function deleteItem(orm, id) {
502
+ const [row] = await orm.delete(items).where(eq(items.id, id)).returning({ name: items.name });
503
+ return row?.name;
431
504
  }
432
505
 
433
506
  // src/commands/backlog/migrateLocalBacklog.ts
@@ -469,74 +542,70 @@ function gitPullBacklog(dir) {
469
542
  }
470
543
  }
471
544
 
545
+ // src/commands/backlog/itemColumns.ts
546
+ function itemColumns(item, origin) {
547
+ return {
548
+ origin,
549
+ type: item.type,
550
+ name: item.name,
551
+ description: item.description ?? null,
552
+ acceptanceCriteria: JSON.stringify(item.acceptanceCriteria),
553
+ status: item.status,
554
+ currentPhase: item.currentPhase ?? null
555
+ };
556
+ }
557
+
472
558
  // src/commands/backlog/insertItem.ts
473
559
  async function insertItem(db, item, origin) {
474
- const row = await db.get(
475
- `INSERT INTO items (origin, type, name, description, acceptance_criteria, status, current_phase)
476
- VALUES (?, ?, ?, ?, ?, ?, ?)
477
- RETURNING id`,
478
- [
479
- origin,
480
- item.type,
481
- item.name,
482
- item.description ?? null,
483
- JSON.stringify(item.acceptanceCriteria),
484
- item.status,
485
- item.currentPhase ?? null
486
- ]
487
- );
560
+ const [row] = await db.insert(items).values(itemColumns(item, origin)).returning({ id: items.id });
488
561
  if (!row) throw new Error("Failed to insert backlog item");
489
562
  return row.id;
490
563
  }
491
564
 
492
565
  // src/commands/backlog/insertItemRelations.ts
493
566
  async function insertComments(db, item) {
494
- if (!item.comments) return;
495
- for (let i = 0; i < item.comments.length; i++) {
496
- const c = item.comments[i];
497
- if (c.id !== void 0) {
498
- await db.run(
499
- "INSERT INTO comments (id, item_id, idx, text, phase, timestamp, type) VALUES (?, ?, ?, ?, ?, ?, ?)",
500
- [c.id, item.id, i, c.text, c.phase ?? null, c.timestamp, c.type]
501
- );
502
- } else {
503
- await db.run(
504
- "INSERT INTO comments (item_id, idx, text, phase, timestamp, type) VALUES (?, ?, ?, ?, ?, ?)",
505
- [item.id, i, c.text, c.phase ?? null, c.timestamp, c.type]
506
- );
507
- }
508
- }
567
+ if (!item.comments?.length) return;
568
+ await db.insert(comments).values(
569
+ item.comments.map((c, i) => ({
570
+ id: c.id,
571
+ itemId: item.id,
572
+ idx: i,
573
+ text: c.text,
574
+ phase: c.phase ?? null,
575
+ timestamp: c.timestamp,
576
+ type: c.type
577
+ }))
578
+ );
509
579
  }
510
580
  async function insertLinks(db, item) {
511
- if (!item.links) return;
512
- for (const l of item.links) {
513
- await db.run(
514
- "INSERT INTO links (item_id, type, target_id) VALUES (?, ?, ?)",
515
- [item.id, l.type, l.targetId]
516
- );
517
- }
581
+ if (!item.links?.length) return;
582
+ await db.insert(links).values(
583
+ item.links.map((l) => ({
584
+ itemId: item.id,
585
+ type: l.type,
586
+ targetId: l.targetId
587
+ }))
588
+ );
518
589
  }
519
590
  async function insertPlan(db, item) {
520
- if (!item.plan) return;
521
- for (let pi = 0; pi < item.plan.length; pi++) {
522
- const phase = item.plan[pi];
523
- await db.run(
524
- "INSERT INTO plan_phases (item_id, idx, name, manual_checks) VALUES (?, ?, ?, ?)",
525
- [
526
- item.id,
527
- pi,
528
- phase.name,
529
- phase.manualChecks ? JSON.stringify(phase.manualChecks) : null
530
- ]
531
- );
532
- for (let ti = 0; ti < phase.tasks.length; ti++) {
533
- const task = phase.tasks[ti];
534
- await db.run(
535
- "INSERT INTO plan_tasks (item_id, phase_idx, idx, task) VALUES (?, ?, ?, ?)",
536
- [item.id, pi, ti, task.task]
537
- );
538
- }
539
- }
591
+ if (!item.plan?.length) return;
592
+ await db.insert(planPhases).values(
593
+ item.plan.map((phase, pi) => ({
594
+ itemId: item.id,
595
+ idx: pi,
596
+ name: phase.name,
597
+ manualChecks: phase.manualChecks ? JSON.stringify(phase.manualChecks) : null
598
+ }))
599
+ );
600
+ const tasks = item.plan.flatMap(
601
+ (phase, pi) => phase.tasks.map((task, ti) => ({
602
+ itemId: item.id,
603
+ phaseIdx: pi,
604
+ idx: ti,
605
+ task: task.task
606
+ }))
607
+ );
608
+ if (tasks.length) await db.insert(planTasks).values(tasks);
540
609
  }
541
610
  async function insertItemRelations(db, item) {
542
611
  await insertComments(db, item);
@@ -559,98 +628,138 @@ function remap(item, newId, oldToNew) {
559
628
  };
560
629
  return remapped;
561
630
  }
562
- async function importItemsRemapped(db, items, origin) {
563
- return db.transaction(async (tx) => {
631
+ async function importItemsRemapped(orm, items2, origin) {
632
+ return orm.transaction(async (tx) => {
564
633
  const oldToNew = /* @__PURE__ */ new Map();
565
- for (const item of items) {
634
+ for (const item of items2) {
566
635
  oldToNew.set(item.id, await insertItem(tx, item, origin));
567
636
  }
568
- for (const item of items) {
637
+ for (const item of items2) {
569
638
  const newId = oldToNew.get(item.id);
570
639
  if (newId === void 0) continue;
571
640
  await insertItemRelations(tx, remap(item, newId, oldToNew));
572
641
  }
573
- return items.length;
642
+ return items2.length;
574
643
  });
575
644
  }
576
645
 
577
- // src/commands/backlog/loadComments.ts
578
- async function loadComments(db, itemId) {
579
- const rows = await db.all(
580
- "SELECT id, text, phase, timestamp, type FROM comments WHERE item_id = ? ORDER BY idx",
581
- [itemId]
582
- );
583
- return rows.map((r) => {
584
- const c = {
585
- id: r.id,
586
- text: r.text,
587
- timestamp: r.timestamp,
588
- type: r.type
589
- };
590
- if (r.phase != null) c.phase = r.phase;
591
- return c;
592
- });
646
+ // src/commands/backlog/loadAllItems.ts
647
+ import { asc as asc2, eq as eq2 } from "drizzle-orm";
648
+
649
+ // src/commands/backlog/loadRelations.ts
650
+ import { asc, inArray } from "drizzle-orm";
651
+ function groupByItem(rows) {
652
+ const map = /* @__PURE__ */ new Map();
653
+ for (const row of rows) {
654
+ const bucket = map.get(row.itemId);
655
+ if (bucket) bucket.push(row);
656
+ else map.set(row.itemId, [row]);
657
+ }
658
+ return map;
659
+ }
660
+ var selectComments = (orm, ids) => orm.select().from(comments).where(inArray(comments.itemId, ids)).orderBy(asc(comments.itemId), asc(comments.idx));
661
+ var selectLinks = (orm, ids) => orm.select().from(links).where(inArray(links.itemId, ids)).orderBy(asc(links.itemId));
662
+ var selectPhases = (orm, ids) => orm.select().from(planPhases).where(inArray(planPhases.itemId, ids)).orderBy(asc(planPhases.itemId), asc(planPhases.idx));
663
+ var selectTasks = (orm, ids) => orm.select().from(planTasks).where(inArray(planTasks.itemId, ids)).orderBy(
664
+ asc(planTasks.itemId),
665
+ asc(planTasks.phaseIdx),
666
+ asc(planTasks.idx)
667
+ );
668
+ async function loadRelations(orm, ids) {
669
+ const [commentRows, linkRows, phaseRows, taskRows] = await Promise.all([
670
+ selectComments(orm, ids),
671
+ selectLinks(orm, ids),
672
+ selectPhases(orm, ids),
673
+ selectTasks(orm, ids)
674
+ ]);
675
+ return {
676
+ comments: groupByItem(commentRows),
677
+ links: groupByItem(linkRows),
678
+ phases: groupByItem(phaseRows),
679
+ tasks: groupByItem(taskRows)
680
+ };
593
681
  }
594
682
 
595
- // src/commands/backlog/loadPlan.ts
596
- async function toPhase(db, itemId, p) {
597
- const tasks = await db.all(
598
- "SELECT task FROM plan_tasks WHERE item_id = ? AND phase_idx = ? ORDER BY idx",
599
- [itemId, p.idx]
600
- );
683
+ // src/commands/backlog/rowToItem.ts
684
+ function rowToComment(c) {
685
+ const comment3 = {
686
+ id: c.id,
687
+ text: c.text,
688
+ timestamp: c.timestamp,
689
+ type: c.type
690
+ };
691
+ if (c.phase != null) comment3.phase = c.phase;
692
+ return comment3;
693
+ }
694
+ function rowToLink(l) {
695
+ return { type: l.type, targetId: l.targetId };
696
+ }
697
+ function groupTasksByPhase(taskRows) {
698
+ const byPhase = /* @__PURE__ */ new Map();
699
+ for (const t of taskRows) {
700
+ const bucket = byPhase.get(t.phaseIdx);
701
+ if (bucket) bucket.push(t);
702
+ else byPhase.set(t.phaseIdx, [t]);
703
+ }
704
+ return byPhase;
705
+ }
706
+ function rowToPhase(p, byPhase) {
601
707
  const phase = {
602
708
  name: p.name,
603
- tasks: tasks.map((t) => ({ task: t.task }))
709
+ tasks: (byPhase.get(p.idx) ?? []).map((t) => ({ task: t.task }))
604
710
  };
605
- if (p.manual_checks) {
606
- phase.manualChecks = JSON.parse(p.manual_checks);
607
- }
711
+ if (p.manualChecks) phase.manualChecks = JSON.parse(p.manualChecks);
608
712
  return phase;
609
713
  }
610
- async function loadPlan(db, itemId) {
611
- const phases = await db.all(
612
- "SELECT idx, name, manual_checks FROM plan_phases WHERE item_id = ? ORDER BY idx",
613
- [itemId]
614
- );
615
- if (phases.length === 0) return void 0;
616
- return Promise.all(phases.map((p) => toPhase(db, itemId, p)));
714
+ function buildPlan(phaseRows, taskRows) {
715
+ const byPhase = groupTasksByPhase(taskRows);
716
+ return phaseRows.map((p) => rowToPhase(p, byPhase));
617
717
  }
618
-
619
- // src/commands/backlog/loadAllItems.ts
620
- async function loadLinks(db, itemId) {
621
- return db.all(
622
- 'SELECT type, target_id as "targetId" FROM links WHERE item_id = ?',
623
- [itemId]
624
- );
718
+ function assignOptionalColumns(item, row) {
719
+ if (row.description != null) item.description = row.description;
720
+ if (row.currentPhase != null) item.currentPhase = row.currentPhase;
625
721
  }
626
- async function rowToItem(db, row) {
627
- const comments2 = await loadComments(db, row.id);
628
- const links = await loadLinks(db, row.id);
629
- const plan2 = await loadPlan(db, row.id);
722
+ function baseItem(row) {
630
723
  const item = {
631
724
  id: row.id,
632
725
  type: row.type,
633
726
  name: row.name,
634
- acceptanceCriteria: JSON.parse(row.acceptance_criteria),
727
+ acceptanceCriteria: JSON.parse(row.acceptanceCriteria),
635
728
  status: row.status,
636
729
  origin: row.origin
637
730
  };
638
- if (row.description != null) item.description = row.description;
639
- if (row.current_phase != null) item.currentPhase = row.current_phase;
640
- if (comments2.length > 0) item.comments = comments2;
641
- if (links && links.length > 0) item.links = links;
642
- if (plan2) item.plan = plan2;
731
+ assignOptionalColumns(item, row);
732
+ return item;
733
+ }
734
+ function attachComments(item, rel, id) {
735
+ const comments3 = (rel.comments.get(id) ?? []).map(rowToComment);
736
+ if (comments3.length > 0) item.comments = comments3;
737
+ }
738
+ function attachLinks(item, rel, id) {
739
+ const links2 = (rel.links.get(id) ?? []).map(rowToLink);
740
+ if (links2.length > 0) item.links = links2;
741
+ }
742
+ function attachPlan(item, rel, id) {
743
+ const phases = rel.phases.get(id) ?? [];
744
+ if (phases.length > 0) item.plan = buildPlan(phases, rel.tasks.get(id) ?? []);
745
+ }
746
+ function rowToItem(row, rel) {
747
+ const item = baseItem(row);
748
+ attachComments(item, rel, row.id);
749
+ attachLinks(item, rel, row.id);
750
+ attachPlan(item, rel, row.id);
643
751
  return item;
644
752
  }
645
- async function loadAllItems(db, origin) {
646
- const rows = await db.all(
647
- `SELECT id, type, name, description, acceptance_criteria, status, current_phase, origin
648
- FROM items
649
- WHERE (?::text IS NULL OR origin = ?)
650
- ORDER BY id`,
651
- [origin ?? null, origin ?? null]
753
+
754
+ // src/commands/backlog/loadAllItems.ts
755
+ async function loadAllItems(orm, origin) {
756
+ const rows = await orm.select().from(items).where(origin === void 0 ? void 0 : eq2(items.origin, origin)).orderBy(asc2(items.id));
757
+ if (rows.length === 0) return [];
758
+ const rel = await loadRelations(
759
+ orm,
760
+ rows.map((r) => r.id)
652
761
  );
653
- return Promise.all(rows.map((row) => rowToItem(db, row)));
762
+ return rows.map((row) => rowToItem(row, rel));
654
763
  }
655
764
 
656
765
  // src/commands/backlog/parseBacklogJsonl.ts
@@ -707,22 +816,22 @@ function parseBacklogJsonl(path52) {
707
816
  function jsonlPath(dir) {
708
817
  return join4(dir, ".assist", "backlog.jsonl");
709
818
  }
710
- async function verifyImport(db, origin, items, imported) {
711
- const reloaded = await loadAllItems(db, origin);
819
+ async function verifyImport(orm, origin, items2, imported) {
820
+ const reloaded = await loadAllItems(orm, origin);
712
821
  if (reloaded.length !== imported) {
713
822
  throw new Error(
714
823
  `backlog migrate: expected ${imported} item(s) for ${origin} after import but found ${reloaded.length}.`
715
824
  );
716
825
  }
717
- if (items.length > 0 && !reloaded.some((i) => i.name === items[0].name)) {
826
+ if (items2.length > 0 && !reloaded.some((i) => i.name === items2[0].name)) {
718
827
  throw new Error(
719
- `backlog migrate: spot-check failed; "${items[0].name}" not found after import.`
828
+ `backlog migrate: spot-check failed; "${items2[0].name}" not found after import.`
720
829
  );
721
830
  }
722
831
  }
723
- async function migrateLocalBacklog(db, dir, origin) {
832
+ async function migrateLocalBacklog(orm, dir, origin) {
724
833
  if (!existsSync5(jsonlPath(dir))) return;
725
- const existing = (await loadAllItems(db, origin)).length;
834
+ const existing = (await loadAllItems(orm, origin)).length;
726
835
  if (existing > 0) {
727
836
  const moved2 = backupLocalBacklogFiles(dir);
728
837
  console.error(
@@ -733,9 +842,9 @@ async function migrateLocalBacklog(db, dir, origin) {
733
842
  return;
734
843
  }
735
844
  gitPullBacklog(dir);
736
- const items = parseBacklogJsonl(jsonlPath(dir));
737
- const imported = await importItemsRemapped(db, items, origin);
738
- await verifyImport(db, origin, items, imported);
845
+ const items2 = parseBacklogJsonl(jsonlPath(dir));
846
+ const imported = await importItemsRemapped(orm, items2, origin);
847
+ await verifyImport(orm, origin, items2, imported);
739
848
  const moved = backupLocalBacklogFiles(dir);
740
849
  console.error(
741
850
  chalk4.green(
@@ -746,10 +855,10 @@ async function migrateLocalBacklog(db, dir, origin) {
746
855
 
747
856
  // src/commands/backlog/ensureMigrated.ts
748
857
  var attempted = /* @__PURE__ */ new Set();
749
- async function ensureMigrated(db, dir, origin) {
858
+ async function ensureMigrated(orm, dir, origin) {
750
859
  if (attempted.has(origin)) return;
751
860
  attempted.add(origin);
752
- await migrateLocalBacklog(db, dir, origin);
861
+ await migrateLocalBacklog(orm, dir, origin);
753
862
  }
754
863
 
755
864
  // src/commands/backlog/findBacklogUp.ts
@@ -771,64 +880,16 @@ function findBacklogUp(startDir) {
771
880
  return null;
772
881
  }
773
882
 
774
- // src/commands/backlog/getBacklogDb.ts
883
+ // src/commands/backlog/getBacklogOrm.ts
775
884
  import chalk5 from "chalk";
776
885
  import { Pool } from "pg";
777
886
 
778
- // src/commands/backlog/BacklogDb.ts
779
- function toPg(sql2) {
780
- let i = 0;
781
- return sql2.replace(/\?/g, () => `$${++i}`);
782
- }
783
- var PgBacklogDb = class _PgBacklogDb {
784
- constructor(queryable, pool) {
785
- this.queryable = queryable;
786
- this.pool = pool;
787
- }
788
- async all(sql2, params = []) {
789
- const result = await this.queryable.query(toPg(sql2), params);
790
- return result.rows;
791
- }
792
- async get(sql2, params = []) {
793
- const result = await this.queryable.query(toPg(sql2), params);
794
- return result.rows[0];
795
- }
796
- async run(sql2, params = []) {
797
- const result = await this.queryable.query(toPg(sql2), params);
798
- return { changes: result.rowCount ?? result.affectedRows ?? 0 };
799
- }
800
- async exec(sql2) {
801
- if (this.queryable.exec) {
802
- await this.queryable.exec(sql2);
803
- } else {
804
- await this.queryable.query(sql2);
805
- }
806
- }
807
- async transaction(fn) {
808
- const client = this.pool ? await this.pool.connect() : void 0;
809
- const tx = client ? new _PgBacklogDb({
810
- query: (sql2, params) => client.query(sql2, params)
811
- }) : this;
812
- try {
813
- return await tx.atomic(fn);
814
- } finally {
815
- client?.release();
816
- }
817
- }
818
- async atomic(fn) {
819
- await this.queryable.query("BEGIN");
820
- try {
821
- const result = await fn(this);
822
- await this.queryable.query("COMMIT");
823
- return result;
824
- } catch (error) {
825
- await this.queryable.query("ROLLBACK");
826
- throw error;
827
- }
828
- }
829
- };
830
- function makeBacklogDb(queryable, pool) {
831
- return new PgBacklogDb(queryable, pool);
887
+ // src/commands/backlog/BacklogOrm.ts
888
+ import {
889
+ drizzle as drizzleNodePg
890
+ } from "drizzle-orm/node-postgres";
891
+ function makeOrmFromPool(pool) {
892
+ return drizzleNodePg(pool, { schema: backlogSchema });
832
893
  }
833
894
 
834
895
  // src/commands/backlog/ensureSchema.ts
@@ -886,11 +947,11 @@ var SCHEMA = `
886
947
  value TEXT NOT NULL
887
948
  );
888
949
  `;
889
- async function ensureSchema(db) {
890
- await db.exec(SCHEMA);
950
+ async function ensureSchema(exec3) {
951
+ await exec3(SCHEMA);
891
952
  }
892
953
 
893
- // src/commands/backlog/getBacklogDb.ts
954
+ // src/commands/backlog/getBacklogOrm.ts
894
955
  var DATABASE_URL_ENV = "ASSIST_BACKLOG_DATABASE_URL";
895
956
  var MISSING_URL_MESSAGE = `No backlog database configured.
896
957
 
@@ -911,27 +972,23 @@ function getDatabaseUrl() {
911
972
  }
912
973
  return url;
913
974
  }
914
- var _db;
915
975
  var _connecting;
916
976
  var _pool;
917
- function getBacklogDb() {
918
- if (_db) return Promise.resolve(_db);
977
+ var _orm;
978
+ function getBacklogOrm() {
979
+ if (_orm) return Promise.resolve(_orm);
919
980
  if (_connecting) return _connecting;
920
981
  _connecting = (async () => {
921
982
  const pool = new Pool({ connectionString: getDatabaseUrl() });
922
983
  _pool = pool;
923
- const db = makeBacklogDb(
924
- { query: (sql2, params) => pool.query(sql2, params) },
925
- pool
926
- );
927
- await ensureSchema(db);
928
- _db = db;
929
- return db;
984
+ await ensureSchema((sql4) => pool.query(sql4));
985
+ _orm = makeOrmFromPool(pool);
986
+ return _orm;
930
987
  })();
931
988
  return _connecting;
932
989
  }
933
990
  async function withBacklogClient(fn) {
934
- await getBacklogDb();
991
+ await getBacklogOrm();
935
992
  const pool = _pool;
936
993
  if (!pool) {
937
994
  throw new Error("COPY streaming requires a pooled Postgres connection.");
@@ -947,7 +1004,7 @@ async function closeBacklogDb() {
947
1004
  const pool = _pool;
948
1005
  if (!pool) return;
949
1006
  _pool = void 0;
950
- _db = void 0;
1007
+ _orm = void 0;
951
1008
  _connecting = void 0;
952
1009
  await pool.end();
953
1010
  }
@@ -1001,55 +1058,44 @@ function getCurrentOrigin(cwd) {
1001
1058
  return `local:${root ?? cwd}`;
1002
1059
  }
1003
1060
 
1061
+ // src/commands/backlog/saveAllItems.ts
1062
+ import { eq as eq4, sql } from "drizzle-orm";
1063
+
1064
+ // src/commands/backlog/deleteItemRelations.ts
1065
+ import { eq as eq3 } from "drizzle-orm";
1066
+ async function deleteItemRelations(db, itemId) {
1067
+ await db.delete(planTasks).where(eq3(planTasks.itemId, itemId));
1068
+ await db.delete(planPhases).where(eq3(planPhases.itemId, itemId));
1069
+ await db.delete(comments).where(eq3(comments.itemId, itemId));
1070
+ await db.delete(links).where(eq3(links.itemId, itemId));
1071
+ }
1072
+
1004
1073
  // src/commands/backlog/saveAllItems.ts
1005
1074
  async function upsertItem(db, item, origin) {
1006
- await db.run(
1007
- `INSERT INTO items (id, origin, type, name, description, acceptance_criteria, status, current_phase)
1008
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1009
- ON CONFLICT(id) DO UPDATE SET
1010
- origin = excluded.origin,
1011
- type = excluded.type,
1012
- name = excluded.name,
1013
- description = excluded.description,
1014
- acceptance_criteria = excluded.acceptance_criteria,
1015
- status = excluded.status,
1016
- current_phase = excluded.current_phase`,
1017
- [
1018
- item.id,
1019
- origin,
1020
- item.type,
1021
- item.name,
1022
- item.description ?? null,
1023
- JSON.stringify(item.acceptanceCriteria),
1024
- item.status,
1025
- item.currentPhase ?? null
1026
- ]
1027
- );
1075
+ const values = itemColumns(item, origin);
1076
+ await db.insert(items).values({ id: item.id, ...values }).onConflictDoUpdate({ target: items.id, set: values });
1028
1077
  await deleteItemRelations(db, item.id);
1029
1078
  await insertItemRelations(db, item);
1030
1079
  }
1031
1080
  async function resyncSequences(db) {
1032
- await db.exec(
1033
- "SELECT setval(pg_get_serial_sequence('items', 'id'), COALESCE((SELECT MAX(id) FROM items), 0) + 1, false)"
1081
+ await db.execute(
1082
+ sql`SELECT setval(pg_get_serial_sequence('items', 'id'), COALESCE((SELECT MAX(id) FROM items), 0) + 1, false)`
1034
1083
  );
1035
- await db.exec(
1036
- "SELECT setval(pg_get_serial_sequence('comments', 'id'), COALESCE((SELECT MAX(id) FROM comments), 0) + 1, false)"
1084
+ await db.execute(
1085
+ sql`SELECT setval(pg_get_serial_sequence('comments', 'id'), COALESCE((SELECT MAX(id) FROM comments), 0) + 1, false)`
1037
1086
  );
1038
1087
  }
1039
- async function saveAllItems(db, items, origin) {
1040
- await db.transaction(async (tx) => {
1041
- const existingIds = await tx.all(
1042
- "SELECT id FROM items WHERE origin = ?",
1043
- [origin]
1044
- );
1045
- const newIds = new Set(items.map((i) => i.id));
1088
+ async function saveAllItems(orm, file, origin) {
1089
+ await orm.transaction(async (tx) => {
1090
+ const existingIds = await tx.select({ id: items.id }).from(items).where(eq4(items.origin, origin));
1091
+ const newIds = new Set(file.map((i) => i.id));
1046
1092
  for (const { id } of existingIds) {
1047
1093
  if (!newIds.has(id)) {
1048
1094
  await deleteItemRelations(tx, id);
1049
- await tx.run("DELETE FROM items WHERE id = ?", [id]);
1095
+ await tx.delete(items).where(eq4(items.id, id));
1050
1096
  }
1051
1097
  }
1052
- for (const item of items) {
1098
+ for (const item of file) {
1053
1099
  await upsertItem(tx, item, origin);
1054
1100
  }
1055
1101
  await resyncSequences(tx);
@@ -1057,51 +1103,35 @@ async function saveAllItems(db, items, origin) {
1057
1103
  }
1058
1104
 
1059
1105
  // src/commands/backlog/searchItemIds.ts
1060
- async function searchItemIds(db, query, origin) {
1106
+ import { and, asc as asc3, eq as eq5, ilike, or } from "drizzle-orm";
1107
+ async function searchItemIds(orm, query, origin) {
1061
1108
  const pattern2 = `%${query}%`;
1062
- const rows = await db.all(
1063
- `SELECT DISTINCT i.id
1064
- FROM items i
1065
- LEFT JOIN comments c ON c.item_id = i.id
1066
- LEFT JOIN plan_phases p ON p.item_id = i.id
1067
- WHERE (?::text IS NULL OR i.origin = ?)
1068
- AND (
1069
- i.name ILIKE ?
1070
- OR i.description ILIKE ?
1071
- OR i.acceptance_criteria ILIKE ?
1072
- OR c.text ILIKE ?
1073
- OR p.name ILIKE ?
1074
- )
1075
- ORDER BY i.id`,
1076
- [
1077
- origin ?? null,
1078
- origin ?? null,
1079
- pattern2,
1080
- pattern2,
1081
- pattern2,
1082
- pattern2,
1083
- pattern2
1084
- ]
1085
- );
1109
+ const rows = await orm.selectDistinct({ id: items.id }).from(items).leftJoin(comments, eq5(comments.itemId, items.id)).leftJoin(planPhases, eq5(planPhases.itemId, items.id)).where(
1110
+ and(
1111
+ origin ? eq5(items.origin, origin) : void 0,
1112
+ or(
1113
+ ilike(items.name, pattern2),
1114
+ ilike(items.description, pattern2),
1115
+ ilike(items.acceptanceCriteria, pattern2),
1116
+ ilike(comments.text, pattern2),
1117
+ ilike(planPhases.name, pattern2)
1118
+ )
1119
+ )
1120
+ ).orderBy(asc3(items.id));
1086
1121
  return rows.map((r) => r.id);
1087
1122
  }
1088
1123
 
1089
1124
  // src/commands/backlog/updateCurrentPhase.ts
1090
- async function updateCurrentPhase(db, id, phase) {
1091
- const result = await db.run(
1092
- "UPDATE items SET current_phase = ? WHERE id = ?",
1093
- [phase, id]
1094
- );
1095
- return result.changes > 0;
1125
+ import { eq as eq6 } from "drizzle-orm";
1126
+ async function updateCurrentPhase(orm, id, phase) {
1127
+ await orm.update(items).set({ currentPhase: phase }).where(eq6(items.id, id));
1096
1128
  }
1097
1129
 
1098
1130
  // src/commands/backlog/updateStatus.ts
1099
- async function updateStatus(db, id, status2) {
1100
- const result = await db.run("UPDATE items SET status = ? WHERE id = ?", [
1101
- status2,
1102
- id
1103
- ]);
1104
- return result.changes > 0;
1131
+ import { eq as eq7 } from "drizzle-orm";
1132
+ async function updateStatus(orm, id, status2) {
1133
+ const [row] = await orm.update(items).set({ status: status2 }).where(eq7(items.id, id)).returning({ name: items.name });
1134
+ return row?.name;
1105
1135
  }
1106
1136
 
1107
1137
  // src/commands/backlog/shared.ts
@@ -1115,58 +1145,54 @@ function getBacklogDir() {
1115
1145
  function getOrigin() {
1116
1146
  return getCurrentOrigin(getBacklogDir());
1117
1147
  }
1118
- async function getDb() {
1119
- const db = await getBacklogDb();
1148
+ async function getReady() {
1149
+ const orm = await getBacklogOrm();
1120
1150
  const dir = getBacklogDir();
1121
- await ensureMigrated(db, dir, getCurrentOrigin(dir));
1122
- return db;
1151
+ await ensureMigrated(orm, dir, getCurrentOrigin(dir));
1152
+ return { orm };
1123
1153
  }
1124
1154
  async function loadBacklog(allRepos = false) {
1125
- const db = await getDb();
1126
- return loadAllItems(db, allRepos ? void 0 : getOrigin());
1155
+ const { orm } = await getReady();
1156
+ return loadAllItems(orm, allRepos ? void 0 : getOrigin());
1127
1157
  }
1128
1158
  async function searchBacklog(query) {
1129
- const db = await getDb();
1159
+ const { orm } = await getReady();
1130
1160
  const origin = getOrigin();
1131
- const ids = await searchItemIds(db, query, origin);
1132
- const allItems = await loadAllItems(db, origin);
1161
+ const ids = await searchItemIds(orm, query, origin);
1162
+ const allItems = await loadAllItems(orm, origin);
1133
1163
  return allItems.filter((item) => ids.includes(item.id));
1134
1164
  }
1135
- async function saveBacklog(items) {
1136
- const db = await getDb();
1137
- await saveAllItems(db, items, getOrigin());
1165
+ async function saveBacklog(items2) {
1166
+ const { orm } = await getReady();
1167
+ await saveAllItems(orm, items2, getOrigin());
1138
1168
  }
1139
- function findItem(items, id) {
1140
- return items.find((item) => item.id === id);
1169
+ function findItem(items2, id) {
1170
+ return items2.find((item) => item.id === id);
1141
1171
  }
1142
1172
  async function loadAndFindItem(id) {
1143
- const items = await loadBacklog();
1144
- const item = findItem(items, Number.parseInt(id, 10));
1173
+ const items2 = await loadBacklog();
1174
+ const item = findItem(items2, Number.parseInt(id, 10));
1145
1175
  if (!item) {
1146
1176
  console.log(chalk6.red(`Item #${id} not found.`));
1147
1177
  return void 0;
1148
1178
  }
1149
- return { items, item };
1179
+ return { items: items2, item };
1150
1180
  }
1151
1181
  async function setStatus(id, status2) {
1152
- const result = await loadAndFindItem(id);
1153
- if (!result) return void 0;
1154
- const db = await getBacklogDb();
1155
- await updateStatus(db, result.item.id, status2);
1156
- return result.item.name;
1182
+ const { orm } = await getReady();
1183
+ const name = await updateStatus(orm, Number.parseInt(id, 10), status2);
1184
+ if (name === void 0) console.log(chalk6.red(`Item #${id} not found.`));
1185
+ return name;
1157
1186
  }
1158
1187
  async function setCurrentPhase(id, phase) {
1159
- const result = await loadAndFindItem(id);
1160
- if (!result) return;
1161
- const db = await getBacklogDb();
1162
- await updateCurrentPhase(db, result.item.id, phase);
1188
+ const { orm } = await getReady();
1189
+ await updateCurrentPhase(orm, Number.parseInt(id, 10), phase);
1163
1190
  }
1164
1191
  async function removeItem(id) {
1165
- const result = await loadAndFindItem(id);
1166
- if (!result) return void 0;
1167
- const db = await getBacklogDb();
1168
- await deleteItem(db, result.item.id);
1169
- return result.item.name;
1192
+ const { orm } = await getReady();
1193
+ const name = await deleteItem(orm, Number.parseInt(id, 10));
1194
+ if (name === void 0) console.log(chalk6.red(`Item #${id} not found.`));
1195
+ return name;
1170
1196
  }
1171
1197
 
1172
1198
  // src/commands/backlog/acquireLock.ts
@@ -1232,17 +1258,17 @@ function phaseLabel(item) {
1232
1258
  if (!item.plan) return "";
1233
1259
  return chalk7.dim(` (phase ${item.currentPhase ?? 1}/${item.plan.length})`);
1234
1260
  }
1235
- function isBlocked(item, items) {
1261
+ function isBlocked(item, items2) {
1236
1262
  const deps2 = (item.links ?? []).filter((l) => l.type === "depends-on");
1237
1263
  return deps2.some((dep) => {
1238
- const target = items.find((i) => i.id === dep.targetId);
1264
+ const target = items2.find((i) => i.id === dep.targetId);
1239
1265
  return target !== void 0 && target.status !== "done";
1240
1266
  });
1241
1267
  }
1242
- function dependencyLabel(item, items) {
1268
+ function dependencyLabel(item, items2) {
1243
1269
  const deps2 = (item.links ?? []).filter((l) => l.type === "depends-on");
1244
1270
  if (deps2.length === 0) return "";
1245
- if (isBlocked(item, items)) return chalk7.red(" [blocked]");
1271
+ if (isBlocked(item, items2)) return chalk7.red(" [blocked]");
1246
1272
  return chalk7.dim(` [${deps2.length} dep${deps2.length > 1 ? "s" : ""}]`);
1247
1273
  }
1248
1274
  function printVerboseDetails(item) {
@@ -1259,21 +1285,21 @@ function printVerboseDetails(item) {
1259
1285
  }
1260
1286
 
1261
1287
  // src/commands/backlog/findResumable.ts
1262
- function findResumable(items) {
1263
- return items.find(
1264
- (i) => i.status === "in-progress" && i.plan && !isLockedByOther(i.id) && !isBlocked(i, items)
1288
+ function findResumable(items2) {
1289
+ return items2.find(
1290
+ (i) => i.status === "in-progress" && i.plan && !isLockedByOther(i.id) && !isBlocked(i, items2)
1265
1291
  );
1266
1292
  }
1267
1293
 
1268
1294
  // src/commands/backlog/findUnblockedTodos.ts
1269
1295
  import chalk8 from "chalk";
1270
- function findUnblockedTodos(items) {
1271
- const todo = items.filter((i) => i.status === "todo");
1296
+ function findUnblockedTodos(items2) {
1297
+ const todo = items2.filter((i) => i.status === "todo");
1272
1298
  if (todo.length === 0) {
1273
1299
  console.log(chalk8.green("All backlog items complete."));
1274
1300
  return void 0;
1275
1301
  }
1276
- const unblocked = todo.filter((i) => !isBlocked(i, items));
1302
+ const unblocked = todo.filter((i) => !isBlocked(i, items2));
1277
1303
  if (unblocked.length === 0) {
1278
1304
  console.log(
1279
1305
  chalk8.yellow("All remaining todo items are blocked by dependencies.")
@@ -1344,9 +1370,9 @@ function spawnClaude(prompt, options2 = {}) {
1344
1370
  }
1345
1371
 
1346
1372
  // src/commands/backlog/buildCommentLines.ts
1347
- function buildCommentLines(comments2) {
1348
- if (!comments2?.length) return [];
1349
- return ["", "Comments:", ...comments2.map(formatPromptComment)];
1373
+ function buildCommentLines(comments3) {
1374
+ if (!comments3?.length) return [];
1375
+ return ["", "Comments:", ...comments3.map(formatPromptComment)];
1350
1376
  }
1351
1377
  function formatPromptComment(entry) {
1352
1378
  const id = entry.id !== void 0 ? `#${entry.id} ` : "";
@@ -1503,8 +1529,8 @@ function cleanupSignal() {
1503
1529
  }
1504
1530
  }
1505
1531
  async function isTerminalStatus(itemId) {
1506
- const items = await loadBacklog();
1507
- const item = items.find((i) => i.id === itemId);
1532
+ const items2 = await loadBacklog();
1533
+ const item = items2.find((i) => i.id === itemId);
1508
1534
  return item?.status === "done" || item?.status === "wontdo";
1509
1535
  }
1510
1536
  async function resolvePhaseResult(phaseIndex, itemId) {
@@ -1660,23 +1686,23 @@ async function ensureDone(id) {
1660
1686
  }
1661
1687
 
1662
1688
  // src/commands/backlog/next.ts
1663
- function toChoice(item, items) {
1689
+ function toChoice(item, items2) {
1664
1690
  const name = `${typeLabel(item.type)} #${item.id}: ${item.name}`;
1665
- return isBlocked(item, items) ? { name, disabled: chalk13.red("[blocked]") } : { name };
1691
+ return isBlocked(item, items2) ? { name, disabled: chalk13.red("[blocked]") } : { name };
1666
1692
  }
1667
- async function selectItem(todo, items) {
1693
+ async function selectItem(todo, items2) {
1668
1694
  const { selected } = await exitOnCancel(
1669
1695
  enquirer2.prompt({
1670
1696
  type: "select",
1671
1697
  name: "selected",
1672
1698
  message: "Choose a backlog item to start:",
1673
- choices: todo.map((i) => toChoice(i, items))
1699
+ choices: todo.map((i) => toChoice(i, items2))
1674
1700
  })
1675
1701
  );
1676
1702
  return selected.match(/#(\d+)/)?.[1] ?? "";
1677
1703
  }
1678
- async function pickItem(items, firstPick = false) {
1679
- const resumable = findResumable(items);
1704
+ async function pickItem(items2, firstPick = false) {
1705
+ const resumable = findResumable(items2);
1680
1706
  if (resumable) {
1681
1707
  console.log(
1682
1708
  chalk13.bold(
@@ -1685,15 +1711,15 @@ async function pickItem(items, firstPick = false) {
1685
1711
  );
1686
1712
  return String(resumable.id);
1687
1713
  }
1688
- const unblocked = findUnblockedTodos(items);
1714
+ const unblocked = findUnblockedTodos(items2);
1689
1715
  if (!unblocked) return void 0;
1690
1716
  if (firstPick && unblocked.length === 1) {
1691
1717
  const item = unblocked[0];
1692
1718
  console.log(chalk13.bold(`Auto-selecting item #${item.id}: ${item.name}`));
1693
1719
  return String(item.id);
1694
1720
  }
1695
- const todo = items.filter((i) => i.status === "todo");
1696
- return selectItem(todo, items);
1721
+ const todo = items2.filter((i) => i.status === "todo");
1722
+ return selectItem(todo, items2);
1697
1723
  }
1698
1724
  async function next(options2) {
1699
1725
  if (blockedByHandover()) return;
@@ -1710,46 +1736,50 @@ async function next(options2) {
1710
1736
  // src/commands/backlog/phaseDone.ts
1711
1737
  import chalk14 from "chalk";
1712
1738
 
1713
- // src/commands/backlog/addComment.ts
1714
- function addComment(item, text, phase) {
1715
- const entry = {
1716
- text,
1739
+ // src/commands/backlog/appendComment.ts
1740
+ import { sql as sql2 } from "drizzle-orm";
1741
+ async function appendComment(orm, itemId, text2, opts = {}) {
1742
+ await orm.insert(comments).values({
1743
+ itemId,
1744
+ idx: sql2`(SELECT COALESCE(MAX(${comments.idx}) + 1, 0) FROM ${comments} WHERE ${comments.itemId} = ${itemId})`,
1745
+ text: text2,
1746
+ phase: opts.phase ?? null,
1717
1747
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1718
- type: "comment",
1719
- ...phase !== void 0 && { phase }
1720
- };
1721
- if (!item.comments) item.comments = [];
1722
- item.comments.push(entry);
1748
+ type: opts.type ?? "comment"
1749
+ });
1723
1750
  }
1724
- function addPhaseSummary(item, text, phase) {
1725
- const entry = {
1726
- text,
1727
- phase,
1728
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1729
- type: "summary"
1730
- };
1731
- if (!item.comments) item.comments = [];
1732
- item.comments.push(entry);
1751
+
1752
+ // src/commands/backlog/getItemStatus.ts
1753
+ import { eq as eq8 } from "drizzle-orm";
1754
+ async function getItemStatus(orm, id) {
1755
+ const [row] = await orm.select({ status: items.status }).from(items).where(eq8(items.id, id));
1756
+ return row?.status;
1733
1757
  }
1734
1758
 
1735
1759
  // src/commands/backlog/phaseDone.ts
1736
1760
  async function phaseDone(id, phase, summary) {
1737
1761
  const phaseNumber = Number.parseInt(phase, 10);
1738
1762
  const phaseIndex = phaseNumber - 1;
1763
+ const itemId = Number.parseInt(id, 10);
1739
1764
  writeSignal("phase-done", {
1740
- itemId: Number.parseInt(id, 10),
1765
+ itemId,
1741
1766
  phaseIndex,
1742
1767
  completedAt: (/* @__PURE__ */ new Date()).toISOString()
1743
1768
  });
1744
- const result = await loadAndFindItem(id);
1745
- if (result?.item.status === "done") {
1746
- console.log(chalk14.dim(`Item #${id} already done, skipping phase advance.`));
1769
+ const { orm } = await getReady();
1770
+ const status2 = await getItemStatus(orm, itemId);
1771
+ if (status2 === void 0) {
1772
+ console.log(chalk14.red(`Item #${id} not found.`));
1747
1773
  return;
1748
1774
  }
1749
- if (result) {
1750
- addPhaseSummary(result.item, summary, phaseNumber);
1751
- await saveBacklog(result.items);
1775
+ if (status2 === "done") {
1776
+ console.log(chalk14.dim(`Item #${id} already done, skipping phase advance.`));
1777
+ return;
1752
1778
  }
1779
+ await appendComment(orm, itemId, summary, {
1780
+ phase: phaseNumber,
1781
+ type: "summary"
1782
+ });
1753
1783
  await setCurrentPhase(id, phaseNumber + 1);
1754
1784
  console.log(
1755
1785
  chalk14.green(`Phase ${phaseNumber} of item #${id} marked as complete.`)
@@ -1793,12 +1823,12 @@ function formatComment(entry) {
1793
1823
 
1794
1824
  // src/commands/backlog/show/printLinks.ts
1795
1825
  import chalk17 from "chalk";
1796
- function printLinks(item, items) {
1797
- const links = item.links ?? [];
1798
- if (links.length === 0) return;
1826
+ function printLinks(item, items2) {
1827
+ const links2 = item.links ?? [];
1828
+ if (links2.length === 0) return;
1799
1829
  console.log(chalk17.bold("Links"));
1800
- for (const link3 of links) {
1801
- const target = items.find((i) => i.id === link3.targetId);
1830
+ for (const link3 of links2) {
1831
+ const target = items2.find((i) => i.id === link3.targetId);
1802
1832
  const typeLabel2 = link3.type === "depends-on" ? chalk17.red("depends-on") : chalk17.blue("relates-to");
1803
1833
  if (target) {
1804
1834
  console.log(
@@ -1837,14 +1867,14 @@ function printPlan(item) {
1837
1867
  }
1838
1868
  console.log();
1839
1869
  }
1840
- function phaseHeader(index, name, isCurrent) {
1841
- const phaseNumber = index + 1;
1870
+ function phaseHeader(index2, name, isCurrent) {
1871
+ const phaseNumber = index2 + 1;
1842
1872
  const marker = isCurrent ? chalk19.green("\u25B6 ") : " ";
1843
1873
  const label2 = isCurrent ? chalk19.green.bold(`Phase ${phaseNumber}: ${name}`) : `${chalk19.bold(`Phase ${phaseNumber}:`)} ${name}`;
1844
1874
  return `${marker}${label2}`;
1845
1875
  }
1846
- function printPhase(phase, index, isCurrent) {
1847
- console.log(phaseHeader(index, phase.name, isCurrent));
1876
+ function printPhase(phase, index2, isCurrent) {
1877
+ console.log(phaseHeader(index2, phase.name, isCurrent));
1848
1878
  printPhaseTasks(phase);
1849
1879
  }
1850
1880
  function printHeader(item) {
@@ -1865,7 +1895,7 @@ function printAcceptanceCriteria(criteria) {
1865
1895
  async function show(id) {
1866
1896
  const result = await loadAndFindItem(id);
1867
1897
  if (!result) process.exit(1);
1868
- const { item, items } = result;
1898
+ const { item, items: items2 } = result;
1869
1899
  printHeader(item);
1870
1900
  if (item.description) {
1871
1901
  console.log(chalk19.bold("Description"));
@@ -1873,7 +1903,7 @@ async function show(id) {
1873
1903
  console.log();
1874
1904
  }
1875
1905
  printAcceptanceCriteria(item.acceptanceCriteria);
1876
- printLinks(item, items);
1906
+ printLinks(item, items2);
1877
1907
  printPlan(item);
1878
1908
  printComments(item);
1879
1909
  }
@@ -2009,7 +2039,7 @@ async function parseRewindBody(req) {
2009
2039
  // src/commands/backlog/web/createItem.ts
2010
2040
  async function createItem(req, res) {
2011
2041
  const body = await parseItemBody(req);
2012
- const db = await getBacklogDb();
2042
+ const orm = await getBacklogOrm();
2013
2043
  const newItem = {
2014
2044
  type: body.type ?? "story",
2015
2045
  name: body.name,
@@ -2017,15 +2047,37 @@ async function createItem(req, res) {
2017
2047
  acceptanceCriteria: body.acceptanceCriteria ?? [],
2018
2048
  status: "todo"
2019
2049
  };
2020
- const id = await insertItem(db, newItem, getOrigin());
2050
+ const id = await insertItem(orm, newItem, getOrigin());
2021
2051
  respondJson(res, 201, { id, ...newItem });
2022
2052
  }
2023
2053
 
2054
+ // src/commands/backlog/addComment.ts
2055
+ function addComment(item, text2, phase) {
2056
+ const entry = {
2057
+ text: text2,
2058
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2059
+ type: "comment",
2060
+ ...phase !== void 0 && { phase }
2061
+ };
2062
+ if (!item.comments) item.comments = [];
2063
+ item.comments.push(entry);
2064
+ }
2065
+ function addPhaseSummary(item, text2, phase) {
2066
+ const entry = {
2067
+ text: text2,
2068
+ phase,
2069
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2070
+ type: "summary"
2071
+ };
2072
+ if (!item.comments) item.comments = [];
2073
+ item.comments.push(entry);
2074
+ }
2075
+
2024
2076
  // src/commands/backlog/web/rewindItemPhase.ts
2025
2077
  async function rewindItemPhase(req, res, id) {
2026
2078
  const { phase, reason } = await parseRewindBody(req);
2027
- const items = await loadBacklog();
2028
- const item = items.find((i) => i.id === id);
2079
+ const items2 = await loadBacklog();
2080
+ const item = items2.find((i) => i.id === id);
2029
2081
  if (!item) {
2030
2082
  respondJson(res, 404, { error: "Not found" });
2031
2083
  return;
@@ -2043,7 +2095,7 @@ async function rewindItemPhase(req, res, id) {
2043
2095
  );
2044
2096
  item.currentPhase = phase;
2045
2097
  item.status = "in-progress";
2046
- await saveBacklog(items);
2098
+ await saveBacklog(items2);
2047
2099
  respondJson(res, 200, item);
2048
2100
  }
2049
2101
  function validateRewind(item, phase) {
@@ -2067,13 +2119,13 @@ async function listItems(req, res) {
2067
2119
  respondJson(res, 200, q ? await searchBacklog(q) : await loadBacklog());
2068
2120
  }
2069
2121
  async function findItemOr404(res, id) {
2070
- const items = await loadBacklog();
2071
- const item = items.find((i) => i.id === id);
2122
+ const items2 = await loadBacklog();
2123
+ const item = items2.find((i) => i.id === id);
2072
2124
  if (!item) {
2073
2125
  respondJson(res, 404, { error: "Not found" });
2074
2126
  return void 0;
2075
2127
  }
2076
- return { items, item };
2128
+ return { items: items2, item };
2077
2129
  }
2078
2130
  async function getItemById(res, id) {
2079
2131
  const result = await findItemOr404(res, id);
@@ -2539,8 +2591,8 @@ function safeParse(line) {
2539
2591
  function extractName(entry) {
2540
2592
  const msg = entry.message;
2541
2593
  const content = msg?.content;
2542
- const text = typeof content === "string" ? content : Array.isArray(content) ? content.find((c) => c.type === "text")?.text ?? "" : "";
2543
- return text.replace(/<command-[^>]*>[^<]*<\/command-[^>]*>/g, "").trim().slice(0, 80);
2594
+ const text2 = typeof content === "string" ? content : Array.isArray(content) ? content.find((c) => c.type === "text")?.text ?? "" : "";
2595
+ return text2.replace(/<command-[^>]*>[^<]*<\/command-[^>]*>/g, "").trim().slice(0, 80);
2544
2596
  }
2545
2597
 
2546
2598
  // src/commands/sessions/web/parseSessionFile.ts
@@ -2839,9 +2891,9 @@ import chalk22 from "chalk";
2839
2891
  // src/commands/backlog/tryRunById.ts
2840
2892
  import chalk21 from "chalk";
2841
2893
  async function tryRunById(id, options2) {
2842
- const items = await loadBacklog();
2894
+ const items2 = await loadBacklog();
2843
2895
  const numericId = Number.parseInt(id, 10);
2844
- const item = Number.isNaN(numericId) ? void 0 : items.find((i) => i.id === numericId);
2896
+ const item = Number.isNaN(numericId) ? void 0 : items2.find((i) => i.id === numericId);
2845
2897
  if (!item) {
2846
2898
  console.log(chalk21.red(`Item #${id} not found.`));
2847
2899
  return false;
@@ -2854,7 +2906,7 @@ async function tryRunById(id, options2) {
2854
2906
  console.log(chalk21.red(`Item #${id} is marked won't do.`));
2855
2907
  return false;
2856
2908
  }
2857
- if (isBlocked(item, items)) {
2909
+ if (isBlocked(item, items2)) {
2858
2910
  console.log(
2859
2911
  chalk21.red(`Item #${id} is blocked by unresolved dependencies.`)
2860
2912
  );
@@ -2889,8 +2941,8 @@ async function launchMode(slashCommand) {
2889
2941
  import chalk23 from "chalk";
2890
2942
  import enquirer3 from "enquirer";
2891
2943
  async function pickItemForRefine() {
2892
- const items = await loadBacklog();
2893
- const active = items.filter(
2944
+ const items2 = await loadBacklog();
2945
+ const active = items2.filter(
2894
2946
  (i) => i.status === "todo" || i.status === "in-progress"
2895
2947
  );
2896
2948
  if (active.length === 0) {
@@ -3606,8 +3658,8 @@ var options = [
3606
3658
  ];
3607
3659
 
3608
3660
  // src/commands/verify/init/getAvailableOptions/index.ts
3609
- function resolveDescription(desc, setup2) {
3610
- return typeof desc === "function" ? desc(setup2) : desc;
3661
+ function resolveDescription(desc2, setup2) {
3662
+ return typeof desc2 === "function" ? desc2(setup2) : desc2;
3611
3663
  }
3612
3664
  function toVerifyOption(def, setup2) {
3613
3665
  return {
@@ -4332,10 +4384,10 @@ ${failed2.length} script(s) failed:`);
4332
4384
  console.error(` - ${f.script} (exit code ${f.code})`);
4333
4385
  }
4334
4386
  }
4335
- function createTimerCallback(taskStatuses, index) {
4387
+ function createTimerCallback(taskStatuses, index2) {
4336
4388
  return (exitCode) => {
4337
- taskStatuses[index].endTime = Date.now();
4338
- taskStatuses[index].code = exitCode;
4389
+ taskStatuses[index2].endTime = Date.now();
4390
+ taskStatuses[index2].code = exitCode;
4339
4391
  printTaskStatuses(taskStatuses);
4340
4392
  };
4341
4393
  }
@@ -4414,9 +4466,9 @@ function runAllEntries(entries, timer) {
4414
4466
  const taskStatuses = initTaskStatuses(entries.map((e) => e.name));
4415
4467
  return Promise.all(
4416
4468
  entries.map(
4417
- (entry, index) => runEntry(
4469
+ (entry, index2) => runEntry(
4418
4470
  entry,
4419
- timer ? createTimerCallback(taskStatuses, index) : void 0
4471
+ timer ? createTimerCallback(taskStatuses, index2) : void 0
4420
4472
  )
4421
4473
  )
4422
4474
  );
@@ -4900,13 +4952,13 @@ async function activity(options2) {
4900
4952
  const activeDays = data.filter((d) => d.count > 0).length;
4901
4953
  console.log(`${total} commits across ${activeDays} active days.`);
4902
4954
  const weekly = /* @__PURE__ */ new Map();
4903
- for (const { date, count } of data) {
4955
+ for (const { date, count: count4 } of data) {
4904
4956
  const d = new Date(date);
4905
4957
  d.setDate(d.getDate() - d.getDay());
4906
4958
  const weekStart = d.toISOString().slice(0, 10);
4907
- weekly.set(weekStart, (weekly.get(weekStart) ?? 0) + count);
4959
+ weekly.set(weekStart, (weekly.get(weekStart) ?? 0) + count4);
4908
4960
  }
4909
- const weeklyData = [...weekly.entries()].map(([date, count]) => ({ date, count })).sort((a, b) => a.date.localeCompare(b.date));
4961
+ const weeklyData = [...weekly.entries()].map(([date, count4]) => ({ date, count: count4 })).sort((a, b) => a.date.localeCompare(b.date));
4910
4962
  const until = data[data.length - 1].date;
4911
4963
  activityChart(weeklyData, { since, until });
4912
4964
  }
@@ -4921,17 +4973,17 @@ function registerActivity(program2) {
4921
4973
 
4922
4974
  // src/commands/backlog/comment/index.ts
4923
4975
  import chalk46 from "chalk";
4924
- async function comment(id, text) {
4976
+ async function comment(id, text2) {
4925
4977
  const result = await loadAndFindItem(id);
4926
4978
  if (!result) process.exit(1);
4927
- addComment(result.item, text);
4979
+ addComment(result.item, text2);
4928
4980
  await saveBacklog(result.items);
4929
4981
  console.log(chalk46.green(`Comment added to item #${id}.`));
4930
4982
  }
4931
4983
 
4932
4984
  // src/commands/backlog/comments/index.ts
4933
4985
  import chalk47 from "chalk";
4934
- async function comments(id) {
4986
+ async function comments2(id) {
4935
4987
  const result = await loadAndFindItem(id);
4936
4988
  if (!result) process.exit(1);
4937
4989
  const { item } = result;
@@ -4952,17 +5004,12 @@ async function comments(id) {
4952
5004
  import chalk48 from "chalk";
4953
5005
 
4954
5006
  // src/commands/backlog/deleteComment.ts
4955
- async function deleteComment(db, itemId, commentId) {
4956
- const row = await db.get(
4957
- "SELECT type FROM comments WHERE id = ? AND item_id = ?",
4958
- [commentId, itemId]
4959
- );
5007
+ import { and as and2, eq as eq9 } from "drizzle-orm";
5008
+ async function deleteComment(orm, itemId, commentId) {
5009
+ const [row] = await orm.select({ type: comments.type }).from(comments).where(and2(eq9(comments.id, commentId), eq9(comments.itemId, itemId)));
4960
5010
  if (!row) return "not-found";
4961
5011
  if (row.type === "summary") return "is-summary";
4962
- await db.run("DELETE FROM comments WHERE id = ? AND item_id = ?", [
4963
- commentId,
4964
- itemId
4965
- ]);
5012
+ await orm.delete(comments).where(and2(eq9(comments.id, commentId), eq9(comments.itemId, itemId)));
4966
5013
  return "deleted";
4967
5014
  }
4968
5015
 
@@ -4970,9 +5017,9 @@ async function deleteComment(db, itemId, commentId) {
4970
5017
  async function deleteCommentCmd(id, commentId) {
4971
5018
  const result = await loadAndFindItem(id);
4972
5019
  if (!result) process.exit(1);
4973
- const db = await getBacklogDb();
5020
+ const orm = await getBacklogOrm();
4974
5021
  const outcome = await deleteComment(
4975
- db,
5022
+ orm,
4976
5023
  result.item.id,
4977
5024
  Number.parseInt(commentId, 10)
4978
5025
  );
@@ -5000,7 +5047,7 @@ async function deleteCommentCmd(id, commentId) {
5000
5047
  // src/commands/backlog/registerCommentCommands.ts
5001
5048
  function registerCommentCommands(cmd) {
5002
5049
  cmd.command("comment <id> <text>").description("Add a comment to a backlog item").action(comment);
5003
- cmd.command("comments <id>").description("List comments and summaries for a backlog item").action(comments);
5050
+ cmd.command("comments <id>").description("List comments and summaries for a backlog item").action(comments2);
5004
5051
  cmd.command("delete-comment <id> <comment-id>").description("Delete a comment from a backlog item").action(deleteCommentCmd);
5005
5052
  }
5006
5053
 
@@ -5056,8 +5103,8 @@ async function buildDump(copyOut) {
5056
5103
  // src/commands/backlog/dump/copyTableOut.ts
5057
5104
  import { to as copyTo } from "pg-copy-streams";
5058
5105
  async function copyTableOut(client, table) {
5059
- const sql2 = `COPY ${table.name} (${table.columns.join(", ")}) TO STDOUT`;
5060
- const stream = client.query(copyTo(sql2));
5106
+ const sql4 = `COPY ${table.name} (${table.columns.join(", ")}) TO STDOUT`;
5107
+ const stream = client.query(copyTo(sql4));
5061
5108
  const chunks = [];
5062
5109
  for await (const chunk of stream) {
5063
5110
  chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
@@ -5094,10 +5141,10 @@ import chalk51 from "chalk";
5094
5141
  // src/commands/backlog/dump/countCopyRows.ts
5095
5142
  function countCopyRows(data) {
5096
5143
  let rows = 0;
5097
- let index = data.indexOf(10);
5098
- while (index !== -1) {
5144
+ let index2 = data.indexOf(10);
5145
+ while (index2 !== -1) {
5099
5146
  rows++;
5100
- index = data.indexOf(10, index + 1);
5147
+ index2 = data.indexOf(10, index2 + 1);
5101
5148
  }
5102
5149
  return rows;
5103
5150
  }
@@ -5113,9 +5160,9 @@ function readLine(dump, start3) {
5113
5160
  return { text: dump.subarray(start3, eol).toString("utf8"), next: eol + 1 };
5114
5161
  }
5115
5162
  function parseHeader(dump) {
5116
- const { text, next: next3 } = readLine(dump, 0);
5163
+ const { text: text2, next: next3 } = readLine(dump, 0);
5117
5164
  try {
5118
- return { header: JSON.parse(text), bodyStart: next3 };
5165
+ return { header: JSON.parse(text2), bodyStart: next3 };
5119
5166
  } catch {
5120
5167
  return invalid("header is not valid JSON.");
5121
5168
  }
@@ -5124,9 +5171,9 @@ function parseSections(dump, bodyStart) {
5124
5171
  const sections = /* @__PURE__ */ new Map();
5125
5172
  let cursor = bodyStart;
5126
5173
  while (cursor < dump.length) {
5127
- const { text, next: next3 } = readLine(dump, cursor);
5128
- const match = text.match(/^@table (\S+) (\d+)$/);
5129
- if (!match) invalid(`malformed table marker "${text}".`);
5174
+ const { text: text2, next: next3 } = readLine(dump, cursor);
5175
+ const match = text2.match(/^@table (\S+) (\d+)$/);
5176
+ if (!match) invalid(`malformed table marker "${text2}".`);
5130
5177
  const [, name, bytes] = match;
5131
5178
  const end = next3 + Number(bytes);
5132
5179
  if (end > dump.length) invalid(`section "${name}" overruns the dump.`);
@@ -5207,8 +5254,8 @@ async function readStdinBuffer() {
5207
5254
  import { finished } from "stream/promises";
5208
5255
  import { from as copyFrom } from "pg-copy-streams";
5209
5256
  async function copyTableIn(client, table, data) {
5210
- const sql2 = `COPY ${table.name} (${table.columns.join(", ")}) FROM STDIN`;
5211
- const stream = client.query(copyFrom(sql2));
5257
+ const sql4 = `COPY ${table.name} (${table.columns.join(", ")}) FROM STDIN`;
5258
+ const stream = client.query(copyFrom(sql4));
5212
5259
  stream.end(data);
5213
5260
  await finished(stream);
5214
5261
  }
@@ -5354,9 +5401,9 @@ async function add(options2) {
5354
5401
  const name = options2.name ?? await promptName();
5355
5402
  const description = options2.desc?.replaceAll("\\n", "\n") ?? await promptDescription();
5356
5403
  const acceptanceCriteria2 = options2.ac ?? await promptAcceptanceCriteria();
5357
- const db = await getBacklogDb();
5404
+ const orm = await getBacklogOrm();
5358
5405
  const id = await insertItem(
5359
- db,
5406
+ orm,
5360
5407
  {
5361
5408
  type,
5362
5409
  name,
@@ -5373,48 +5420,37 @@ async function add(options2) {
5373
5420
  import chalk54 from "chalk";
5374
5421
 
5375
5422
  // src/commands/backlog/insertPhaseAt.ts
5376
- async function insertPhaseAt(db, itemId, phaseIdx, name, tasks, manualChecks, currentPhase) {
5377
- await db.transaction(async (tx) => {
5378
- const toShift = await tx.all(
5379
- "SELECT idx FROM plan_phases WHERE item_id = ? AND idx >= ? ORDER BY idx DESC",
5380
- [itemId, phaseIdx]
5381
- );
5382
- for (const p of toShift) {
5383
- await tx.run(
5384
- "UPDATE plan_tasks SET phase_idx = ? WHERE item_id = ? AND phase_idx = ?",
5385
- [p.idx + 1, itemId, p.idx]
5386
- );
5387
- await tx.run(
5388
- "UPDATE plan_phases SET idx = ? WHERE item_id = ? AND idx = ?",
5389
- [p.idx + 1, itemId, p.idx]
5390
- );
5391
- }
5392
- await tx.run(
5393
- "INSERT INTO plan_phases (item_id, idx, name, manual_checks) VALUES (?, ?, ?, ?)",
5394
- [itemId, phaseIdx, name, manualChecks]
5395
- );
5396
- for (let i = 0; i < tasks.length; i++) {
5397
- await tx.run(
5398
- "INSERT INTO plan_tasks (item_id, phase_idx, idx, task) VALUES (?, ?, ?, ?)",
5399
- [itemId, phaseIdx, i, tasks[i]]
5400
- );
5423
+ import { eq as eq11 } from "drizzle-orm";
5424
+
5425
+ // src/commands/backlog/shiftPhasesUp.ts
5426
+ import { and as and3, desc, eq as eq10, gte } from "drizzle-orm";
5427
+ async function shiftPhasesUp(db, itemId, fromIdx) {
5428
+ const toShift = await db.select({ idx: planPhases.idx }).from(planPhases).where(and3(eq10(planPhases.itemId, itemId), gte(planPhases.idx, fromIdx))).orderBy(desc(planPhases.idx));
5429
+ for (const p of toShift) {
5430
+ await db.update(planTasks).set({ phaseIdx: p.idx + 1 }).where(and3(eq10(planTasks.itemId, itemId), eq10(planTasks.phaseIdx, p.idx)));
5431
+ await db.update(planPhases).set({ idx: p.idx + 1 }).where(and3(eq10(planPhases.itemId, itemId), eq10(planPhases.idx, p.idx)));
5432
+ }
5433
+ }
5434
+
5435
+ // src/commands/backlog/insertPhaseAt.ts
5436
+ async function insertPhaseAt(orm, itemId, phaseIdx, name, tasks, manualChecks, currentPhase) {
5437
+ await orm.transaction(async (tx) => {
5438
+ await shiftPhasesUp(tx, itemId, phaseIdx);
5439
+ await tx.insert(planPhases).values({ itemId, idx: phaseIdx, name, manualChecks });
5440
+ if (tasks.length) {
5441
+ await tx.insert(planTasks).values(tasks.map((task, i) => ({ itemId, phaseIdx, idx: i, task })));
5401
5442
  }
5402
5443
  if (currentPhase !== void 0 && currentPhase - 1 >= phaseIdx) {
5403
- await tx.run("UPDATE items SET current_phase = ? WHERE id = ?", [
5404
- currentPhase + 1,
5405
- itemId
5406
- ]);
5444
+ await tx.update(items).set({ currentPhase: currentPhase + 1 }).where(eq11(items.id, itemId));
5407
5445
  }
5408
5446
  });
5409
5447
  }
5410
5448
 
5411
5449
  // src/commands/backlog/resolveInsertPosition.ts
5412
5450
  import chalk53 from "chalk";
5413
- async function resolveInsertPosition(db, itemId, position) {
5414
- const row = await db.get(
5415
- "SELECT COUNT(*)::int as cnt FROM plan_phases WHERE item_id = ?",
5416
- [itemId]
5417
- );
5451
+ import { count, eq as eq12 } from "drizzle-orm";
5452
+ async function resolveInsertPosition(orm, itemId, position) {
5453
+ const [row] = await orm.select({ cnt: count() }).from(planPhases).where(eq12(planPhases.itemId, itemId));
5418
5454
  const phaseCount = row?.cnt ?? 0;
5419
5455
  if (position === void 0) return phaseCount;
5420
5456
  const pos = Number.parseInt(position, 10);
@@ -5445,12 +5481,12 @@ async function addPhase(id, name, options2) {
5445
5481
  process.exitCode = 1;
5446
5482
  return;
5447
5483
  }
5448
- const db = await getBacklogDb();
5484
+ const orm = await getBacklogOrm();
5449
5485
  const itemId = result.item.id;
5450
- const phaseIdx = await resolveInsertPosition(db, itemId, options2.position);
5486
+ const phaseIdx = await resolveInsertPosition(orm, itemId, options2.position);
5451
5487
  if (phaseIdx === void 0) return;
5452
5488
  await insertPhaseAt(
5453
- db,
5489
+ orm,
5454
5490
  itemId,
5455
5491
  phaseIdx,
5456
5492
  name,
@@ -5469,7 +5505,7 @@ async function addPhase(id, name, options2) {
5469
5505
  // src/commands/backlog/init/index.ts
5470
5506
  import chalk55 from "chalk";
5471
5507
  async function init6() {
5472
- await getBacklogDb();
5508
+ await getBacklogOrm();
5473
5509
  console.log(
5474
5510
  chalk55.green(
5475
5511
  `Backlog database ready. This repository maps to origin: ${getOrigin()}`
@@ -5493,22 +5529,22 @@ function originDisplayName(origin) {
5493
5529
  }
5494
5530
 
5495
5531
  // src/commands/backlog/list/index.ts
5496
- function filterItems(items, options2) {
5497
- if (options2.status) return items.filter((i) => i.status === options2.status);
5532
+ function filterItems(items2, options2) {
5533
+ if (options2.status) return items2.filter((i) => i.status === options2.status);
5498
5534
  if (!options2.all)
5499
- return items.filter((i) => i.status !== "done" && i.status !== "wontdo");
5500
- return items;
5535
+ return items2.filter((i) => i.status !== "done" && i.status !== "wontdo");
5536
+ return items2;
5501
5537
  }
5502
5538
  async function list2(options2) {
5503
5539
  const allItems = await loadBacklog(options2.allRepos);
5504
- const items = filterItems(allItems, options2);
5505
- if (items.length === 0) {
5540
+ const items2 = filterItems(allItems, options2);
5541
+ if (items2.length === 0) {
5506
5542
  console.log(chalk56.dim("Backlog is empty."));
5507
5543
  return;
5508
5544
  }
5509
5545
  const repoNameOf = (item) => item.origin ? originDisplayName(item.origin) : "";
5510
- const prefixWidth = options2.allRepos ? Math.max(0, ...items.map((i) => repoNameOf(i).length)) : 0;
5511
- for (const item of items) {
5546
+ const prefixWidth = options2.allRepos ? Math.max(0, ...items2.map((i) => repoNameOf(i).length)) : 0;
5547
+ for (const item of items2) {
5512
5548
  const repoPrefix2 = options2.allRepos ? `${chalk56.dim(repoNameOf(item).padEnd(prefixWidth))} ` : "";
5513
5549
  console.log(
5514
5550
  `${repoPrefix2}${statusIcon(item.status)} ${typeLabel(item.type)} ${chalk56.dim(`#${item.id}`)} ${item.name}${phaseLabel(item)}${dependencyLabel(item, allItems)}`
@@ -5540,7 +5576,7 @@ function registerItemCommands(cmd) {
5540
5576
  import chalk58 from "chalk";
5541
5577
 
5542
5578
  // src/commands/backlog/hasCycle.ts
5543
- function hasCycle(items, fromId, toId) {
5579
+ function hasCycle(adjacency, fromId, toId) {
5544
5580
  const visited = /* @__PURE__ */ new Set();
5545
5581
  const stack = [toId];
5546
5582
  while (stack.length > 0) {
@@ -5548,100 +5584,107 @@ function hasCycle(items, fromId, toId) {
5548
5584
  if (current === fromId) return true;
5549
5585
  if (visited.has(current)) continue;
5550
5586
  visited.add(current);
5551
- const item = items.find((i) => i.id === current);
5552
- if (!item?.links) continue;
5553
- for (const link3 of item.links) {
5554
- if (link3.type === "depends-on") {
5555
- stack.push(link3.targetId);
5556
- }
5587
+ for (const target of adjacency.get(current) ?? []) {
5588
+ stack.push(target);
5557
5589
  }
5558
5590
  }
5559
5591
  return false;
5560
5592
  }
5561
5593
 
5594
+ // src/commands/backlog/loadDependencyGraph.ts
5595
+ import { eq as eq13 } from "drizzle-orm";
5596
+ async function loadDependencyGraph(orm) {
5597
+ const rows = await orm.select({ itemId: links.itemId, targetId: links.targetId }).from(links).where(eq13(links.type, "depends-on"));
5598
+ const graph = /* @__PURE__ */ new Map();
5599
+ for (const { itemId, targetId } of rows) {
5600
+ const bucket = graph.get(itemId);
5601
+ if (bucket) bucket.push(targetId);
5602
+ else graph.set(itemId, [targetId]);
5603
+ }
5604
+ return graph;
5605
+ }
5606
+
5607
+ // src/commands/backlog/loadItem.ts
5608
+ import { eq as eq14 } from "drizzle-orm";
5609
+ async function loadItem(orm, id) {
5610
+ const [row] = await orm.select().from(items).where(eq14(items.id, id));
5611
+ if (!row) return void 0;
5612
+ const rel = await loadRelations(orm, [id]);
5613
+ return rowToItem(row, rel);
5614
+ }
5615
+
5562
5616
  // src/commands/backlog/validateLinkTarget.ts
5563
5617
  import chalk57 from "chalk";
5564
- function validateLinkTarget(items, fromItem, fromId, toId, toNum, linkType) {
5565
- const toItem = items.find((i) => i.id === toNum);
5566
- if (!toItem) {
5567
- console.log(chalk57.red(`Item #${toId} not found.`));
5568
- return void 0;
5569
- }
5570
- if (!fromItem.links) fromItem.links = [];
5571
- const duplicate = fromItem.links.some(
5618
+ function validateLinkTarget(fromItem, fromNum, toNum, linkType) {
5619
+ const duplicate = (fromItem.links ?? []).some(
5572
5620
  (l) => l.targetId === toNum && l.type === linkType
5573
5621
  );
5574
5622
  if (duplicate) {
5575
5623
  console.log(
5576
- chalk57.yellow(`Link already exists: #${fromId} ${linkType} #${toId}`)
5624
+ chalk57.yellow(`Link already exists: #${fromNum} ${linkType} #${toNum}`)
5577
5625
  );
5578
- return void 0;
5626
+ return false;
5579
5627
  }
5580
- return toItem;
5628
+ return true;
5581
5629
  }
5582
5630
 
5583
5631
  // src/commands/backlog/link.ts
5632
+ function fail(message) {
5633
+ console.log(chalk58.red(message));
5634
+ return void 0;
5635
+ }
5636
+ function parseLinkType(type) {
5637
+ const linkType = type ?? "relates-to";
5638
+ if (linkType === "relates-to" || linkType === "depends-on") return linkType;
5639
+ return fail(`Invalid link type: ${linkType}`);
5640
+ }
5641
+ async function createsCycle(orm, linkType, fromNum, toNum) {
5642
+ if (linkType !== "depends-on") return false;
5643
+ const graph = await loadDependencyGraph(orm);
5644
+ if (!hasCycle(graph, fromNum, toNum)) return false;
5645
+ fail(`Cannot add dependency: #${fromNum} \u2192 #${toNum} would create a cycle.`);
5646
+ return true;
5647
+ }
5584
5648
  async function link(fromId, toId, opts) {
5585
- const linkType = opts.type ?? "relates-to";
5586
- if (linkType !== "relates-to" && linkType !== "depends-on") {
5587
- console.log(chalk58.red(`Invalid link type: ${linkType}`));
5588
- return;
5589
- }
5649
+ const linkType = parseLinkType(opts.type);
5650
+ if (!linkType) return;
5590
5651
  const fromNum = Number.parseInt(fromId, 10);
5591
5652
  const toNum = Number.parseInt(toId, 10);
5592
- if (fromNum === toNum) {
5593
- console.log(chalk58.red("Cannot link an item to itself."));
5594
- return;
5595
- }
5596
- const result = await loadAndFindItem(fromId);
5597
- if (!result) return;
5598
- const { items, item: fromItem } = result;
5599
- const toItem = validateLinkTarget(
5600
- items,
5601
- fromItem,
5602
- fromId,
5603
- toId,
5604
- toNum,
5605
- linkType
5606
- );
5607
- if (!toItem) return;
5608
- if (linkType === "depends-on" && hasCycle(items, fromNum, toNum)) {
5609
- console.log(
5610
- chalk58.red(
5611
- `Cannot add dependency: #${fromId} \u2192 #${toId} would create a circular dependency.`
5612
- )
5613
- );
5614
- return;
5615
- }
5616
- if (!fromItem.links) fromItem.links = [];
5617
- fromItem.links.push({ type: linkType, targetId: toNum });
5618
- await saveBacklog(items);
5653
+ if (fromNum === toNum) return void fail("Cannot link an item to itself.");
5654
+ const { orm } = await getReady();
5655
+ const fromItem = await loadItem(orm, fromNum);
5656
+ if (!fromItem) return void fail(`Item #${fromNum} not found.`);
5657
+ const toItem = await loadItem(orm, toNum);
5658
+ if (!toItem) return void fail(`Item #${toNum} not found.`);
5659
+ if (!validateLinkTarget(fromItem, fromNum, toNum, linkType)) return;
5660
+ if (await createsCycle(orm, linkType, fromNum, toNum)) return;
5661
+ await orm.insert(links).values({ itemId: fromNum, type: linkType, targetId: toNum });
5619
5662
  console.log(
5620
- chalk58.green(`Linked #${fromId} ${linkType} #${toId} (${toItem.name})`)
5663
+ chalk58.green(`Linked #${fromNum} ${linkType} #${toNum} (${toItem.name})`)
5621
5664
  );
5622
5665
  }
5623
5666
 
5624
5667
  // src/commands/backlog/unlink.ts
5625
5668
  import chalk59 from "chalk";
5669
+ import { and as and4, eq as eq15 } from "drizzle-orm";
5626
5670
  async function unlink(fromId, toId) {
5671
+ const fromNum = Number.parseInt(fromId, 10);
5627
5672
  const toNum = Number.parseInt(toId, 10);
5628
- const result = await loadAndFindItem(fromId);
5629
- if (!result) return;
5630
- const { items, item: fromItem } = result;
5673
+ const { orm } = await getReady();
5674
+ const fromItem = await loadItem(orm, fromNum);
5675
+ if (!fromItem) {
5676
+ console.log(chalk59.red(`Item #${fromId} not found.`));
5677
+ return;
5678
+ }
5631
5679
  if (!fromItem.links || fromItem.links.length === 0) {
5632
5680
  console.log(chalk59.yellow(`No links found on item #${fromId}.`));
5633
5681
  return;
5634
5682
  }
5635
- const before = fromItem.links.length;
5636
- fromItem.links = fromItem.links.filter((l) => l.targetId !== toNum);
5637
- if (fromItem.links.length === before) {
5683
+ if (!fromItem.links.some((l) => l.targetId === toNum)) {
5638
5684
  console.log(chalk59.yellow(`No link from #${fromId} to #${toId} found.`));
5639
5685
  return;
5640
5686
  }
5641
- if (fromItem.links.length === 0) {
5642
- fromItem.links = void 0;
5643
- }
5644
- await saveBacklog(items);
5687
+ await orm.delete(links).where(and4(eq15(links.itemId, fromNum), eq15(links.targetId, toNum)));
5645
5688
  console.log(chalk59.green(`Removed link from #${fromId} to #${toId}.`));
5646
5689
  }
5647
5690
 
@@ -5679,9 +5722,12 @@ function validateRewind2(item, phaseNumber) {
5679
5722
  async function rewindPhase(id, phase, opts) {
5680
5723
  const phaseNumber = Number.parseInt(phase, 10);
5681
5724
  const phaseIndex = phaseNumber - 1;
5682
- const result = await loadAndFindItem(id);
5683
- if (!result) return;
5684
- const { item } = result;
5725
+ const { orm } = await getReady();
5726
+ const item = await loadItem(orm, Number.parseInt(id, 10));
5727
+ if (!item) {
5728
+ console.log(chalk60.red(`Item #${id} not found.`));
5729
+ return;
5730
+ }
5685
5731
  const error = validateRewind2(item, phaseNumber);
5686
5732
  if (error) {
5687
5733
  console.log(chalk60.red(error));
@@ -5689,12 +5735,12 @@ async function rewindPhase(id, phase, opts) {
5689
5735
  return;
5690
5736
  }
5691
5737
  const phaseName = item.plan?.[phaseIndex].name;
5692
- addComment(
5693
- item,
5738
+ await appendComment(
5739
+ orm,
5740
+ item.id,
5694
5741
  `Rewound to phase ${phaseNumber} (${phaseName}): ${opts.reason}`,
5695
- phaseNumber
5742
+ { phase: phaseNumber }
5696
5743
  );
5697
- await saveBacklog(result.items);
5698
5744
  await setCurrentPhase(id, phaseNumber);
5699
5745
  await setStatus(id, "in-progress");
5700
5746
  writeSignal("rewind", {
@@ -5721,18 +5767,18 @@ function registerRunCommand(cmd) {
5721
5767
  // src/commands/backlog/search/index.ts
5722
5768
  import chalk61 from "chalk";
5723
5769
  async function search(query) {
5724
- const items = await searchBacklog(query);
5725
- if (items.length === 0) {
5770
+ const items2 = await searchBacklog(query);
5771
+ if (items2.length === 0) {
5726
5772
  console.log(chalk61.dim(`No items matching "${query}".`));
5727
5773
  return;
5728
5774
  }
5729
5775
  console.log(
5730
5776
  chalk61.dim(
5731
- `${items.length} item${items.length === 1 ? "" : "s"} matching "${query}":
5777
+ `${items2.length} item${items2.length === 1 ? "" : "s"} matching "${query}":
5732
5778
  `
5733
5779
  )
5734
5780
  );
5735
- for (const item of items) {
5781
+ for (const item of items2) {
5736
5782
  console.log(
5737
5783
  `${statusIcon(item.status)} ${typeLabel(item.type)} ${chalk61.dim(`#${item.id}`)} ${item.name}`
5738
5784
  );
@@ -5796,8 +5842,8 @@ async function start(id) {
5796
5842
  // src/commands/backlog/stop/index.ts
5797
5843
  import chalk65 from "chalk";
5798
5844
  async function stop() {
5799
- const items = await loadBacklog();
5800
- const inProgress = items.filter((item) => item.status === "in-progress");
5845
+ const items2 = await loadBacklog();
5846
+ const inProgress = items2.filter((item) => item.status === "in-progress");
5801
5847
  if (inProgress.length === 0) {
5802
5848
  console.log(chalk65.yellow("No in-progress items to stop."));
5803
5849
  return;
@@ -5806,7 +5852,7 @@ async function stop() {
5806
5852
  item.status = "todo";
5807
5853
  item.currentPhase = 1;
5808
5854
  }
5809
- await saveBacklog(items);
5855
+ await saveBacklog(items2);
5810
5856
  for (const item of inProgress) {
5811
5857
  console.log(chalk65.yellow(`Stopped item #${item.id}: ${item.name}`));
5812
5858
  }
@@ -5837,19 +5883,18 @@ function registerStatusCommands(cmd) {
5837
5883
 
5838
5884
  // src/commands/backlog/removePhase.ts
5839
5885
  import chalk68 from "chalk";
5886
+ import { and as and7, eq as eq18 } from "drizzle-orm";
5840
5887
 
5841
5888
  // src/commands/backlog/findPhase.ts
5842
5889
  import chalk67 from "chalk";
5890
+ import { and as and5, count as count2, eq as eq16 } from "drizzle-orm";
5843
5891
  async function findPhase(id, phase) {
5844
5892
  const result = await loadAndFindItem(id);
5845
5893
  if (!result) return void 0;
5846
- const db = await getBacklogDb();
5894
+ const orm = await getBacklogOrm();
5847
5895
  const itemId = result.item.id;
5848
5896
  const phaseIdx = Number.parseInt(phase, 10) - 1;
5849
- const row = await db.get(
5850
- "SELECT COUNT(*)::int as cnt FROM plan_phases WHERE item_id = ? AND idx = ?",
5851
- [itemId, phaseIdx]
5852
- );
5897
+ const [row] = await orm.select({ cnt: count2() }).from(planPhases).where(and5(eq16(planPhases.itemId, itemId), eq16(planPhases.idx, phaseIdx)));
5853
5898
  if (!row || row.cnt === 0) {
5854
5899
  console.log(
5855
5900
  chalk67.red(`Phase ${phaseIdx + 1} not found on item #${itemId}.`)
@@ -5857,26 +5902,18 @@ async function findPhase(id, phase) {
5857
5902
  process.exitCode = 1;
5858
5903
  return void 0;
5859
5904
  }
5860
- return { result, db, itemId, phaseIdx };
5905
+ return { result, orm, itemId, phaseIdx };
5861
5906
  }
5862
5907
 
5863
5908
  // src/commands/backlog/reindexPhases.ts
5909
+ import { and as and6, asc as asc4, count as count3, eq as eq17 } from "drizzle-orm";
5864
5910
  async function reindexPhases(db, itemId) {
5865
- const remaining = await db.all(
5866
- "SELECT idx FROM plan_phases WHERE item_id = ? ORDER BY idx",
5867
- [itemId]
5868
- );
5911
+ const remaining = await db.select({ idx: planPhases.idx }).from(planPhases).where(eq17(planPhases.itemId, itemId)).orderBy(asc4(planPhases.idx));
5869
5912
  for (let i = 0; i < remaining.length; i++) {
5870
5913
  const oldIdx = remaining[i].idx;
5871
5914
  if (oldIdx === i) continue;
5872
- await db.run(
5873
- "UPDATE plan_tasks SET phase_idx = ? WHERE item_id = ? AND phase_idx = ?",
5874
- [i, itemId, oldIdx]
5875
- );
5876
- await db.run(
5877
- "UPDATE plan_phases SET idx = ? WHERE item_id = ? AND idx = ?",
5878
- [i, itemId, oldIdx]
5879
- );
5915
+ await db.update(planTasks).set({ phaseIdx: i }).where(and6(eq17(planTasks.itemId, itemId), eq17(planTasks.phaseIdx, oldIdx)));
5916
+ await db.update(planPhases).set({ idx: i }).where(and6(eq17(planPhases.itemId, itemId), eq17(planPhases.idx, oldIdx)));
5880
5917
  }
5881
5918
  }
5882
5919
  async function adjustCurrentPhase(db, item, removedIdx) {
@@ -5884,38 +5921,25 @@ async function adjustCurrentPhase(db, item, removedIdx) {
5884
5921
  if (currentPhase === void 0) return;
5885
5922
  const currentIdx = currentPhase - 1;
5886
5923
  if (removedIdx < currentIdx) {
5887
- await db.run("UPDATE items SET current_phase = ? WHERE id = ?", [
5888
- currentPhase - 1,
5889
- item.id
5890
- ]);
5924
+ await db.update(items).set({ currentPhase: currentPhase - 1 }).where(eq17(items.id, item.id));
5891
5925
  return;
5892
5926
  }
5893
5927
  if (removedIdx !== currentIdx) return;
5894
- const row = await db.get(
5895
- "SELECT COUNT(*)::int as cnt FROM plan_phases WHERE item_id = ?",
5896
- [item.id]
5897
- );
5928
+ const [row] = await db.select({ cnt: count3() }).from(planPhases).where(eq17(planPhases.itemId, item.id));
5898
5929
  const cnt = row?.cnt ?? 0;
5899
- await db.run("UPDATE items SET current_phase = ? WHERE id = ?", [
5900
- cnt === 0 ? null : Math.min(currentPhase, cnt),
5901
- item.id
5902
- ]);
5930
+ await db.update(items).set({ currentPhase: cnt === 0 ? null : Math.min(currentPhase, cnt) }).where(eq17(items.id, item.id));
5903
5931
  }
5904
5932
 
5905
5933
  // src/commands/backlog/removePhase.ts
5906
5934
  async function removePhase(id, phase) {
5907
5935
  const found = await findPhase(id, phase);
5908
5936
  if (!found) return;
5909
- const { result, db, itemId, phaseIdx } = found;
5910
- await db.transaction(async (tx) => {
5911
- await tx.run("DELETE FROM plan_tasks WHERE item_id = ? AND phase_idx = ?", [
5912
- itemId,
5913
- phaseIdx
5914
- ]);
5915
- await tx.run("DELETE FROM plan_phases WHERE item_id = ? AND idx = ?", [
5916
- itemId,
5917
- phaseIdx
5918
- ]);
5937
+ const { result, orm, itemId, phaseIdx } = found;
5938
+ await orm.transaction(async (tx) => {
5939
+ await tx.delete(planTasks).where(
5940
+ and7(eq18(planTasks.itemId, itemId), eq18(planTasks.phaseIdx, phaseIdx))
5941
+ );
5942
+ await tx.delete(planPhases).where(and7(eq18(planPhases.itemId, itemId), eq18(planPhases.idx, phaseIdx)));
5919
5943
  await reindexPhases(tx, itemId);
5920
5944
  await adjustCurrentPhase(tx, result.item, phaseIdx);
5921
5945
  });
@@ -5926,20 +5950,21 @@ async function removePhase(id, phase) {
5926
5950
 
5927
5951
  // src/commands/backlog/update/index.ts
5928
5952
  import chalk70 from "chalk";
5953
+ import { eq as eq19 } from "drizzle-orm";
5929
5954
 
5930
5955
  // src/commands/backlog/update/parseListIndex.ts
5931
5956
  function parseListIndex(raw, length, label2) {
5932
5957
  if (!/^\d+$/.test(raw)) {
5933
5958
  return { ok: false, error: `${label2} must be a positive integer.` };
5934
5959
  }
5935
- const index = Number.parseInt(raw, 10);
5936
- if (index < 1 || index > length) {
5960
+ const index2 = Number.parseInt(raw, 10);
5961
+ if (index2 < 1 || index2 > length) {
5937
5962
  return {
5938
5963
  ok: false,
5939
- error: `${label2} ${index} is out of range (1-${length}).`
5964
+ error: `${label2} ${index2} is out of range (1-${length}).`
5940
5965
  };
5941
5966
  }
5942
- return { ok: true, index };
5967
+ return { ok: true, index: index2 };
5943
5968
  }
5944
5969
 
5945
5970
  // src/commands/backlog/update/applyListMutations.ts
@@ -5949,7 +5974,7 @@ function hasListMutations(options2) {
5949
5974
  );
5950
5975
  }
5951
5976
  function applyListMutations(current, options2, flags) {
5952
- let items = [...current];
5977
+ let items2 = [...current];
5953
5978
  if (options2.edit) {
5954
5979
  const [rawIndex, ...textParts] = options2.edit;
5955
5980
  if (rawIndex === void 0 || textParts.length === 0) {
@@ -5960,25 +5985,25 @@ function applyListMutations(current, options2, flags) {
5960
5985
  }
5961
5986
  const parsed = parseListIndex(
5962
5987
  rawIndex,
5963
- items.length,
5988
+ items2.length,
5964
5989
  `${flags.edit} index`
5965
5990
  );
5966
5991
  if (!parsed.ok) return parsed;
5967
- items[parsed.index - 1] = textParts.join(" ");
5992
+ items2[parsed.index - 1] = textParts.join(" ");
5968
5993
  }
5969
5994
  if (options2.remove) {
5970
5995
  const parsed = parseListIndex(
5971
5996
  options2.remove,
5972
- items.length,
5997
+ items2.length,
5973
5998
  `${flags.remove} index`
5974
5999
  );
5975
6000
  if (!parsed.ok) return parsed;
5976
- items = items.filter((_, i) => i !== parsed.index - 1);
6001
+ items2 = items2.filter((_, i) => i !== parsed.index - 1);
5977
6002
  }
5978
6003
  if (options2.add) {
5979
- items.push(...options2.add);
6004
+ items2.push(...options2.add);
5980
6005
  }
5981
- return { ok: true, items };
6006
+ return { ok: true, items: items2 };
5982
6007
  }
5983
6008
 
5984
6009
  // src/commands/backlog/update/applyAcMutations.ts
@@ -5999,11 +6024,11 @@ function applyAcMutations(current, options2) {
5999
6024
  return { ok: true, criteria: result.items };
6000
6025
  }
6001
6026
 
6002
- // src/commands/backlog/update/buildUpdateSql.ts
6027
+ // src/commands/backlog/update/buildUpdateValues.ts
6003
6028
  import chalk69 from "chalk";
6004
- function buildUpdateSql(options2) {
6005
- const { name, desc, type, ac } = options2;
6006
- if (!name && !desc && !type && !ac) {
6029
+ function buildUpdateValues(options2) {
6030
+ const { name, desc: desc2, type, ac } = options2;
6031
+ if (!name && !desc2 && !type && !ac) {
6007
6032
  console.log(chalk69.red("Nothing to update. Provide at least one flag."));
6008
6033
  process.exitCode = 1;
6009
6034
  return void 0;
@@ -6013,30 +6038,25 @@ function buildUpdateSql(options2) {
6013
6038
  process.exitCode = 1;
6014
6039
  return void 0;
6015
6040
  }
6016
- const sets = [];
6017
- const params = [];
6041
+ const set = {};
6018
6042
  const fieldNames = [];
6019
6043
  if (name) {
6020
- sets.push("name = ?");
6021
- params.push(name);
6044
+ set.name = name;
6022
6045
  fieldNames.push("name");
6023
6046
  }
6024
- if (desc) {
6025
- sets.push("description = ?");
6026
- params.push(desc);
6047
+ if (desc2) {
6048
+ set.description = desc2;
6027
6049
  fieldNames.push("description");
6028
6050
  }
6029
6051
  if (type) {
6030
- sets.push("type = ?");
6031
- params.push(type);
6052
+ set.type = type;
6032
6053
  fieldNames.push("type");
6033
6054
  }
6034
6055
  if (ac) {
6035
- sets.push("acceptance_criteria = ?");
6036
- params.push(JSON.stringify(ac));
6056
+ set.acceptanceCriteria = JSON.stringify(ac);
6037
6057
  fieldNames.push("acceptance criteria");
6038
6058
  }
6039
- return { sets, params, fields: fieldNames.join(", ") };
6059
+ return { set, fields: fieldNames.join(", ") };
6040
6060
  }
6041
6061
 
6042
6062
  // src/commands/backlog/update/index.ts
@@ -6060,14 +6080,11 @@ async function update(id, options2) {
6060
6080
  }
6061
6081
  ac = mutation.criteria;
6062
6082
  }
6063
- const built = buildUpdateSql({ ...options2, ac });
6083
+ const built = buildUpdateValues({ ...options2, ac });
6064
6084
  if (!built) return;
6065
- const db = await getBacklogDb();
6085
+ const orm = await getBacklogOrm();
6066
6086
  const itemId = result.item.id;
6067
- await db.run(`UPDATE items SET ${built.sets.join(", ")} WHERE id = ?`, [
6068
- ...built.params,
6069
- itemId
6070
- ]);
6087
+ await orm.update(items).set(built.set).where(eq19(items.id, itemId));
6071
6088
  console.log(chalk70.green(`Updated ${built.fields} on item #${itemId}.`));
6072
6089
  }
6073
6090
 
@@ -6075,29 +6092,31 @@ async function update(id, options2) {
6075
6092
  import chalk71 from "chalk";
6076
6093
 
6077
6094
  // src/commands/backlog/applyPhaseUpdate.ts
6078
- async function applyPhaseUpdate(db, itemId, phaseIdx, fields) {
6079
- await db.transaction(async (tx) => {
6095
+ import { and as and8, eq as eq20 } from "drizzle-orm";
6096
+ async function applyPhaseUpdate(orm, itemId, phaseIdx, fields) {
6097
+ await orm.transaction(async (tx) => {
6080
6098
  if (fields.name) {
6081
- await tx.run(
6082
- "UPDATE plan_phases SET name = ? WHERE item_id = ? AND idx = ?",
6083
- [fields.name, itemId, phaseIdx]
6099
+ await tx.update(planPhases).set({ name: fields.name }).where(
6100
+ and8(eq20(planPhases.itemId, itemId), eq20(planPhases.idx, phaseIdx))
6084
6101
  );
6085
6102
  }
6086
6103
  if (fields.manualCheck) {
6087
- await tx.run(
6088
- "UPDATE plan_phases SET manual_checks = ? WHERE item_id = ? AND idx = ?",
6089
- [JSON.stringify(fields.manualCheck), itemId, phaseIdx]
6104
+ await tx.update(planPhases).set({ manualChecks: JSON.stringify(fields.manualCheck) }).where(
6105
+ and8(eq20(planPhases.itemId, itemId), eq20(planPhases.idx, phaseIdx))
6090
6106
  );
6091
6107
  }
6092
6108
  if (fields.task) {
6093
- await tx.run(
6094
- "DELETE FROM plan_tasks WHERE item_id = ? AND phase_idx = ?",
6095
- [itemId, phaseIdx]
6109
+ await tx.delete(planTasks).where(
6110
+ and8(eq20(planTasks.itemId, itemId), eq20(planTasks.phaseIdx, phaseIdx))
6096
6111
  );
6097
- for (let i = 0; i < fields.task.length; i++) {
6098
- await tx.run(
6099
- "INSERT INTO plan_tasks (item_id, phase_idx, idx, task) VALUES (?, ?, ?, ?)",
6100
- [itemId, phaseIdx, i, fields.task[i]]
6112
+ if (fields.task.length) {
6113
+ await tx.insert(planTasks).values(
6114
+ fields.task.map((task, i) => ({
6115
+ itemId,
6116
+ phaseIdx,
6117
+ idx: i,
6118
+ task
6119
+ }))
6101
6120
  );
6102
6121
  }
6103
6122
  }
@@ -6171,7 +6190,7 @@ function resolvePhaseFields(options2, current) {
6171
6190
  async function updatePhase(id, phase, options2) {
6172
6191
  const found = await findPhase(id, phase);
6173
6192
  if (!found) return;
6174
- const { result, db, itemId, phaseIdx } = found;
6193
+ const { result, orm, itemId, phaseIdx } = found;
6175
6194
  const resolved = resolvePhaseFields(options2, result.item.plan?.[phaseIdx]);
6176
6195
  if (!resolved.ok) {
6177
6196
  console.log(chalk71.red(resolved.error));
@@ -6179,7 +6198,7 @@ async function updatePhase(id, phase, options2) {
6179
6198
  return;
6180
6199
  }
6181
6200
  const { name, task, manualCheck } = resolved.fields;
6182
- await applyPhaseUpdate(db, itemId, phaseIdx, { name, task, manualCheck });
6201
+ await applyPhaseUpdate(orm, itemId, phaseIdx, { name, task, manualCheck });
6183
6202
  const fields = [
6184
6203
  name && "name",
6185
6204
  task && "tasks",
@@ -6380,7 +6399,7 @@ import { mkdirSync as mkdirSync4 } from "fs";
6380
6399
  import { homedir as homedir4 } from "os";
6381
6400
  import { join as join18 } from "path";
6382
6401
  import Database from "better-sqlite3";
6383
- var _db2;
6402
+ var _db;
6384
6403
  function getDbDir() {
6385
6404
  return join18(homedir4(), ".assist");
6386
6405
  }
@@ -6398,13 +6417,13 @@ function initSchema(db) {
6398
6417
  `);
6399
6418
  }
6400
6419
  function openPromptsDb(dir) {
6401
- if (_db2) return _db2;
6420
+ if (_db) return _db;
6402
6421
  const dbDir = dir ?? getDbDir();
6403
6422
  mkdirSync4(dbDir, { recursive: true });
6404
6423
  const db = new Database(join18(dbDir, "assist.db"));
6405
6424
  db.pragma("journal_mode = WAL");
6406
6425
  initSchema(db);
6407
- _db2 = db;
6426
+ _db = db;
6408
6427
  return db;
6409
6428
  }
6410
6429
  function logDeniedToolCall(entry) {
@@ -6881,17 +6900,17 @@ function isClaudeCode() {
6881
6900
  }
6882
6901
 
6883
6902
  // src/commands/permitCliReads/mapAsync.ts
6884
- async function mapAsync(items, concurrency, fn) {
6885
- const results = new Array(items.length);
6903
+ async function mapAsync(items2, concurrency, fn) {
6904
+ const results = new Array(items2.length);
6886
6905
  let next3 = 0;
6887
6906
  async function worker() {
6888
- while (next3 < items.length) {
6907
+ while (next3 < items2.length) {
6889
6908
  const idx = next3++;
6890
- results[idx] = await fn(items[idx]);
6909
+ results[idx] = await fn(items2[idx]);
6891
6910
  }
6892
6911
  }
6893
6912
  const workers = Array.from(
6894
- { length: Math.min(concurrency, items.length) },
6913
+ { length: Math.min(concurrency, items2.length) },
6895
6914
  () => worker()
6896
6915
  );
6897
6916
  await Promise.all(workers);
@@ -7089,8 +7108,8 @@ function formatHuman(cli, commands) {
7089
7108
  `];
7090
7109
  for (const cmd of sorted) {
7091
7110
  const full = `${cli} ${cmd.path.join(" ")}`;
7092
- const text = cmd.description ? `${full} \u2014 ${cmd.description}` : full;
7093
- lines.push(`${prefix(classifyVerb(cmd.path))}${text}`);
7111
+ const text2 = cmd.description ? `${full} \u2014 ${cmd.description}` : full;
7112
+ lines.push(`${prefix(classifyVerb(cmd.path))}${text2}`);
7094
7113
  }
7095
7114
  return lines.join("\n");
7096
7115
  }
@@ -7225,12 +7244,12 @@ function denyList() {
7225
7244
  import chalk75 from "chalk";
7226
7245
  function denyRemove(pattern2, options2) {
7227
7246
  const { deny, saveDeny } = loadDenyConfig(options2.global);
7228
- const index = deny.findIndex((r) => r.pattern === pattern2);
7229
- if (index === -1) {
7247
+ const index2 = deny.findIndex((r) => r.pattern === pattern2);
7248
+ if (index2 === -1) {
7230
7249
  console.log(chalk75.yellow(`No deny rule found for: ${pattern2}`));
7231
7250
  return;
7232
7251
  }
7233
- deny.splice(index, 1);
7252
+ deny.splice(index2, 1);
7234
7253
  saveDeny(deny.length > 0 ? deny : void 0);
7235
7254
  console.log(chalk75.green(`Removed deny rule: ${pattern2}`));
7236
7255
  }
@@ -7467,7 +7486,7 @@ function calculateHalstead(node) {
7467
7486
  // src/commands/complexity/shared/countSloc.ts
7468
7487
  function countSloc(content) {
7469
7488
  let inMultiLineComment = false;
7470
- let count = 0;
7489
+ let count4 = 0;
7471
7490
  for (const line of content.split("\n")) {
7472
7491
  const trimmed = line.trim();
7473
7492
  if (inMultiLineComment) {
@@ -7475,7 +7494,7 @@ function countSloc(content) {
7475
7494
  inMultiLineComment = false;
7476
7495
  const afterComment = trimmed.substring(trimmed.indexOf("*/") + 2);
7477
7496
  if (afterComment.trim().length > 0) {
7478
- count++;
7497
+ count4++;
7479
7498
  }
7480
7499
  }
7481
7500
  continue;
@@ -7487,7 +7506,7 @@ function countSloc(content) {
7487
7506
  if (trimmed.includes("*/")) {
7488
7507
  const afterComment = trimmed.substring(trimmed.indexOf("*/") + 2);
7489
7508
  if (afterComment.trim().length > 0) {
7490
- count++;
7509
+ count4++;
7491
7510
  }
7492
7511
  } else {
7493
7512
  inMultiLineComment = true;
@@ -7495,10 +7514,10 @@ function countSloc(content) {
7495
7514
  continue;
7496
7515
  }
7497
7516
  if (trimmed.length > 0) {
7498
- count++;
7517
+ count4++;
7499
7518
  }
7500
7519
  }
7501
- return count;
7520
+ return count4;
7502
7521
  }
7503
7522
 
7504
7523
  // src/commands/complexity/shared/index.ts
@@ -9193,16 +9212,16 @@ function parseUserLine(line) {
9193
9212
  if (entry.type !== "user") return void 0;
9194
9213
  const msg = entry.message;
9195
9214
  const c = msg?.content;
9196
- let text;
9215
+ let text2;
9197
9216
  if (typeof c === "string") {
9198
- text = c;
9217
+ text2 = c;
9199
9218
  } else if (Array.isArray(c)) {
9200
9219
  const collected = c.filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n");
9201
- text = collected || void 0;
9220
+ text2 = collected || void 0;
9202
9221
  }
9203
- if (!text) return void 0;
9222
+ if (!text2) return void 0;
9204
9223
  return {
9205
- text,
9224
+ text: text2,
9206
9225
  entrypoint: typeof entry.entrypoint === "string" ? entry.entrypoint : void 0
9207
9226
  };
9208
9227
  }
@@ -9253,12 +9272,12 @@ ${payload}`;
9253
9272
  return "";
9254
9273
  }
9255
9274
  }
9256
- function stripPreludes(text) {
9257
- return text.replace(/<command-name>[\s\S]*?<\/command-name>/g, "").replace(/<command-message>[\s\S]*?<\/command-message>/g, "").replace(/<command-args>[\s\S]*?<\/command-args>/g, "").replace(/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/g, "").replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").trim();
9275
+ function stripPreludes(text2) {
9276
+ return text2.replace(/<command-name>[\s\S]*?<\/command-name>/g, "").replace(/<command-message>[\s\S]*?<\/command-message>/g, "").replace(/<command-args>[\s\S]*?<\/command-args>/g, "").replace(/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/g, "").replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").trim();
9258
9277
  }
9259
- function capPayload(text, maxBytes) {
9260
- const buf = Buffer.from(text, "utf8");
9261
- if (buf.length <= maxBytes) return text;
9278
+ function capPayload(text2, maxBytes) {
9279
+ const buf = Buffer.from(text2, "utf8");
9280
+ if (buf.length <= maxBytes) return text2;
9262
9281
  return buf.subarray(buf.length - maxBytes).toString("utf8");
9263
9282
  }
9264
9283
  function normaliseOutput(raw) {
@@ -9327,9 +9346,9 @@ import chalk103 from "chalk";
9327
9346
 
9328
9347
  // src/commands/jira/adfToText.ts
9329
9348
  function renderInline(node) {
9330
- const text = node.text ?? "";
9331
- if (node.marks?.some((m) => m.type === "code")) return `\`${text}\``;
9332
- return text;
9349
+ const text2 = node.text ?? "";
9350
+ if (node.marks?.some((m) => m.type === "code")) return `\`${text2}\``;
9351
+ return text2;
9333
9352
  }
9334
9353
  function renderChildren(node, indent2) {
9335
9354
  return renderNodes(node.content ?? [], indent2);
@@ -9371,8 +9390,8 @@ function isListNode(node) {
9371
9390
  function renderListChild(child, indent2, pad, marker, isFirst) {
9372
9391
  if (isListNode(child)) return renderNodes([child], indent2 + 1);
9373
9392
  if (child.type !== "paragraph") return renderNode(child, indent2);
9374
- const text = renderChildren(child, indent2);
9375
- return isFirst ? `${pad}${marker} ${text}` : `${pad} ${text}`;
9393
+ const text2 = renderChildren(child, indent2);
9394
+ return isFirst ? `${pad}${marker} ${text2}` : `${pad} ${text2}`;
9376
9395
  }
9377
9396
  function renderListItem(node, indent2, marker) {
9378
9397
  const pad = " ".repeat(indent2);
@@ -9735,9 +9754,9 @@ function excerpt(xml, ...tags) {
9735
9754
  for (const tag of tags) {
9736
9755
  const raw = extractText(xml, tag);
9737
9756
  if (!raw) continue;
9738
- const text = stripHtml(raw);
9739
- if (text.length <= MAX_EXCERPT) return text;
9740
- return `${text.slice(0, MAX_EXCERPT)}\u2026`;
9757
+ const text2 = stripHtml(raw);
9758
+ if (text2.length <= MAX_EXCERPT) return text2;
9759
+ return `${text2.slice(0, MAX_EXCERPT)}\u2026`;
9741
9760
  }
9742
9761
  return "";
9743
9762
  }
@@ -9782,22 +9801,22 @@ async function fetchFeeds(urls, onProgress) {
9782
9801
  const origin = new URL(url).origin;
9783
9802
  const res = await fetch(url);
9784
9803
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
9785
- const items2 = parseFeed(await res.text(), origin);
9804
+ const items3 = parseFeed(await res.text(), origin);
9786
9805
  done2++;
9787
9806
  onProgress?.(done2, urls.length);
9788
- return items2;
9807
+ return items3;
9789
9808
  })
9790
9809
  );
9791
- const items = [];
9810
+ const items2 = [];
9792
9811
  for (const result of results) {
9793
9812
  if (result.status === "fulfilled") {
9794
- items.push(...result.value);
9813
+ items2.push(...result.value);
9795
9814
  }
9796
9815
  }
9797
- items.sort(
9816
+ items2.sort(
9798
9817
  (a, b) => new Date(b.pubDate).getTime() - new Date(a.pubDate).getTime()
9799
9818
  );
9800
- return items;
9819
+ return items2;
9801
9820
  }
9802
9821
 
9803
9822
  // src/commands/news/web/getHtml.ts
@@ -9833,13 +9852,13 @@ function prefetch() {
9833
9852
  process.stdout.write(
9834
9853
  `\r${chalk109.dim(`Fetching feeds ${bar} ${done2}/${t}`)}`
9835
9854
  );
9836
- }).then((items) => {
9855
+ }).then((items2) => {
9837
9856
  process.stdout.write(
9838
- `\r${chalk109.green(`Fetched ${items.length} items from ${total} feed(s)`)}
9857
+ `\r${chalk109.green(`Fetched ${items2.length} items from ${total} feed(s)`)}
9839
9858
  `
9840
9859
  );
9841
- cachedItems = items;
9842
- return items;
9860
+ cachedItems = items2;
9861
+ return items2;
9843
9862
  });
9844
9863
  }
9845
9864
  async function listItems2(_req, res) {
@@ -9901,11 +9920,11 @@ function printPromptsTable(rows) {
9901
9920
  console.log(chalk110.dim(header));
9902
9921
  console.log(chalk110.dim("-".repeat(header.length)));
9903
9922
  for (const row of rows) {
9904
- const count = String(row.count).padStart(countWidth);
9923
+ const count4 = String(row.count).padStart(countWidth);
9905
9924
  const tool = row.tool.padEnd(toolWidth);
9906
9925
  const command = truncate(row.command, 60).padEnd(commandWidth);
9907
9926
  console.log(
9908
- `${chalk110.yellow(count)} ${tool} ${command} ${chalk110.dim(row.repos)}`
9927
+ `${chalk110.yellow(count4)} ${tool} ${command} ${chalk110.dim(row.repos)}`
9909
9928
  );
9910
9929
  }
9911
9930
  }
@@ -10196,8 +10215,8 @@ function requireCache(prNumber) {
10196
10215
  }
10197
10216
  return cache;
10198
10217
  }
10199
- function findLineComment(comments2, commentId) {
10200
- return comments2.find((c) => c.type === "line" && c.id === commentId);
10218
+ function findLineComment(comments3, commentId) {
10219
+ return comments3.find((c) => c.type === "line" && c.id === commentId);
10201
10220
  }
10202
10221
  function requireLineComment(cache, commentId) {
10203
10222
  const comment3 = findLineComment(cache.comments, commentId);
@@ -10330,8 +10349,8 @@ function mapLineComment(c, threadInfo) {
10330
10349
  };
10331
10350
  }
10332
10351
  function fetchLineComments(org, repo, prNumber, threadInfo) {
10333
- const comments2 = fetchJson(`repos/${org}/${repo}/pulls/${prNumber}/comments`);
10334
- return comments2.map(
10352
+ const comments3 = fetchJson(`repos/${org}/${repo}/pulls/${prNumber}/comments`);
10353
+ return comments3.map(
10335
10354
  (c) => mapLineComment(c, threadInfo)
10336
10355
  );
10337
10356
  }
@@ -10355,33 +10374,33 @@ function formatForHuman(comment3) {
10355
10374
  ""
10356
10375
  ].join("\n");
10357
10376
  }
10358
- function summarise2(comments2) {
10359
- const lineCount = comments2.filter((c) => c.type === "line").length;
10360
- const reviewCount = comments2.filter((c) => c.type === "review").length;
10377
+ function summarise2(comments3) {
10378
+ const lineCount = comments3.filter((c) => c.type === "line").length;
10379
+ const reviewCount = comments3.filter((c) => c.type === "review").length;
10361
10380
  const parts = [];
10362
10381
  if (lineCount > 0) parts.push(`${lineCount} line`);
10363
10382
  if (reviewCount > 0) parts.push(`${reviewCount} review`);
10364
- return `Found ${parts.join(" and ")} comment${comments2.length === 1 ? "" : "s"}.`;
10383
+ return `Found ${parts.join(" and ")} comment${comments3.length === 1 ? "" : "s"}.`;
10365
10384
  }
10366
10385
  function printComments2(result) {
10367
- const { comments: comments2, cachePath } = result;
10368
- if (comments2.length === 0) {
10386
+ const { comments: comments3, cachePath } = result;
10387
+ if (comments3.length === 0) {
10369
10388
  console.log("No comments found.");
10370
10389
  return;
10371
10390
  }
10372
10391
  if (!isClaudeCode()) {
10373
- for (const comment3 of comments2) {
10392
+ for (const comment3 of comments3) {
10374
10393
  console.log(formatForHuman(comment3));
10375
10394
  }
10376
10395
  }
10377
- console.log(summarise2(comments2));
10396
+ console.log(summarise2(comments3));
10378
10397
  if (cachePath) {
10379
10398
  console.log(`Saved to ${cachePath}`);
10380
10399
  }
10381
10400
  }
10382
10401
 
10383
10402
  // src/commands/prs/listComments/index.ts
10384
- function writeCommentsCache(prNumber, comments2) {
10403
+ function writeCommentsCache(prNumber, comments3) {
10385
10404
  const assistDir = join34(process.cwd(), ".assist");
10386
10405
  if (!existsSync34(assistDir)) {
10387
10406
  mkdirSync9(assistDir, { recursive: true });
@@ -10389,7 +10408,7 @@ function writeCommentsCache(prNumber, comments2) {
10389
10408
  const cacheData = {
10390
10409
  prNumber,
10391
10410
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
10392
- comments: comments2
10411
+ comments: comments3
10393
10412
  };
10394
10413
  const cachePath = join34(assistDir, `pr-${prNumber}-comments.yaml`);
10395
10414
  writeFileSync23(cachePath, stringify(cacheData));
@@ -10406,9 +10425,9 @@ function handleKnownErrors(error) {
10406
10425
  }
10407
10426
  return null;
10408
10427
  }
10409
- function updateCache(prNumber, comments2) {
10410
- if (comments2.some((c) => c.type === "line")) {
10411
- writeCommentsCache(prNumber, comments2);
10428
+ function updateCache(prNumber, comments3) {
10429
+ if (comments3.some((c) => c.type === "line")) {
10430
+ writeCommentsCache(prNumber, comments3);
10412
10431
  } else {
10413
10432
  deleteCommentsCache(prNumber);
10414
10433
  }
@@ -10515,8 +10534,8 @@ async function promptNavigation(currentPage, totalPages) {
10515
10534
  });
10516
10535
  return parseAction(action);
10517
10536
  }
10518
- function computeTotalPages(count) {
10519
- return Math.ceil(count / PAGE_SIZE);
10537
+ function computeTotalPages(count4) {
10538
+ return Math.ceil(count4 / PAGE_SIZE);
10520
10539
  }
10521
10540
  async function navigateAndDisplay(pullRequests, totalPages, currentPage) {
10522
10541
  const delta = await promptNavigation(currentPage, totalPages);
@@ -10704,9 +10723,9 @@ function opExec(args) {
10704
10723
  }).trim();
10705
10724
  }
10706
10725
  function searchItems(search2) {
10707
- let items;
10726
+ let items2;
10708
10727
  try {
10709
- items = JSON.parse(opExec("item list --format=json"));
10728
+ items2 = JSON.parse(opExec("item list --format=json"));
10710
10729
  } catch {
10711
10730
  console.error(
10712
10731
  chalk114.red(
@@ -10716,7 +10735,7 @@ function searchItems(search2) {
10716
10735
  process.exit(1);
10717
10736
  }
10718
10737
  const lower = search2.toLowerCase();
10719
- return items.filter((i) => i.title.toLowerCase().includes(lower));
10738
+ return items2.filter((i) => i.title.toLowerCase().includes(lower));
10720
10739
  }
10721
10740
  function getItemFields(itemId) {
10722
10741
  try {
@@ -10740,14 +10759,14 @@ async function selectOpSecret(searchTerm) {
10740
10759
  name: "search",
10741
10760
  message: "Search 1Password for API key item:"
10742
10761
  }).run();
10743
- const items = searchItems(search2);
10744
- if (items.length === 0) {
10762
+ const items2 = searchItems(search2);
10763
+ if (items2.length === 0) {
10745
10764
  console.error(chalk115.red(`No items found matching "${search2}".`));
10746
10765
  process.exit(1);
10747
10766
  }
10748
10767
  const itemId = await selectOne(
10749
10768
  "Select item:",
10750
- items.map((i) => ({ name: `${i.title} (${i.vault.name})`, value: i.id }))
10769
+ items2.map((i) => ({ name: `${i.title} (${i.vault.name})`, value: i.id }))
10751
10770
  );
10752
10771
  const fields = getItemFields(itemId);
10753
10772
  if (fields.length === 0) {
@@ -11611,9 +11630,9 @@ function resolveImports(target, dependencies, sourceFile, statements = []) {
11611
11630
  function extractTexts(target, allFunctions, statements) {
11612
11631
  const stmtTexts = statements.map((v) => v.getFullText().trim());
11613
11632
  const fnTexts = allFunctions.map((fn) => {
11614
- const text = fn.getFullText().trim();
11615
- if (fn === target && !text.startsWith("export ")) return `export ${text}`;
11616
- return text;
11633
+ const text2 = fn.getFullText().trim();
11634
+ if (fn === target && !text2.startsWith("export ")) return `export ${text2}`;
11635
+ return text2;
11617
11636
  });
11618
11637
  return [...stmtTexts, ...fnTexts];
11619
11638
  }
@@ -11769,7 +11788,7 @@ function sourceReferencesName(sourceFile, functionName, extractedNames) {
11769
11788
  }
11770
11789
 
11771
11790
  // src/commands/refactor/extract/buildPlan.ts
11772
- function buildPlan(functionName, sourceFile, sourcePath, destPath, project) {
11791
+ function buildPlan2(functionName, sourceFile, sourcePath, destPath, project) {
11773
11792
  const analysis = analyseTarget(sourceFile, functionName);
11774
11793
  const sourceRelPath = getRelativeImportPath(destPath, sourcePath);
11775
11794
  const rewrittenImports = rewriteImportPaths(
@@ -11923,7 +11942,7 @@ async function extract(file, functionName, destination, options2 = {}) {
11923
11942
  const cwd = process.cwd();
11924
11943
  const relDest = path36.relative(cwd, destPath);
11925
11944
  const { project, sourceFile } = loadProjectFile(file);
11926
- const plan2 = buildPlan(
11945
+ const plan2 = buildPlan2(
11927
11946
  functionName,
11928
11947
  sourceFile,
11929
11948
  sourcePath,
@@ -12417,8 +12436,8 @@ import fs25 from "fs";
12417
12436
  import path45 from "path";
12418
12437
  function collectEntry(results, dir, entry) {
12419
12438
  const full = path45.join(dir, entry.name);
12420
- const items = entry.isDirectory() ? listFilesRecursive(full) : [full];
12421
- results.push(...items);
12439
+ const items2 = entry.isDirectory() ? listFilesRecursive(full) : [full];
12440
+ results.push(...items2);
12422
12441
  }
12423
12442
  function listFilesRecursive(dir) {
12424
12443
  if (!fs25.existsSync(dir)) return [];
@@ -12502,7 +12521,7 @@ function planFileMoves(clusters) {
12502
12521
  }
12503
12522
 
12504
12523
  // src/commands/refactor/restructure/index.ts
12505
- function buildPlan2(candidateFiles, tsConfigPath) {
12524
+ function buildPlan3(candidateFiles, tsConfigPath) {
12506
12525
  const candidates = new Set(candidateFiles.map((f) => path47.resolve(f)));
12507
12526
  const graph = buildImportGraph(candidates, tsConfigPath);
12508
12527
  const allProjectFiles = /* @__PURE__ */ new Set([
@@ -12527,7 +12546,7 @@ async function restructure(pattern2, options2 = {}) {
12527
12546
  return;
12528
12547
  }
12529
12548
  const tsConfigPath = path47.resolve("tsconfig.json");
12530
- const plan2 = buildPlan2(files, tsConfigPath);
12549
+ const plan2 = buildPlan3(files, tsConfigPath);
12531
12550
  if (plan2.moves.length === 0) {
12532
12551
  console.log(chalk134.green("No restructuring needed"));
12533
12552
  return;
@@ -12585,10 +12604,10 @@ function threadKey(c, byId) {
12585
12604
  }
12586
12605
  return `root-${c.id}`;
12587
12606
  }
12588
- function groupIntoThreads(comments2) {
12589
- const byId = new Map(comments2.map((c) => [c.id, c]));
12607
+ function groupIntoThreads(comments3) {
12608
+ const byId = new Map(comments3.map((c) => [c.id, c]));
12590
12609
  const threads = /* @__PURE__ */ new Map();
12591
- for (const c of comments2) {
12610
+ for (const c of comments3) {
12592
12611
  const key = threadKey(c, byId);
12593
12612
  const existing = threads.get(key);
12594
12613
  const parent = c.inReplyToId !== null ? byId.get(c.inReplyToId) : void 0;
@@ -12616,9 +12635,9 @@ function formatThread(thread) {
12616
12635
  return lines.join("\n\n");
12617
12636
  }
12618
12637
  var INTRO = `The PR already has the review comments below (including resolved and outdated threads). Avoid re-raising findings that a prior comment substantively covers.`;
12619
- function formatPriorComments(comments2) {
12620
- if (comments2.length === 0) return "";
12621
- const blocks = groupIntoThreads(comments2).map(formatThread);
12638
+ function formatPriorComments(comments3) {
12639
+ if (comments3.length === 0) return "";
12640
+ const blocks = groupIntoThreads(comments3).map(formatThread);
12622
12641
  return `## Prior review comments
12623
12642
 
12624
12643
  ${INTRO}
@@ -13020,9 +13039,9 @@ function warnUnlocated(unlocated) {
13020
13039
  }
13021
13040
 
13022
13041
  // src/commands/review/postReviewToPr.ts
13023
- async function confirmPost(prNumber, count, options2) {
13042
+ async function confirmPost(prNumber, count4, options2) {
13024
13043
  if (!options2.prompt) return true;
13025
- return promptConfirm(`Post ${count} comment(s) to PR #${prNumber}?`, false);
13044
+ return promptConfirm(`Post ${count4} comment(s) to PR #${prNumber}?`, false);
13026
13045
  }
13027
13046
  async function postReviewToPr(synthesisPath, options2) {
13028
13047
  const prNumber = findCurrentPrNumber();
@@ -13222,9 +13241,9 @@ var MultiSpinner = class {
13222
13241
  elapsedPrefix: prefix2
13223
13242
  });
13224
13243
  }
13225
- failRemaining(text) {
13244
+ failRemaining(text2) {
13226
13245
  for (const entry of this.entries) {
13227
- if (entry.state === "running") this.resolve(entry, "failed", text);
13246
+ if (entry.state === "running") this.resolve(entry, "failed", text2);
13228
13247
  }
13229
13248
  }
13230
13249
  add(entry) {
@@ -13237,14 +13256,14 @@ var MultiSpinner = class {
13237
13256
  set text(value) {
13238
13257
  entry.text = value;
13239
13258
  },
13240
- succeed: (text) => this.resolve(entry, "succeeded", text),
13241
- fail: (text) => this.resolve(entry, "failed", text)
13259
+ succeed: (text2) => this.resolve(entry, "succeeded", text2),
13260
+ fail: (text2) => this.resolve(entry, "failed", text2)
13242
13261
  };
13243
13262
  }
13244
- resolve(entry, state, text) {
13263
+ resolve(entry, state, text2) {
13245
13264
  if (entry.state !== "running") return;
13246
13265
  entry.state = state;
13247
- if (text !== void 0) entry.text = text;
13266
+ if (text2 !== void 0) entry.text = text2;
13248
13267
  entry.elapsedStart = void 0;
13249
13268
  this.render();
13250
13269
  this.maybeFinish();
@@ -13319,12 +13338,12 @@ function skippedCodexResult(outputPath) {
13319
13338
  // src/commands/review/formatReviewerFailure.ts
13320
13339
  var FAST_FAIL_MS = 1e3;
13321
13340
  var STDOUT_TAIL_LINES = 20;
13322
- function indent(text) {
13323
- return text.split(/\r?\n/).map((line) => ` ${line}`);
13341
+ function indent(text2) {
13342
+ return text2.split(/\r?\n/).map((line) => ` ${line}`);
13324
13343
  }
13325
- function tailLines(text, maxLines) {
13326
- const lines = text.split(/\r?\n/);
13327
- return lines.length <= maxLines ? text : lines.slice(-maxLines).join("\n");
13344
+ function tailLines(text2, maxLines) {
13345
+ const lines = text2.split(/\r?\n/);
13346
+ return lines.length <= maxLines ? text2 : lines.slice(-maxLines).join("\n");
13328
13347
  }
13329
13348
  function isFastFail(input) {
13330
13349
  return input.exitCode !== 0 && input.elapsedMs !== void 0 && input.elapsedMs < FAST_FAIL_MS;
@@ -14046,9 +14065,9 @@ async function runReviewPipeline(paths, options2) {
14046
14065
  }
14047
14066
 
14048
14067
  // src/commands/review/reviewPr.ts
14049
- function logPriorComments(count) {
14050
- if (count === 0) return;
14051
- console.log(`Including ${count} prior review comment(s) in request.md.`);
14068
+ function logPriorComments(count4) {
14069
+ if (count4 === 0) return;
14070
+ console.log(`Including ${count4} prior review comment(s) in request.md.`);
14052
14071
  }
14053
14072
  function gatherChangedContext() {
14054
14073
  const context = gatherContext();
@@ -14290,10 +14309,10 @@ function filterToSql(filter) {
14290
14309
  }
14291
14310
 
14292
14311
  // src/commands/seq/fetchSeqData.ts
14293
- async function fetchSeqData(conn, filter, count, from, to) {
14312
+ async function fetchSeqData(conn, filter, count4, from, to) {
14294
14313
  const sqlFilter = filterToSql(filter);
14295
- const sql2 = `select @Timestamp, @Level, @Exception, @Message from stream where ${sqlFilter} order by @Timestamp desc limit ${count}`;
14296
- const params = new URLSearchParams({ q: sql2 });
14314
+ const sql4 = `select @Timestamp, @Level, @Exception, @Message from stream where ${sqlFilter} order by @Timestamp desc limit ${count4}`;
14315
+ const params = new URLSearchParams({ q: sql4 });
14297
14316
  if (from) params.set("fromDateUtc", from);
14298
14317
  if (to) params.set("toDateUtc", to);
14299
14318
  const response = await fetchSeq(conn, "/api/data", params);
@@ -14462,12 +14481,12 @@ function resolveConnection2(name) {
14462
14481
  async function seqQuery(filter, options2) {
14463
14482
  rejectTimestampFilter(filter);
14464
14483
  const conn = resolveConnection2(options2.connection);
14465
- const count = Number.parseInt(options2.count ?? "1000", 10);
14484
+ const count4 = Number.parseInt(options2.count ?? "1000", 10);
14466
14485
  const from = options2.from ? parseRelativeTime(options2.from) : void 0;
14467
14486
  const to = options2.to ? parseRelativeTime(options2.to) : void 0;
14468
- const events = from || to ? await fetchSeqData(conn, filter, count, from, to) : await fetchSeqEvents(
14487
+ const events = from || to ? await fetchSeqData(conn, filter, count4, from, to) : await fetchSeqEvents(
14469
14488
  conn,
14470
- new URLSearchParams({ filter, count: String(count) })
14489
+ new URLSearchParams({ filter, count: String(count4) })
14471
14490
  );
14472
14491
  if (events.length === 0) {
14473
14492
  console.log(chalk141.yellow("No events found."));
@@ -14483,10 +14502,10 @@ async function seqQuery(filter, options2) {
14483
14502
  }
14484
14503
  console.log(chalk141.dim(`
14485
14504
  ${events.length} events`));
14486
- if (events.length >= count) {
14505
+ if (events.length >= count4) {
14487
14506
  console.log(
14488
14507
  chalk141.yellow(
14489
- `Results limited to ${count}. Use --count to retrieve more.`
14508
+ `Results limited to ${count4}. Use --count to retrieve more.`
14490
14509
  )
14491
14510
  );
14492
14511
  }
@@ -14545,26 +14564,26 @@ import chalk144 from "chalk";
14545
14564
  // src/commands/sql/loadConnections.ts
14546
14565
  function loadConnections3() {
14547
14566
  const raw = loadGlobalConfigRaw();
14548
- const sql2 = raw.sql;
14549
- return sql2?.connections ?? [];
14567
+ const sql4 = raw.sql;
14568
+ return sql4?.connections ?? [];
14550
14569
  }
14551
14570
  function saveConnections3(connections) {
14552
14571
  const raw = loadGlobalConfigRaw();
14553
- const sql2 = raw.sql ?? {};
14554
- sql2.connections = connections;
14555
- raw.sql = sql2;
14572
+ const sql4 = raw.sql ?? {};
14573
+ sql4.connections = connections;
14574
+ raw.sql = sql4;
14556
14575
  saveGlobalConfig(raw);
14557
14576
  }
14558
14577
  function getDefaultConnection2() {
14559
14578
  const raw = loadGlobalConfigRaw();
14560
- const sql2 = raw.sql;
14561
- return sql2?.defaultConnection;
14579
+ const sql4 = raw.sql;
14580
+ return sql4?.defaultConnection;
14562
14581
  }
14563
14582
  function setDefaultConnection2(name) {
14564
14583
  const raw = loadGlobalConfigRaw();
14565
- const sql2 = raw.sql ?? {};
14566
- sql2.defaultConnection = name;
14567
- raw.sql = sql2;
14584
+ const sql4 = raw.sql ?? {};
14585
+ sql4.defaultConnection = name;
14586
+ raw.sql = sql4;
14568
14587
  saveGlobalConfig(raw);
14569
14588
  }
14570
14589
 
@@ -14635,9 +14654,9 @@ function resolveConnection3(name) {
14635
14654
  }
14636
14655
 
14637
14656
  // src/commands/sql/sqlConnect.ts
14638
- import sql from "mssql";
14657
+ import sql3 from "mssql";
14639
14658
  async function sqlConnect(conn) {
14640
- return await sql.connect({
14659
+ return await sql3.connect({
14641
14660
  server: conn.server,
14642
14661
  port: conn.port,
14643
14662
  user: conn.user,
@@ -14700,11 +14719,11 @@ var MUTATION_PATTERN = new RegExp(
14700
14719
  `\\b(${MUTATION_KEYWORDS.join("|")})\\b`,
14701
14720
  "i"
14702
14721
  );
14703
- function stripComments(sql2) {
14704
- return sql2.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/--[^\n]*/g, " ");
14722
+ function stripComments(sql4) {
14723
+ return sql4.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/--[^\n]*/g, " ");
14705
14724
  }
14706
- function isMutation(sql2) {
14707
- const stripped = stripComments(sql2);
14725
+ function isMutation(sql4) {
14726
+ const stripped = stripComments(sql4);
14708
14727
  if (MUTATION_PATTERN.test(stripped)) return true;
14709
14728
  return /\bSELECT\b[\s\S]+\bINTO\s+\w/i.test(stripped);
14710
14729
  }
@@ -15000,8 +15019,8 @@ import { existsSync as existsSync39, mkdirSync as mkdirSync11, readFileSync as r
15000
15019
  import { basename as basename9, dirname as dirname21, join as join39 } from "path";
15001
15020
 
15002
15021
  // src/commands/transcript/cleanText.ts
15003
- function cleanText(text) {
15004
- const words = text.split(/\s+/);
15022
+ function cleanText(text2) {
15023
+ const words = text2.split(/\s+/);
15005
15024
  const cleaned = [];
15006
15025
  for (let i = 0; i < words.length; i++) {
15007
15026
  let isRepeat = false;
@@ -15021,8 +15040,8 @@ function cleanText(text) {
15021
15040
  }
15022
15041
 
15023
15042
  // src/commands/transcript/format/processVttFile/parseVtt/deduplicateCues/removeSubstringDuplicates.ts
15024
- function normalizeText(text) {
15025
- return text.toLowerCase().trim();
15043
+ function normalizeText(text2) {
15044
+ return text2.toLowerCase().trim();
15026
15045
  }
15027
15046
  function checkSubstringRelation(textI, textJ) {
15028
15047
  if (textI.includes(textJ) && textI.length > textJ.length)
@@ -15151,13 +15170,13 @@ function parseTimestampLine(line) {
15151
15170
  return { startMs: parseTimestamp(startStr), endMs: parseTimestamp(endStr) };
15152
15171
  }
15153
15172
  function buildCue(startMs, endMs, fullText) {
15154
- const { speaker, text } = extractSpeaker(fullText);
15155
- return text ? { startMs, endMs, speaker, text } : null;
15173
+ const { speaker, text: text2 } = extractSpeaker(fullText);
15174
+ return text2 ? { startMs, endMs, speaker, text: text2 } : null;
15156
15175
  }
15157
15176
  function parseCueLine(lines, i) {
15158
15177
  const { startMs, endMs } = parseTimestampLine(lines[i]);
15159
- const { text, nextIndex } = collectTextLines(lines, i + 1);
15160
- return { cue: buildCue(startMs, endMs, text), nextIndex };
15178
+ const { text: text2, nextIndex } = collectTextLines(lines, i + 1);
15179
+ return { cue: buildCue(startMs, endMs, text2), nextIndex };
15161
15180
  }
15162
15181
  function isCueSeparator(line) {
15163
15182
  return line.trim().includes("-->");
@@ -15521,13 +15540,13 @@ function logs(options2) {
15521
15540
  console.log("No voice log file found");
15522
15541
  return;
15523
15542
  }
15524
- const count = Number.parseInt(options2.lines ?? "150", 10);
15543
+ const count4 = Number.parseInt(options2.lines ?? "150", 10);
15525
15544
  const content = readFileSync33(voicePaths.log, "utf-8").trim();
15526
15545
  if (!content) {
15527
15546
  console.log("Voice log is empty");
15528
15547
  return;
15529
15548
  }
15530
- const lines = content.split("\n").slice(-count);
15549
+ const lines = content.split("\n").slice(-count4);
15531
15550
  for (const line of lines) {
15532
15551
  try {
15533
15552
  const event = JSON.parse(line);
@@ -15674,10 +15693,10 @@ function isProcessAlive3(pid) {
15674
15693
  return false;
15675
15694
  }
15676
15695
  }
15677
- function readRecentLogs(count) {
15696
+ function readRecentLogs(count4) {
15678
15697
  if (!existsSync45(voicePaths.log)) return [];
15679
15698
  const lines = readFileSync35(voicePaths.log, "utf-8").trim().split("\n");
15680
- return lines.slice(-count);
15699
+ return lines.slice(-count4);
15681
15700
  }
15682
15701
  function status() {
15683
15702
  if (!existsSync45(voicePaths.pid)) {
@@ -15869,8 +15888,8 @@ async function exchangeToken(params) {
15869
15888
  body: body.toString()
15870
15889
  });
15871
15890
  if (!response.ok) {
15872
- const text = await response.text();
15873
- throw new Error(`Token exchange failed (${response.status}): ${text}`);
15891
+ const text2 = await response.text();
15892
+ throw new Error(`Token exchange failed (${response.status}): ${text2}`);
15874
15893
  }
15875
15894
  return response.json();
15876
15895
  }
@@ -16184,11 +16203,11 @@ import { join as join49 } from "path";
16184
16203
 
16185
16204
  // src/commands/run/extractOption.ts
16186
16205
  function extractOption(args, flag) {
16187
- const index = args.indexOf(flag);
16188
- if (index === -1) return { value: void 0, remaining: args };
16206
+ const index2 = args.indexOf(flag);
16207
+ if (index2 === -1) return { value: void 0, remaining: args };
16189
16208
  return {
16190
- value: args[index + 1],
16191
- remaining: [...args.slice(0, index), ...args.slice(index + 2)]
16209
+ value: args[index2 + 1],
16210
+ remaining: [...args.slice(0, index2), ...args.slice(index2 + 2)]
16192
16211
  };
16193
16212
  }
16194
16213
 
@@ -16568,13 +16587,13 @@ function* iterateUserMessages(filePath, maxBytes = 65536) {
16568
16587
 
16569
16588
  // src/commands/sessions/summarise/extractFirstUserMessage.ts
16570
16589
  function extractFirstUserMessage(filePath) {
16571
- for (const text of iterateUserMessages(filePath)) {
16572
- return truncate3(text);
16590
+ for (const text2 of iterateUserMessages(filePath)) {
16591
+ return truncate3(text2);
16573
16592
  }
16574
16593
  return void 0;
16575
16594
  }
16576
- function truncate3(text, maxChars = 500) {
16577
- const trimmed = text.trim();
16595
+ function truncate3(text2, maxChars = 500) {
16596
+ const trimmed = text2.trim();
16578
16597
  if (trimmed.length <= maxChars) return trimmed;
16579
16598
  return `${trimmed.slice(0, maxChars)}\u2026`;
16580
16599
  }
@@ -16582,28 +16601,28 @@ function truncate3(text, maxChars = 500) {
16582
16601
  // src/commands/sessions/summarise/scanSessionBacklogRefs.ts
16583
16602
  function scanSessionBacklogRefs(filePath) {
16584
16603
  const ids = /* @__PURE__ */ new Set();
16585
- for (const text of iterateUserMessages(filePath, Number.MAX_SAFE_INTEGER)) {
16586
- for (const id of extractBacklogIds(text)) {
16604
+ for (const text2 of iterateUserMessages(filePath, Number.MAX_SAFE_INTEGER)) {
16605
+ for (const id of extractBacklogIds(text2)) {
16587
16606
  ids.add(id);
16588
16607
  }
16589
16608
  }
16590
16609
  return [...ids].sort((a, b) => a - b);
16591
16610
  }
16592
- function extractBacklogIds(text) {
16611
+ function extractBacklogIds(text2) {
16593
16612
  const ids = [];
16594
- for (const m of text.matchAll(/backlog\s+run\s+(\d+)/gi)) {
16613
+ for (const m of text2.matchAll(/backlog\s+run\s+(\d+)/gi)) {
16595
16614
  ids.push(Number.parseInt(m[1], 10));
16596
16615
  }
16597
- for (const m of text.matchAll(/backlog\s+(?:item\s+)?#(\d+)/gi)) {
16616
+ for (const m of text2.matchAll(/backlog\s+(?:item\s+)?#(\d+)/gi)) {
16598
16617
  ids.push(Number.parseInt(m[1], 10));
16599
16618
  }
16600
- for (const m of text.matchAll(/backlog\s+phase-done\s+(\d+)/gi)) {
16619
+ for (const m of text2.matchAll(/backlog\s+phase-done\s+(\d+)/gi)) {
16601
16620
  ids.push(Number.parseInt(m[1], 10));
16602
16621
  }
16603
- for (const m of text.matchAll(/backlog\s+comment\s+(\d+)/gi)) {
16622
+ for (const m of text2.matchAll(/backlog\s+comment\s+(\d+)/gi)) {
16604
16623
  ids.push(Number.parseInt(m[1], 10));
16605
16624
  }
16606
- for (const m of text.matchAll(/(?:^|[\s(])#(\d{1,4})(?=[\s).,;:!?]|$)/gm)) {
16625
+ for (const m of text2.matchAll(/(?:^|[\s(])#(\d{1,4})(?=[\s).,;:!?]|$)/gm)) {
16607
16626
  ids.push(Number.parseInt(m[1], 10));
16608
16627
  }
16609
16628
  return ids;