@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
|
-
|
|
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
|
-
|
|
44560
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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",
|