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.
- package/dist/bertrand.js +548 -863
- package/dist/dashboard/assets/index-DW_dgQQt.js +659 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/migrations/0005_drop_pr_count.sql +1 -0
- package/dist/migrations/0006_drop_worktrees_last_question.sql +2 -0
- package/dist/migrations/0007_awesome_justin_hammer.sql +2 -0
- package/dist/migrations/meta/0007_snapshot.json +693 -0
- package/dist/migrations/meta/_journal.json +21 -0
- package/dist/run-screen.js +33 -43
- package/package.json +1 -1
- package/dist/dashboard/assets/index-SB5vH7Ec.js +0 -684
|
@@ -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
|
}
|
package/dist/run-screen.js
CHANGED
|
@@ -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(
|
|
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,
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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
|