@knowsuchagency/fulcrum 2.5.0 → 2.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.html CHANGED
@@ -5,7 +5,7 @@
5
5
  <link rel="icon" type="image/png" href="/logo.png" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Fulcrum</title>
8
- <script type="module" crossorigin src="/assets/index-DSaPCUvd.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-DNNOyQp7.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-D6ht7k3-.css">
10
10
  </head>
11
11
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knowsuchagency/fulcrum",
3
- "version": "2.5.0",
3
+ "version": "2.5.2",
4
4
  "description": "Harness Attention. Orchestrate Agents. Ship.",
5
5
  "license": "PolyForm-Perimeter-1.0.0",
6
6
  "repository": {
package/server/index.js CHANGED
@@ -1109,7 +1109,7 @@ function notInArray(column, values) {
1109
1109
  function isNull(value) {
1110
1110
  return sql`${value} is null`;
1111
1111
  }
1112
- function isNotNull(value) {
1112
+ function isNotNull2(value) {
1113
1113
  return sql`${value} is not null`;
1114
1114
  }
1115
1115
  function exists(subquery) {
@@ -1185,7 +1185,7 @@ function getOperators() {
1185
1185
  ilike,
1186
1186
  inArray,
1187
1187
  isNull,
1188
- isNotNull,
1188
+ isNotNull: isNotNull2,
1189
1189
  like,
1190
1190
  lt,
1191
1191
  lte,
@@ -71282,7 +71282,10 @@ function getSession(id) {
71282
71282
  function listSessions(options) {
71283
71283
  const { limit: limit2 = 50, offset = 0, projectId, search, favorites } = options;
71284
71284
  const conditions2 = [];
71285
- conditions2.push(or(isNull(chatSessions.connectionId), not(like(chatSessions.connectionId, "assistant-%"))));
71285
+ const assistantSessionIds = db2.select({ sessionId: messagingSessionMappings.sessionId }).from(messagingSessionMappings).where(like(messagingSessionMappings.connectionId, "assistant-%")).all().map((row) => row.sessionId);
71286
+ if (assistantSessionIds.length > 0) {
71287
+ conditions2.push(notInArray(chatSessions.id, assistantSessionIds));
71288
+ }
71286
71289
  if (projectId) {
71287
71290
  conditions2.push(eq(chatSessions.projectId, projectId));
71288
71291
  }
@@ -71446,6 +71449,137 @@ This will automatically update the editor. Always provide the COMPLETE document,
71446
71449
 
71447
71450
  ${uiFeatures}`;
71448
71451
  }
71452
+ function buildPageContextString(context) {
71453
+ const contextParts = [];
71454
+ contextParts.push(`Current page: ${context.path}`);
71455
+ switch (context.pageType) {
71456
+ case "task": {
71457
+ if (context.taskId) {
71458
+ const task = db2.select().from(tasks).where(eq(tasks.id, context.taskId)).get();
71459
+ if (task) {
71460
+ contextParts.push(`Viewing task: "${task.title}"`);
71461
+ contextParts.push(`Status: ${task.status}`);
71462
+ if (task.branch)
71463
+ contextParts.push(`Branch: ${task.branch}`);
71464
+ if (task.repoName)
71465
+ contextParts.push(`Repository: ${task.repoName}`);
71466
+ if (task.description)
71467
+ contextParts.push(`Description: ${task.description}`);
71468
+ if (task.worktreePath)
71469
+ contextParts.push(`Worktree: ${task.worktreePath}`);
71470
+ }
71471
+ }
71472
+ break;
71473
+ }
71474
+ case "tasks": {
71475
+ contextParts.push("Viewing the tasks kanban board");
71476
+ if (context.filters?.project) {
71477
+ if (context.filters.project === "inbox") {
71478
+ contextParts.push("Filtered to: Inbox (tasks without a project)");
71479
+ } else {
71480
+ const project = db2.select().from(projects).where(eq(projects.id, context.filters.project)).get();
71481
+ if (project) {
71482
+ contextParts.push(`Filtered to project: "${project.name}"`);
71483
+ }
71484
+ }
71485
+ }
71486
+ if (context.filters?.tags?.length) {
71487
+ contextParts.push(`Filtered by tags: ${context.filters.tags.join(", ")}`);
71488
+ }
71489
+ if (context.filters?.view) {
71490
+ contextParts.push(`View mode: ${context.filters.view}`);
71491
+ }
71492
+ break;
71493
+ }
71494
+ case "project": {
71495
+ if (context.projectId) {
71496
+ const project = db2.select().from(projects).where(eq(projects.id, context.projectId)).get();
71497
+ if (project) {
71498
+ contextParts.push(`Viewing project: "${project.name}"`);
71499
+ if (project.description)
71500
+ contextParts.push(`Description: ${project.description}`);
71501
+ if (project.status)
71502
+ contextParts.push(`Status: ${project.status}`);
71503
+ const repoLinks = db2.select().from(projectRepositories).where(eq(projectRepositories.projectId, context.projectId)).all();
71504
+ if (repoLinks.length > 0) {
71505
+ contextParts.push(`Linked repositories: ${repoLinks.length}`);
71506
+ }
71507
+ }
71508
+ }
71509
+ break;
71510
+ }
71511
+ case "projects": {
71512
+ contextParts.push("Viewing the projects list");
71513
+ break;
71514
+ }
71515
+ case "repository": {
71516
+ if (context.repositoryId) {
71517
+ const repo = db2.select().from(repositories).where(eq(repositories.id, context.repositoryId)).get();
71518
+ if (repo) {
71519
+ contextParts.push(`Viewing repository: "${repo.displayName}"`);
71520
+ contextParts.push(`Path: ${repo.path}`);
71521
+ if (repo.defaultAgent)
71522
+ contextParts.push(`Default agent: ${repo.defaultAgent}`);
71523
+ if (repo.remoteUrl)
71524
+ contextParts.push(`Remote: ${repo.remoteUrl}`);
71525
+ }
71526
+ }
71527
+ break;
71528
+ }
71529
+ case "repositories": {
71530
+ contextParts.push("Viewing the repositories list");
71531
+ break;
71532
+ }
71533
+ case "app": {
71534
+ if (context.appId) {
71535
+ const app23 = db2.select().from(apps).where(eq(apps.id, context.appId)).get();
71536
+ if (app23) {
71537
+ contextParts.push(`Viewing app: "${app23.name}"`);
71538
+ contextParts.push(`Status: ${app23.status}`);
71539
+ contextParts.push(`Branch: ${app23.branch}`);
71540
+ if (app23.lastDeployedAt)
71541
+ contextParts.push(`Last deployed: ${app23.lastDeployedAt}`);
71542
+ }
71543
+ }
71544
+ break;
71545
+ }
71546
+ case "apps": {
71547
+ contextParts.push("Viewing the apps deployment list");
71548
+ break;
71549
+ }
71550
+ case "monitoring": {
71551
+ contextParts.push("Viewing the monitoring dashboard");
71552
+ if (context.activeTab) {
71553
+ contextParts.push(`Active tab: ${context.activeTab}`);
71554
+ }
71555
+ break;
71556
+ }
71557
+ case "terminals": {
71558
+ contextParts.push("Viewing the persistent terminals page");
71559
+ break;
71560
+ }
71561
+ case "jobs":
71562
+ case "job": {
71563
+ contextParts.push(context.pageType === "jobs" ? "Viewing scheduled jobs list" : `Viewing job details`);
71564
+ if (context.jobId) {
71565
+ contextParts.push(`Job ID: ${context.jobId}`);
71566
+ }
71567
+ break;
71568
+ }
71569
+ case "settings": {
71570
+ contextParts.push("Viewing the settings page");
71571
+ break;
71572
+ }
71573
+ }
71574
+ if (contextParts.length > 0) {
71575
+ return `
71576
+
71577
+ Current Context:
71578
+ ${contextParts.map((p) => `- ${p}`).join(`
71579
+ `)}`;
71580
+ }
71581
+ return "";
71582
+ }
71449
71583
  async function* streamMessage2(sessionId, userMessage, modelIdOrOptions, editorContent) {
71450
71584
  const options = typeof modelIdOrOptions === "object" ? modelIdOrOptions : { modelId: modelIdOrOptions, editorContent };
71451
71585
  const session3 = getSession(sessionId);
@@ -71482,6 +71616,9 @@ async function* streamMessage2(sessionId, userMessage, modelIdOrOptions, editorC
71482
71616
  ${options.systemPromptAdditions}`;
71483
71617
  } else {
71484
71618
  systemPrompt = buildSystemPrompt2();
71619
+ if (options.context) {
71620
+ systemPrompt += buildPageContextString(options.context);
71621
+ }
71485
71622
  }
71486
71623
  let textMessage = userMessage;
71487
71624
  if (options.editorContent && options.editorContent.trim()) {
@@ -463721,7 +463858,7 @@ mcpRoutes.all("/", async (c) => {
463721
463858
  });
463722
463859
  const server = new McpServer({
463723
463860
  name: "fulcrum",
463724
- version: "2.5.0"
463861
+ version: "2.5.2"
463725
463862
  });
463726
463863
  const client = new FulcrumClient(`http://localhost:${port}`);
463727
463864
  registerTools(server, client);
@@ -465897,7 +466034,7 @@ assistantRoutes.delete("/sessions/:id", async (c) => {
465897
466034
  });
465898
466035
  assistantRoutes.post("/sessions/:id/messages", async (c) => {
465899
466036
  const sessionId = c.req.param("id");
465900
- const { message, model, editorContent, images } = await c.req.json();
466037
+ const { message, model, editorContent, images, context } = await c.req.json();
465901
466038
  if ((!message || typeof message !== "string") && (!images || images.length === 0)) {
465902
466039
  return c.json({ error: "Message or images required" }, 400);
465903
466040
  }
@@ -465909,7 +466046,8 @@ assistantRoutes.post("/sessions/:id/messages", async (c) => {
465909
466046
  for await (const event of streamMessage2(sessionId, message || "", {
465910
466047
  modelId: model,
465911
466048
  editorContent,
465912
- images
466049
+ images,
466050
+ context
465913
466051
  })) {
465914
466052
  await stream3.writeSSE({
465915
466053
  event: event.type,
@@ -467053,7 +467191,7 @@ function checkPrStatus(prUrl) {
467053
467191
  }
467054
467192
  }
467055
467193
  async function pollPRs() {
467056
- const tasksWithPR = db2.select().from(tasks).where(and(isNotNull(tasks.prUrl), notInArray(tasks.status, ["DONE", "CANCELED"]))).all();
467194
+ const tasksWithPR = db2.select().from(tasks).where(and(isNotNull2(tasks.prUrl), notInArray(tasks.status, ["DONE", "CANCELED"]))).all();
467057
467195
  for (const task of tasksWithPR) {
467058
467196
  if (!task.prUrl)
467059
467197
  continue;