@knowsuchagency/fulcrum 3.3.0 → 3.4.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/bin/fulcrum.js CHANGED
@@ -44351,6 +44351,27 @@ var init_registry = __esm(() => {
44351
44351
  keywords: ["memory", "store", "save", "remember", "knowledge", "persist", "fact"],
44352
44352
  defer_loading: false
44353
44353
  },
44354
+ {
44355
+ name: "memory_search",
44356
+ description: "Search persistent memories using full-text search (FTS5)",
44357
+ category: "memory",
44358
+ keywords: ["memory", "search", "find", "query", "fts", "recall", "knowledge"],
44359
+ defer_loading: false
44360
+ },
44361
+ {
44362
+ name: "memory_list",
44363
+ description: "List persistent memories with optional tag filter",
44364
+ category: "memory",
44365
+ keywords: ["memory", "list", "browse", "tags", "filter", "all"],
44366
+ defer_loading: false
44367
+ },
44368
+ {
44369
+ name: "memory_delete",
44370
+ description: "Delete a persistent memory by ID",
44371
+ category: "memory",
44372
+ keywords: ["memory", "delete", "remove", "clean", "resolved", "outdated"],
44373
+ defer_loading: false
44374
+ },
44354
44375
  {
44355
44376
  name: "memory_file_read",
44356
44377
  description: "Read the master memory file (MEMORY.md)",
@@ -44464,7 +44485,7 @@ function getTodayInTimezone(timezone) {
44464
44485
 
44465
44486
  // cli/src/mcp/tools/tasks.ts
44466
44487
  import { basename as basename2 } from "path";
44467
- var registerTaskTools = (server, client) => {
44488
+ function registerListTasks(server, client) {
44468
44489
  server.tool("list_tasks", "List all Fulcrum tasks with flexible filtering. Supports text search across title/tags/project, multi-tag filtering (OR logic), multi-status filtering, date range, and overdue detection.", {
44469
44490
  status: exports_external.optional(TaskStatusSchema2).describe("Filter by single task status (use statuses for multiple)"),
44470
44491
  statuses: exports_external.optional(exports_external.array(TaskStatusSchema2)).describe("Filter by multiple statuses (OR logic)"),
@@ -44556,20 +44577,8 @@ var registerTaskTools = (server, client) => {
44556
44577
  return handleToolError(err);
44557
44578
  }
44558
44579
  });
44559
- server.tool("get_task", "Get details of a specific task by ID, including dependencies and attachments", {
44560
- id: exports_external.string().describe("Task ID (UUID)")
44561
- }, async ({ id }) => {
44562
- try {
44563
- const [task, dependencies, attachments] = await Promise.all([
44564
- client.getTask(id),
44565
- client.getTaskDependencies(id),
44566
- client.listTaskAttachments(id)
44567
- ]);
44568
- return formatSuccess({ ...task, dependencies, attachments });
44569
- } catch (err) {
44570
- return handleToolError(err);
44571
- }
44572
- });
44580
+ }
44581
+ function registerCreateTask(server, client) {
44573
44582
  server.tool("create_task", "Create a new task. For worktree tasks, provide repoPath to create a git worktree. For non-worktree tasks, omit repoPath. When tags are provided, returns all existing tags for reference.", {
44574
44583
  title: exports_external.string().describe("Task title"),
44575
44584
  repoPath: exports_external.optional(exports_external.string()).describe("Absolute path to the git repository (optional for non-worktree tasks)"),
@@ -44630,6 +44639,8 @@ var registerTaskTools = (server, client) => {
44630
44639
  return handleToolError(err);
44631
44640
  }
44632
44641
  });
44642
+ }
44643
+ function registerUpdateTask(server, client) {
44633
44644
  server.tool("update_task", "Update task metadata (title or description)", {
44634
44645
  id: exports_external.string().describe("Task ID"),
44635
44646
  title: exports_external.optional(exports_external.string()).describe("New title"),
@@ -44647,6 +44658,82 @@ var registerTaskTools = (server, client) => {
44647
44658
  return handleToolError(err);
44648
44659
  }
44649
44660
  });
44661
+ }
44662
+ function registerAddTaskLink(server, client) {
44663
+ server.tool("add_task_link", "Add a URL link to a task (for documentation, related PRs, design files, etc.)", {
44664
+ taskId: exports_external.string().describe("Task ID"),
44665
+ url: exports_external.string().url().describe("URL to add"),
44666
+ label: exports_external.optional(exports_external.string()).describe("Display label (auto-detected if not provided)")
44667
+ }, async ({ taskId, url: url2, label }) => {
44668
+ try {
44669
+ const link = await client.addTaskLink(taskId, url2, label);
44670
+ return formatSuccess(link);
44671
+ } catch (err) {
44672
+ return handleToolError(err);
44673
+ }
44674
+ });
44675
+ }
44676
+ function registerAddTaskTag(server, client) {
44677
+ server.tool("add_task_tag", "Add a tag to a task for categorization. Returns similar existing tags to help catch typos.", {
44678
+ taskId: exports_external.string().describe("Task ID"),
44679
+ tag: exports_external.string().describe("Tag to add")
44680
+ }, async ({ taskId, tag }) => {
44681
+ try {
44682
+ const result = await client.addTaskTag(taskId, tag);
44683
+ const allTasks = await client.listTasks();
44684
+ const existingTags = new Set;
44685
+ for (const t2 of allTasks) {
44686
+ if (t2.tags) {
44687
+ for (const tg of t2.tags) {
44688
+ existingTags.add(tg);
44689
+ }
44690
+ }
44691
+ }
44692
+ const tagLower = tag.toLowerCase();
44693
+ const similarTags = Array.from(existingTags).filter((tg) => tg !== tag && (tg.toLowerCase().includes(tagLower) || tagLower.includes(tg.toLowerCase())));
44694
+ return formatSuccess({
44695
+ ...result,
44696
+ similarTags: similarTags.length > 0 ? similarTags : undefined
44697
+ });
44698
+ } catch (err) {
44699
+ return handleToolError(err);
44700
+ }
44701
+ });
44702
+ }
44703
+ function registerSetTaskDueDate(server, client) {
44704
+ server.tool("set_task_due_date", "Set or clear the due date for a task", {
44705
+ taskId: exports_external.string().describe("Task ID"),
44706
+ dueDate: exports_external.nullable(exports_external.string()).describe("Due date in YYYY-MM-DD format, or null to clear")
44707
+ }, async ({ taskId, dueDate }) => {
44708
+ try {
44709
+ const result = await client.setTaskDueDate(taskId, dueDate);
44710
+ return formatSuccess(result);
44711
+ } catch (err) {
44712
+ return handleToolError(err);
44713
+ }
44714
+ });
44715
+ }
44716
+ var registerTaskTools = (server, client) => {
44717
+ registerListTasks(server, client);
44718
+ registerCreateTask(server, client);
44719
+ registerUpdateTask(server, client);
44720
+ registerAddTaskLink(server, client);
44721
+ registerAddTaskTag(server, client);
44722
+ registerSetTaskDueDate(server, client);
44723
+ server.tool("get_task", "Get details of a specific task by ID, including dependencies and attachments", {
44724
+ id: exports_external.string().describe("Task ID (UUID)")
44725
+ }, async ({ id }) => {
44726
+ try {
44727
+ const [task, dependencies, attachments] = await Promise.all([
44728
+ client.getTask(id),
44729
+ client.getTaskDependencies(id),
44730
+ client.listTaskAttachments(id)
44731
+ ]);
44732
+ return formatSuccess({ ...task, dependencies, attachments });
44733
+ } catch (err) {
44734
+ return handleToolError(err);
44735
+ }
44736
+ });
44650
44737
  server.tool("delete_task", "Delete a task and optionally its linked git worktree", {
44651
44738
  id: exports_external.string().describe("Task ID"),
44652
44739
  deleteWorktree: exports_external.optional(exports_external.boolean()).describe("Also delete the linked git worktree (default: false)")
@@ -44671,18 +44758,6 @@ var registerTaskTools = (server, client) => {
44671
44758
  return handleToolError(err);
44672
44759
  }
44673
44760
  });
44674
- server.tool("add_task_link", "Add a URL link to a task (for documentation, related PRs, design files, etc.)", {
44675
- taskId: exports_external.string().describe("Task ID"),
44676
- url: exports_external.string().url().describe("URL to add"),
44677
- label: exports_external.optional(exports_external.string()).describe("Display label (auto-detected if not provided)")
44678
- }, async ({ taskId, url: url2, label }) => {
44679
- try {
44680
- const link = await client.addTaskLink(taskId, url2, label);
44681
- return formatSuccess(link);
44682
- } catch (err) {
44683
- return handleToolError(err);
44684
- }
44685
- });
44686
44761
  server.tool("remove_task_link", "Remove a URL link from a task", {
44687
44762
  taskId: exports_external.string().describe("Task ID"),
44688
44763
  linkId: exports_external.string().describe("Link ID to remove")
@@ -44704,31 +44779,6 @@ var registerTaskTools = (server, client) => {
44704
44779
  return handleToolError(err);
44705
44780
  }
44706
44781
  });
44707
- server.tool("add_task_tag", "Add a tag to a task for categorization. Returns similar existing tags to help catch typos.", {
44708
- taskId: exports_external.string().describe("Task ID"),
44709
- tag: exports_external.string().describe("Tag to add")
44710
- }, async ({ taskId, tag }) => {
44711
- try {
44712
- const result = await client.addTaskTag(taskId, tag);
44713
- const allTasks = await client.listTasks();
44714
- const existingTags = new Set;
44715
- for (const t2 of allTasks) {
44716
- if (t2.tags) {
44717
- for (const tg of t2.tags) {
44718
- existingTags.add(tg);
44719
- }
44720
- }
44721
- }
44722
- const tagLower = tag.toLowerCase();
44723
- const similarTags = Array.from(existingTags).filter((tg) => tg !== tag && (tg.toLowerCase().includes(tagLower) || tagLower.includes(tg.toLowerCase())));
44724
- return formatSuccess({
44725
- ...result,
44726
- similarTags: similarTags.length > 0 ? similarTags : undefined
44727
- });
44728
- } catch (err) {
44729
- return handleToolError(err);
44730
- }
44731
- });
44732
44782
  server.tool("remove_task_tag", "Remove a tag from a task", {
44733
44783
  taskId: exports_external.string().describe("Task ID"),
44734
44784
  tag: exports_external.string().describe("Tag to remove")
@@ -44740,17 +44790,6 @@ var registerTaskTools = (server, client) => {
44740
44790
  return handleToolError(err);
44741
44791
  }
44742
44792
  });
44743
- server.tool("set_task_due_date", "Set or clear the due date for a task", {
44744
- taskId: exports_external.string().describe("Task ID"),
44745
- dueDate: exports_external.nullable(exports_external.string()).describe("Due date in YYYY-MM-DD format, or null to clear")
44746
- }, async ({ taskId, dueDate }) => {
44747
- try {
44748
- const result = await client.setTaskDueDate(taskId, dueDate);
44749
- return formatSuccess(result);
44750
- } catch (err) {
44751
- return handleToolError(err);
44752
- }
44753
- });
44754
44793
  server.tool("get_task_dependencies", "Get the dependencies and dependents of a task, and whether it is blocked", {
44755
44794
  taskId: exports_external.string().describe("Task ID")
44756
44795
  }, async ({ taskId }) => {
@@ -45841,8 +45880,9 @@ var ChannelSchema, registerAssistantTools = (server, client) => {
45841
45880
  subject: exports_external.optional(exports_external.string()).describe("Email subject (for Gmail channel only)"),
45842
45881
  replyToMessageId: exports_external.optional(exports_external.string()).describe("Message ID to reply to (for threading)"),
45843
45882
  slack_blocks: exports_external.optional(exports_external.array(exports_external.record(exports_external.string(), exports_external.any()))).describe("Slack Block Kit blocks for rich formatting (Slack channel only). Array of block objects."),
45883
+ filePath: exports_external.optional(exports_external.string()).describe("Absolute path to a local file to upload alongside the message (Slack only). Use for sending images, documents, etc."),
45844
45884
  googleAccountId: exports_external.optional(exports_external.string()).describe("Google account ID for Gmail channel. If omitted, auto-resolves when exactly one Gmail-enabled account exists.")
45845
- }, async ({ channel, body, subject, replyToMessageId, slack_blocks, googleAccountId }) => {
45885
+ }, async ({ channel, body, subject, replyToMessageId, slack_blocks, filePath, googleAccountId }) => {
45846
45886
  try {
45847
45887
  if (channel === "gmail") {
45848
45888
  let accountId = googleAccountId;
@@ -45865,7 +45905,8 @@ var ChannelSchema, registerAssistantTools = (server, client) => {
45865
45905
  body,
45866
45906
  subject,
45867
45907
  replyToMessageId,
45868
- slackBlocks: slack_blocks
45908
+ slackBlocks: slack_blocks,
45909
+ filePath
45869
45910
  });
45870
45911
  return formatSuccess(result);
45871
45912
  } catch (err) {
@@ -46152,7 +46193,7 @@ var init_types5 = __esm(() => {
46152
46193
  });
46153
46194
 
46154
46195
  // cli/src/mcp/tools/memory.ts
46155
- var registerMemoryTools = (server, client) => {
46196
+ function registerMemoryStoreTool(server, client) {
46156
46197
  server.tool("memory_store", "Store a piece of knowledge in persistent memory. Use this to remember facts, preferences, decisions, patterns, or any information that should persist across conversations.", {
46157
46198
  content: exports_external.string().describe("The memory content to store. Be specific and self-contained."),
46158
46199
  tags: exports_external.optional(exports_external.array(exports_external.string())).describe('Optional tags for categorization (e.g., ["preference", "architecture", "decision"])'),
@@ -46165,6 +46206,52 @@ var registerMemoryTools = (server, client) => {
46165
46206
  return handleToolError(err);
46166
46207
  }
46167
46208
  });
46209
+ }
46210
+ function registerMemorySearchTool(server, client) {
46211
+ server.tool("memory_search", 'Search persistent memories using full-text search (FTS5). Supports boolean operators (AND, OR, NOT), phrase matching ("quoted"), and prefix matching (term*).', {
46212
+ query: exports_external.string().describe('Search query. Supports FTS5 syntax: AND, OR, NOT, "phrases", prefix*'),
46213
+ tags: exports_external.optional(exports_external.array(exports_external.string())).describe('Filter by tags (e.g., ["actionable", "preference"])'),
46214
+ limit: exports_external.optional(exports_external.number()).describe("Max results to return (default: 20)")
46215
+ }, async ({ query, tags, limit }) => {
46216
+ try {
46217
+ const result = await client.searchMemories({ query, tags, limit });
46218
+ return formatSuccess(result);
46219
+ } catch (err) {
46220
+ return handleToolError(err);
46221
+ }
46222
+ });
46223
+ }
46224
+ function registerMemoryListTool(server, client) {
46225
+ server.tool("memory_list", "List all persistent memories, optionally filtered by tags. Returns memories sorted by creation date (newest first).", {
46226
+ tags: exports_external.optional(exports_external.array(exports_external.string())).describe('Filter by tags (e.g., ["actionable"])'),
46227
+ limit: exports_external.optional(exports_external.number()).describe("Max results to return (default: 50)"),
46228
+ offset: exports_external.optional(exports_external.number()).describe("Offset for pagination")
46229
+ }, async ({ tags, limit, offset }) => {
46230
+ try {
46231
+ const result = await client.listMemories({ tags, limit, offset });
46232
+ return formatSuccess(result);
46233
+ } catch (err) {
46234
+ return handleToolError(err);
46235
+ }
46236
+ });
46237
+ }
46238
+ function registerMemoryDeleteTool(server, client) {
46239
+ server.tool("memory_delete", "Delete a persistent memory by ID. Use this to clean up resolved or outdated memories.", {
46240
+ id: exports_external.string().describe("The ID of the memory to delete")
46241
+ }, async ({ id }) => {
46242
+ try {
46243
+ const result = await client.deleteMemory(id);
46244
+ return formatSuccess(result);
46245
+ } catch (err) {
46246
+ return handleToolError(err);
46247
+ }
46248
+ });
46249
+ }
46250
+ var registerMemoryTools = (server, client) => {
46251
+ registerMemoryStoreTool(server, client);
46252
+ registerMemorySearchTool(server, client);
46253
+ registerMemoryListTool(server, client);
46254
+ registerMemoryDeleteTool(server, client);
46168
46255
  };
46169
46256
  var init_memory = __esm(() => {
46170
46257
  init_zod2();
@@ -46173,7 +46260,7 @@ var init_memory = __esm(() => {
46173
46260
  });
46174
46261
 
46175
46262
  // cli/src/mcp/tools/memory-file.ts
46176
- var registerMemoryFileTools = (server, client) => {
46263
+ var registerMemoryFileReadTool = (server, client) => {
46177
46264
  server.tool("memory_file_read", "Read the master memory file (MEMORY.md). This file contains persistent knowledge, user preferences, and instructions that are included in every conversation.", {}, async () => {
46178
46265
  try {
46179
46266
  const result = await client.readMemoryFile();
@@ -46182,6 +46269,8 @@ var registerMemoryFileTools = (server, client) => {
46182
46269
  return handleToolError(err);
46183
46270
  }
46184
46271
  });
46272
+ }, registerMemoryFileTools = (server, client) => {
46273
+ registerMemoryFileReadTool(server, client);
46185
46274
  server.tool("memory_file_update", "Update the master memory file. Provide full content to replace the entire file, or specify a section heading to update just that section. The memory file is included in every conversation, so keep it organized and concise.", {
46186
46275
  content: exports_external.string().describe("The content to write. If section is specified, this replaces only that section body."),
46187
46276
  section: exports_external.optional(exports_external.string()).describe('Optional markdown heading (e.g., "## Preferences") to update a specific section. If omitted, replaces the entire file.')
@@ -46332,7 +46421,7 @@ async function runMcpServer(urlOverride, portOverride) {
46332
46421
  const client = new FulcrumClient(urlOverride, portOverride);
46333
46422
  const server = new McpServer({
46334
46423
  name: "fulcrum",
46335
- version: "3.3.0"
46424
+ version: "3.4.0"
46336
46425
  });
46337
46426
  registerTools(server, client);
46338
46427
  const transport = new StdioServerTransport;
@@ -48681,7 +48770,7 @@ var marketplace_default = `{
48681
48770
  "name": "fulcrum",
48682
48771
  "source": "./",
48683
48772
  "description": "Task orchestration for Claude Code",
48684
- "version": "3.3.0",
48773
+ "version": "3.4.0",
48685
48774
  "skills": [
48686
48775
  "./skills/fulcrum"
48687
48776
  ],
@@ -49885,7 +49974,7 @@ function compareVersions(v1, v2) {
49885
49974
  var package_default = {
49886
49975
  name: "@knowsuchagency/fulcrum",
49887
49976
  private: true,
49888
- version: "3.3.0",
49977
+ version: "3.4.0",
49889
49978
  description: "Harness Attention. Orchestrate Agents. Ship.",
49890
49979
  license: "PolyForm-Perimeter-1.0.0",
49891
49980
  type: "module",
@@ -49901,7 +49990,7 @@ var package_default = {
49901
49990
  "db:studio": "drizzle-kit studio"
49902
49991
  },
49903
49992
  dependencies: {
49904
- "@anthropic-ai/claude-agent-sdk": "^0.2.34",
49993
+ "@anthropic-ai/claude-agent-sdk": "^0.2.37",
49905
49994
  "@atlaskit/pragmatic-drag-and-drop": "^1.7.7",
49906
49995
  "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.1.0",
49907
49996
  "@azurity/pure-nerd-font": "^3.0.5",