bertrand 0.22.1 → 0.23.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.
@@ -36,6 +36,27 @@
36
36
  "when": 1781546596070,
37
37
  "tag": "0004_dapper_apocalypse",
38
38
  "breakpoints": true
39
+ },
40
+ {
41
+ "idx": 5,
42
+ "version": "6",
43
+ "when": 1781577167000,
44
+ "tag": "0005_drop_pr_count",
45
+ "breakpoints": true
46
+ },
47
+ {
48
+ "idx": 6,
49
+ "version": "6",
50
+ "when": 1781578831000,
51
+ "tag": "0006_drop_worktrees_last_question",
52
+ "breakpoints": true
53
+ },
54
+ {
55
+ "idx": 7,
56
+ "version": "6",
57
+ "when": 1782249283327,
58
+ "tag": "0007_awesome_justin_hammer",
59
+ "breakpoints": true
39
60
  }
40
61
  ]
41
62
  }
@@ -561,7 +561,7 @@ function StatusDot({ status }) {
561
561
  }, undefined, false, undefined, this);
562
562
  }
563
563
  // src/db/queries/sessions.ts
564
- import { eq, and, inArray, sql as sql2 } from "drizzle-orm";
564
+ import { eq as eq2, and, inArray, sql as sql2 } from "drizzle-orm";
565
565
 
566
566
  // src/db/client.ts
567
567
  import { Database } from "bun:sqlite";
@@ -719,7 +719,6 @@ function resolveActiveProject() {
719
719
  // src/db/schema.ts
720
720
  var exports_schema = {};
721
721
  __export(exports_schema, {
722
- worktreeAssociations: () => worktreeAssociations,
723
722
  sessions: () => sessions,
724
723
  sessionStats: () => sessionStats,
725
724
  sessionLabels: () => sessionLabels,
@@ -764,6 +763,8 @@ var sessions = sqliteTable("sessions", {
764
763
  pid: integer("pid"),
765
764
  startedAt: text("started_at").notNull().default(sql`(datetime('now'))`),
766
765
  endedAt: text("ended_at"),
766
+ worktreePath: text("worktree_path"),
767
+ worktreeBranch: text("worktree_branch"),
767
768
  createdAt: text("created_at").notNull().default(sql`(datetime('now'))`),
768
769
  updatedAt: text("updated_at").notNull().default(sql`(datetime('now'))`)
769
770
  }, (t) => [
@@ -784,7 +785,6 @@ var conversations = sqliteTable("conversations", {
784
785
  startedAt: text("started_at").notNull().default(sql`(datetime('now'))`),
785
786
  endedAt: text("ended_at"),
786
787
  discarded: integer("discarded", { mode: "boolean" }).notNull().default(false),
787
- lastQuestion: text("last_question"),
788
788
  eventCount: integer("event_count").notNull().default(0)
789
789
  }, (t) => [index("conv_session").on(t.sessionId)]);
790
790
  var events = sqliteTable("events", {
@@ -801,24 +801,11 @@ var events = sqliteTable("events", {
801
801
  index("ev_event_created").on(t.event, t.createdAt),
802
802
  index("ev_conversation").on(t.conversationId)
803
803
  ]);
804
- var worktreeAssociations = sqliteTable("worktree_associations", {
805
- id: text("id").primaryKey(),
806
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
807
- branch: text("branch").notNull(),
808
- worktreePath: text("worktree_path"),
809
- active: integer("active", { mode: "boolean" }).notNull().default(true),
810
- enteredAt: text("entered_at").notNull().default(sql`(datetime('now'))`),
811
- exitedAt: text("exited_at")
812
- }, (t) => [
813
- index("wt_session").on(t.sessionId),
814
- index("wt_active").on(t.active)
815
- ]);
816
804
  var sessionStats = sqliteTable("session_stats", {
817
805
  sessionId: text("session_id").primaryKey().references(() => sessions.id, { onDelete: "cascade" }),
818
806
  eventCount: integer("event_count").notNull().default(0),
819
807
  conversationCount: integer("conversation_count").notNull().default(0),
820
808
  interactionCount: integer("interaction_count").notNull().default(0),
821
- prCount: integer("pr_count").notNull().default(0),
822
809
  claudeWorkS: integer("claude_work_s").notNull().default(0),
823
810
  userWaitS: integer("user_wait_s").notNull().default(0),
824
811
  activePct: integer("active_pct").notNull().default(0),
@@ -881,13 +868,37 @@ function hasSessionsTable(sqlite) {
881
868
  // src/lib/id.ts
882
869
  import { nanoid } from "nanoid";
883
870
 
871
+ // src/db/queries/categories.ts
872
+ import { eq, like, or, isNull } from "drizzle-orm";
873
+
874
+ // src/lib/parse-session-name.ts
875
+ var SEGMENT_PATTERN = /^[a-z0-9][a-z0-9._-]*$/i;
876
+ function parseSessionName(input) {
877
+ const trimmed = input.trim().replace(/^\/+|\/+$/g, "");
878
+ if (!trimmed) {
879
+ throw new Error("Session name cannot be empty");
880
+ }
881
+ const segments = trimmed.split("/").filter(Boolean);
882
+ if (segments.length < 2) {
883
+ throw new Error(`Session name must include at least one category: "category/session" (got "${trimmed}")`);
884
+ }
885
+ for (const segment of segments) {
886
+ if (!SEGMENT_PATTERN.test(segment)) {
887
+ throw new Error(`Invalid segment "${segment}": must start with alphanumeric and contain only letters, digits, dots, underscores, or dashes`);
888
+ }
889
+ }
890
+ const categoryPath = segments[0];
891
+ const slug = segments.slice(1).join("/");
892
+ return { categoryPath, slug };
893
+ }
894
+
884
895
  // src/db/queries/sessions.ts
885
896
  function getSession(id) {
886
- return getDb().select().from(sessions).where(eq(sessions.id, id)).get();
897
+ return getDb().select().from(sessions).where(eq2(sessions.id, id)).get();
887
898
  }
888
899
  function getAllSessions(opts) {
889
900
  const db = getDb();
890
- const query = db.select({ session: sessions, categoryPath: categories.path }).from(sessions).innerJoin(categories, eq(sessions.categoryId, categories.id));
901
+ const query = db.select({ session: sessions, categoryPath: categories.path }).from(sessions).innerJoin(categories, eq2(sessions.categoryId, categories.id));
891
902
  if (opts?.excludeArchived) {
892
903
  return query.where(inArray(sessions.status, [
893
904
  "active",
@@ -898,10 +909,10 @@ function getAllSessions(opts) {
898
909
  return query.all();
899
910
  }
900
911
  function updateSessionStatus(id, status) {
901
- return getDb().update(sessions).set({ status, updatedAt: sql2`(datetime('now'))` }).where(eq(sessions.id, id)).returning().get();
912
+ return getDb().update(sessions).set({ status, updatedAt: sql2`(datetime('now'))` }).where(eq2(sessions.id, id)).returning().get();
902
913
  }
903
914
  function setSessionRating(id, rating) {
904
- return getDb().update(sessions).set({ rating, updatedAt: sql2`(datetime('now'))` }).where(eq(sessions.id, id)).returning().get();
915
+ return getDb().update(sessions).set({ rating, updatedAt: sql2`(datetime('now'))` }).where(eq2(sessions.id, id)).returning().get();
905
916
  }
906
917
 
907
918
  // src/lib/session-archive.ts
@@ -963,27 +974,6 @@ function formatAgo(isoOrDate) {
963
974
  return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
964
975
  }
965
976
 
966
- // src/lib/parse-session-name.ts
967
- var SEGMENT_PATTERN = /^[a-z0-9][a-z0-9._-]*$/i;
968
- function parseSessionName(input) {
969
- const trimmed = input.trim().replace(/^\/+|\/+$/g, "");
970
- if (!trimmed) {
971
- throw new Error("Session name cannot be empty");
972
- }
973
- const segments = trimmed.split("/").filter(Boolean);
974
- if (segments.length < 2) {
975
- throw new Error(`Session name must include at least one category: "category/session" (got "${trimmed}")`);
976
- }
977
- for (const segment of segments) {
978
- if (!SEGMENT_PATTERN.test(segment)) {
979
- throw new Error(`Invalid segment "${segment}": must start with alphanumeric and contain only letters, digits, dots, underscores, or dashes`);
980
- }
981
- }
982
- const categoryPath = segments[0];
983
- const slug = segments.slice(1).join("/");
984
- return { categoryPath, slug };
985
- }
986
-
987
977
  // src/tui/screens/launch/index.tsx
988
978
  import { jsxDEV as jsxDEV6, Fragment } from "react/jsx-dev-runtime";
989
979
  var STATUS_COLOR = {
@@ -1308,9 +1298,9 @@ import { useState as useState4 } from "react";
1308
1298
  import { Box as Box6, Text as Text7, useInput as useInput3, useTui as useTui2 } from "@orchetron/storm";
1309
1299
 
1310
1300
  // src/db/queries/conversations.ts
1311
- import { eq as eq2, and as and2, desc, sql as sql3 } from "drizzle-orm";
1301
+ import { eq as eq3, and as and2, desc, sql as sql3 } from "drizzle-orm";
1312
1302
  function getConversationsBySession(sessionId) {
1313
- return getDb().select().from(conversations).where(and2(eq2(conversations.sessionId, sessionId), eq2(conversations.discarded, false))).orderBy(desc(conversations.startedAt)).all();
1303
+ return getDb().select().from(conversations).where(and2(eq3(conversations.sessionId, sessionId), eq3(conversations.discarded, false))).orderBy(desc(conversations.startedAt)).all();
1314
1304
  }
1315
1305
 
1316
1306
  // src/tui/screens/Exit.tsx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bertrand",
3
- "version": "0.22.1",
3
+ "version": "0.23.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },