@chapterai/mcp 0.1.0 → 0.1.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.js +732 -382
- package/package.json +12 -11
package/dist/index.js
CHANGED
|
@@ -22352,30 +22352,30 @@ function getClient() {
|
|
|
22352
22352
|
return client;
|
|
22353
22353
|
}
|
|
22354
22354
|
var api = {
|
|
22355
|
-
|
|
22355
|
+
workspaces: {
|
|
22356
22356
|
list(input) {
|
|
22357
|
-
return getClient().
|
|
22357
|
+
return getClient().workspaces.list.query(input);
|
|
22358
22358
|
},
|
|
22359
22359
|
getById(input) {
|
|
22360
|
-
return getClient().
|
|
22360
|
+
return getClient().workspaces.getById.query(input);
|
|
22361
22361
|
},
|
|
22362
22362
|
getReadme(input) {
|
|
22363
|
-
return getClient().
|
|
22363
|
+
return getClient().workspaces.getReadme.query(input);
|
|
22364
22364
|
},
|
|
22365
22365
|
listFiles(input) {
|
|
22366
|
-
return getClient().
|
|
22366
|
+
return getClient().workspaces.listFiles.query(input);
|
|
22367
22367
|
},
|
|
22368
22368
|
listCommits(input) {
|
|
22369
|
-
return getClient().
|
|
22369
|
+
return getClient().workspaces.listCommits.query(input);
|
|
22370
22370
|
},
|
|
22371
22371
|
readFile(input) {
|
|
22372
|
-
return getClient().
|
|
22372
|
+
return getClient().workspaces.readFile.query(input);
|
|
22373
22373
|
},
|
|
22374
22374
|
listChangedFiles(input) {
|
|
22375
|
-
return getClient().
|
|
22375
|
+
return getClient().workspaces.listChangedFiles.query(input);
|
|
22376
22376
|
},
|
|
22377
22377
|
getFileDiff(input) {
|
|
22378
|
-
return getClient().
|
|
22378
|
+
return getClient().workspaces.getFileDiff.query(input);
|
|
22379
22379
|
}
|
|
22380
22380
|
},
|
|
22381
22381
|
changeRequests: {
|
|
@@ -22414,26 +22414,52 @@ var api = {
|
|
|
22414
22414
|
return getClient().search.semantic.query(input);
|
|
22415
22415
|
}
|
|
22416
22416
|
},
|
|
22417
|
-
|
|
22418
|
-
|
|
22419
|
-
return getClient().
|
|
22417
|
+
tasks: {
|
|
22418
|
+
list(input) {
|
|
22419
|
+
return getClient().tasks.list.query(input);
|
|
22420
22420
|
},
|
|
22421
|
-
|
|
22422
|
-
return getClient().
|
|
22421
|
+
getByNumber(input) {
|
|
22422
|
+
return getClient().tasks.getByNumber.query(input);
|
|
22423
22423
|
},
|
|
22424
|
-
|
|
22425
|
-
return getClient().
|
|
22424
|
+
create(input) {
|
|
22425
|
+
return getClient().tasks.create.mutate(input);
|
|
22426
22426
|
},
|
|
22427
|
-
|
|
22428
|
-
return getClient().
|
|
22427
|
+
update(input) {
|
|
22428
|
+
return getClient().tasks.update.mutate(input);
|
|
22429
|
+
}
|
|
22430
|
+
},
|
|
22431
|
+
activity: {
|
|
22432
|
+
feed(input) {
|
|
22433
|
+
return getClient().workspaces.getActivity.query(input);
|
|
22429
22434
|
},
|
|
22430
|
-
|
|
22431
|
-
return getClient().
|
|
22432
|
-
|
|
22433
|
-
|
|
22435
|
+
fileHistory(input) {
|
|
22436
|
+
return getClient().workspaces.getFileHistory.query(input);
|
|
22437
|
+
},
|
|
22438
|
+
stats(input) {
|
|
22439
|
+
return getClient().workspaces.getWorkspaceStats.query(input);
|
|
22440
|
+
}
|
|
22441
|
+
},
|
|
22442
|
+
intelligence: {
|
|
22443
|
+
scan(input) {
|
|
22444
|
+
return getClient().intelligence.scan.mutate(input);
|
|
22445
|
+
},
|
|
22446
|
+
query(input) {
|
|
22447
|
+
return getClient().intelligence.query.query(input);
|
|
22448
|
+
},
|
|
22449
|
+
findSymbol(input) {
|
|
22450
|
+
return getClient().intelligence.findSymbol.query(input);
|
|
22451
|
+
},
|
|
22452
|
+
findCopy(input) {
|
|
22453
|
+
return getClient().intelligence.findCopy.query(input);
|
|
22454
|
+
},
|
|
22455
|
+
contextForTask(input) {
|
|
22456
|
+
return getClient().intelligence.contextForTask.query(input);
|
|
22434
22457
|
},
|
|
22435
|
-
|
|
22436
|
-
return getClient().
|
|
22458
|
+
fileMap(input) {
|
|
22459
|
+
return getClient().intelligence.fileMap.query(input);
|
|
22460
|
+
},
|
|
22461
|
+
deps(input) {
|
|
22462
|
+
return getClient().intelligence.deps.query(input);
|
|
22437
22463
|
}
|
|
22438
22464
|
},
|
|
22439
22465
|
sessions: {
|
|
@@ -22446,46 +22472,54 @@ var api = {
|
|
|
22446
22472
|
}
|
|
22447
22473
|
};
|
|
22448
22474
|
|
|
22449
|
-
// src/lib/
|
|
22475
|
+
// src/lib/workspace-context.ts
|
|
22450
22476
|
import fs2 from "fs";
|
|
22451
22477
|
import path2 from "path";
|
|
22452
|
-
var
|
|
22453
|
-
|
|
22478
|
+
var LINK_PATH = path2.join(".chapter", "workspace.json");
|
|
22479
|
+
var LEGACY_LINK_FILENAME = ".chapter.json";
|
|
22480
|
+
function readLinkFile(filePath) {
|
|
22481
|
+
try {
|
|
22482
|
+
const raw = fs2.readFileSync(filePath, "utf-8");
|
|
22483
|
+
const data = JSON.parse(raw);
|
|
22484
|
+
const id = data.workspaceId ?? data.projectId;
|
|
22485
|
+
const name = data.workspaceName ?? data.projectName;
|
|
22486
|
+
if (!id || !name) return null;
|
|
22487
|
+
return { workspaceId: id, workspaceName: name };
|
|
22488
|
+
} catch {
|
|
22489
|
+
return null;
|
|
22490
|
+
}
|
|
22491
|
+
}
|
|
22492
|
+
function findWorkspaceLink() {
|
|
22454
22493
|
let dir = process.cwd();
|
|
22455
22494
|
while (true) {
|
|
22456
|
-
const
|
|
22457
|
-
if (fs2.existsSync(
|
|
22458
|
-
|
|
22459
|
-
|
|
22460
|
-
return JSON.parse(raw);
|
|
22461
|
-
} catch {
|
|
22462
|
-
return null;
|
|
22463
|
-
}
|
|
22464
|
-
}
|
|
22495
|
+
const newPath = path2.join(dir, LINK_PATH);
|
|
22496
|
+
if (fs2.existsSync(newPath)) return readLinkFile(newPath);
|
|
22497
|
+
const legacyPath = path2.join(dir, LEGACY_LINK_FILENAME);
|
|
22498
|
+
if (fs2.existsSync(legacyPath)) return readLinkFile(legacyPath);
|
|
22465
22499
|
const parent = path2.dirname(dir);
|
|
22466
22500
|
if (parent === dir) break;
|
|
22467
22501
|
dir = parent;
|
|
22468
22502
|
}
|
|
22469
22503
|
return null;
|
|
22470
22504
|
}
|
|
22471
|
-
async function
|
|
22472
|
-
if (
|
|
22473
|
-
if (
|
|
22474
|
-
const
|
|
22475
|
-
const match =
|
|
22476
|
-
(p) => p.name.toLowerCase() ===
|
|
22505
|
+
async function resolveWorkspaceId(workspaceId, workspaceName) {
|
|
22506
|
+
if (workspaceId) return workspaceId;
|
|
22507
|
+
if (workspaceName) {
|
|
22508
|
+
const workspaces = await api.workspaces.list();
|
|
22509
|
+
const match = workspaces.find(
|
|
22510
|
+
(p) => p.name.toLowerCase() === workspaceName.toLowerCase()
|
|
22477
22511
|
);
|
|
22478
22512
|
if (!match) {
|
|
22479
22513
|
throw new Error(
|
|
22480
|
-
`Project "${
|
|
22514
|
+
`Project "${workspaceName}" not found. Use list_workspaces to see available projects.`
|
|
22481
22515
|
);
|
|
22482
22516
|
}
|
|
22483
22517
|
return match.id;
|
|
22484
22518
|
}
|
|
22485
|
-
const link =
|
|
22486
|
-
if (link) return link.
|
|
22519
|
+
const link = findWorkspaceLink();
|
|
22520
|
+
if (link) return link.workspaceId;
|
|
22487
22521
|
throw new Error(
|
|
22488
|
-
"No project specified. Provide
|
|
22522
|
+
"No project specified. Provide workspaceId, workspaceName, or run from a directory linked to a Chapter workspace."
|
|
22489
22523
|
);
|
|
22490
22524
|
}
|
|
22491
22525
|
|
|
@@ -22515,24 +22549,24 @@ function success(text) {
|
|
|
22515
22549
|
return { content: [{ type: "text", text }] };
|
|
22516
22550
|
}
|
|
22517
22551
|
|
|
22518
|
-
// src/tools/
|
|
22519
|
-
function
|
|
22552
|
+
// src/tools/workspaces.ts
|
|
22553
|
+
function registerWorkspaceTools(server2) {
|
|
22520
22554
|
server2.tool(
|
|
22521
|
-
"
|
|
22522
|
-
"List all accessible Chapter
|
|
22555
|
+
"list_workspaces",
|
|
22556
|
+
"List all accessible Chapter workspaces",
|
|
22523
22557
|
{ orgId: external_exports.string().uuid().optional().describe("Filter by organization ID") },
|
|
22524
22558
|
async ({ orgId }) => {
|
|
22525
22559
|
try {
|
|
22526
|
-
const
|
|
22560
|
+
const workspaces = await api.workspaces.list(
|
|
22527
22561
|
orgId ? { orgId } : void 0
|
|
22528
22562
|
);
|
|
22529
|
-
const lines =
|
|
22563
|
+
const lines = workspaces.map(
|
|
22530
22564
|
(p) => `- ${p.name} (id: ${p.id}, backend: ${p.backend})${p.description ? ` \u2014 ${p.description}` : ""}`
|
|
22531
22565
|
);
|
|
22532
22566
|
return success(
|
|
22533
22567
|
lines.length > 0 ? `Found ${lines.length} project(s):
|
|
22534
22568
|
|
|
22535
|
-
${lines.join("\n")}` : "No
|
|
22569
|
+
${lines.join("\n")}` : "No workspaces found."
|
|
22536
22570
|
);
|
|
22537
22571
|
} catch (error2) {
|
|
22538
22572
|
return formatError2(error2);
|
|
@@ -22543,23 +22577,23 @@ ${lines.join("\n")}` : "No projects found."
|
|
|
22543
22577
|
"get_project",
|
|
22544
22578
|
"Get project details including README content",
|
|
22545
22579
|
{
|
|
22546
|
-
|
|
22547
|
-
|
|
22580
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
22581
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)")
|
|
22548
22582
|
},
|
|
22549
|
-
async ({
|
|
22583
|
+
async ({ workspaceId, workspaceName }) => {
|
|
22550
22584
|
try {
|
|
22551
|
-
const id = await
|
|
22552
|
-
const [
|
|
22553
|
-
api.
|
|
22554
|
-
api.
|
|
22585
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
22586
|
+
const [workspace, readme] = await Promise.all([
|
|
22587
|
+
api.workspaces.getById({ id }),
|
|
22588
|
+
api.workspaces.getReadme({ workspaceId: id })
|
|
22555
22589
|
]);
|
|
22556
22590
|
const parts = [
|
|
22557
|
-
`# ${
|
|
22558
|
-
|
|
22559
|
-
${
|
|
22591
|
+
`# ${workspace.name}`,
|
|
22592
|
+
workspace.description ? `
|
|
22593
|
+
${workspace.description}` : "",
|
|
22560
22594
|
`
|
|
22561
|
-
Backend: ${
|
|
22562
|
-
`Created: ${
|
|
22595
|
+
Backend: ${workspace.backend}`,
|
|
22596
|
+
`Created: ${workspace.createdAt}`,
|
|
22563
22597
|
readme.content ? `
|
|
22564
22598
|
## README
|
|
22565
22599
|
|
|
@@ -22572,34 +22606,72 @@ ${readme.content}` : ""
|
|
|
22572
22606
|
}
|
|
22573
22607
|
);
|
|
22574
22608
|
server2.tool(
|
|
22575
|
-
"
|
|
22576
|
-
"Get complete
|
|
22609
|
+
"get_workspace_overview",
|
|
22610
|
+
"Get complete workspace context in one call: README, file tree, recent commits, active tasks, and open changes. This is the best tool to start with when working on a workspace \u2014 it gives you full situational awareness.",
|
|
22577
22611
|
{
|
|
22578
|
-
|
|
22579
|
-
|
|
22612
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
22613
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)")
|
|
22580
22614
|
},
|
|
22581
|
-
async ({
|
|
22615
|
+
async ({ workspaceId, workspaceName }) => {
|
|
22582
22616
|
try {
|
|
22583
|
-
const id = await
|
|
22584
|
-
const [
|
|
22585
|
-
api.
|
|
22586
|
-
api.
|
|
22587
|
-
api.
|
|
22588
|
-
api.
|
|
22617
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
22618
|
+
const [workspace, readme, files, commits, openCRs, appliedCRs, activeTasks, todoTasks, recentSessions] = await Promise.all([
|
|
22619
|
+
api.workspaces.getById({ id }),
|
|
22620
|
+
api.workspaces.getReadme({ workspaceId: id }),
|
|
22621
|
+
api.workspaces.listFiles({ workspaceId: id, path: "" }),
|
|
22622
|
+
api.workspaces.listCommits({ workspaceId: id, limit: 10 }),
|
|
22589
22623
|
api.changeRequests.list({
|
|
22590
|
-
|
|
22624
|
+
workspaceId: id,
|
|
22591
22625
|
status: "open",
|
|
22592
|
-
limit:
|
|
22593
|
-
})
|
|
22626
|
+
limit: 5
|
|
22627
|
+
}),
|
|
22628
|
+
api.changeRequests.list({
|
|
22629
|
+
workspaceId: id,
|
|
22630
|
+
status: "applied",
|
|
22631
|
+
limit: 5
|
|
22632
|
+
}),
|
|
22633
|
+
api.tasks.list({ workspaceId: id, status: "in_progress" }),
|
|
22634
|
+
api.tasks.list({ workspaceId: id, status: "todo" }),
|
|
22635
|
+
api.sessions.list({ workspaceId: id, status: "completed", limit: 5 })
|
|
22594
22636
|
]);
|
|
22595
22637
|
const parts = [];
|
|
22596
|
-
parts.push(`# ${
|
|
22597
|
-
if (
|
|
22598
|
-
parts.push(`Backend: ${
|
|
22638
|
+
parts.push(`# ${workspace.name}`);
|
|
22639
|
+
if (workspace.description) parts.push(workspace.description);
|
|
22640
|
+
parts.push(`Backend: ${workspace.backend}`);
|
|
22599
22641
|
if (readme.content) {
|
|
22600
22642
|
parts.push("\n## README\n");
|
|
22601
22643
|
parts.push(readme.content);
|
|
22602
22644
|
}
|
|
22645
|
+
const allActiveTasks = [...activeTasks, ...todoTasks];
|
|
22646
|
+
if (allActiveTasks.length > 0) {
|
|
22647
|
+
parts.push("\n## Active Tasks\n");
|
|
22648
|
+
parts.push(
|
|
22649
|
+
allActiveTasks.map((t) => {
|
|
22650
|
+
const priority = t.priority !== "none" ? ` (${t.priority})` : "";
|
|
22651
|
+
const status = t.status === "in_progress" ? " [IN PROGRESS]" : "";
|
|
22652
|
+
return `- #${t.number}: ${t.title}${status}${priority}${t.description ? `
|
|
22653
|
+
${t.description.slice(0, 200)}` : ""}`;
|
|
22654
|
+
}).join("\n")
|
|
22655
|
+
);
|
|
22656
|
+
}
|
|
22657
|
+
const sessionsWithSummaries = recentSessions.filter((s) => s.summary);
|
|
22658
|
+
if (sessionsWithSummaries.length > 0) {
|
|
22659
|
+
parts.push("\n## Recent Sessions (what's been done)\n");
|
|
22660
|
+
parts.push(
|
|
22661
|
+
sessionsWithSummaries.map((s) => {
|
|
22662
|
+
try {
|
|
22663
|
+
const parsed = JSON.parse(s.summary);
|
|
22664
|
+
const lines = [`- **${s.title ?? "(untitled)"}** (${new Date(s.createdAt).toLocaleDateString()})`];
|
|
22665
|
+
lines.push(` Asked: ${parsed.whatWasAsked}`);
|
|
22666
|
+
lines.push(` Done: ${parsed.whatWasDone}`);
|
|
22667
|
+
if (parsed.whatToWatch) lines.push(` Watch: ${parsed.whatToWatch}`);
|
|
22668
|
+
return lines.join("\n");
|
|
22669
|
+
} catch {
|
|
22670
|
+
return `- ${s.title ?? "(untitled)"} (${new Date(s.createdAt).toLocaleDateString()})`;
|
|
22671
|
+
}
|
|
22672
|
+
}).join("\n")
|
|
22673
|
+
);
|
|
22674
|
+
}
|
|
22603
22675
|
parts.push("\n## File Tree (root)\n");
|
|
22604
22676
|
if (files.length > 0) {
|
|
22605
22677
|
parts.push(
|
|
@@ -22608,7 +22680,7 @@ ${readme.content}` : ""
|
|
|
22608
22680
|
} else {
|
|
22609
22681
|
parts.push("(empty)");
|
|
22610
22682
|
}
|
|
22611
|
-
parts.push("\n## Recent
|
|
22683
|
+
parts.push("\n## Recent Commits\n");
|
|
22612
22684
|
if (commits.length > 0) {
|
|
22613
22685
|
parts.push(
|
|
22614
22686
|
commits.map(
|
|
@@ -22618,15 +22690,23 @@ ${readme.content}` : ""
|
|
|
22618
22690
|
} else {
|
|
22619
22691
|
parts.push("No commits yet.");
|
|
22620
22692
|
}
|
|
22621
|
-
|
|
22622
|
-
|
|
22693
|
+
if (appliedCRs.length > 0) {
|
|
22694
|
+
parts.push("\n## Recently Shipped Changes\n");
|
|
22695
|
+
parts.push(
|
|
22696
|
+
appliedCRs.map((cr) => {
|
|
22697
|
+
const summary = cr.aiSummary ? `
|
|
22698
|
+
${cr.aiSummary}` : "";
|
|
22699
|
+
return `- #${cr.number}: ${cr.title} (${cr.filesChanged} files, +${cr.additions}/-${cr.deletions})${summary}`;
|
|
22700
|
+
}).join("\n")
|
|
22701
|
+
);
|
|
22702
|
+
}
|
|
22703
|
+
if (openCRs.length > 0) {
|
|
22704
|
+
parts.push("\n## Open Changes (pending review)\n");
|
|
22623
22705
|
parts.push(
|
|
22624
|
-
|
|
22706
|
+
openCRs.map(
|
|
22625
22707
|
(cr) => `- #${cr.number}: ${cr.title} (${cr.filesChanged} files, +${cr.additions}/-${cr.deletions})`
|
|
22626
22708
|
).join("\n")
|
|
22627
22709
|
);
|
|
22628
|
-
} else {
|
|
22629
|
-
parts.push("No open change requests.");
|
|
22630
22710
|
}
|
|
22631
22711
|
return success(parts.join("\n"));
|
|
22632
22712
|
} catch (error2) {
|
|
@@ -22640,18 +22720,18 @@ ${readme.content}` : ""
|
|
|
22640
22720
|
function registerFileTools(server2) {
|
|
22641
22721
|
server2.tool(
|
|
22642
22722
|
"list_files",
|
|
22643
|
-
"Browse files and directories at a path in a
|
|
22723
|
+
"Browse files and directories at a path in a workspace",
|
|
22644
22724
|
{
|
|
22645
|
-
|
|
22646
|
-
|
|
22725
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
22726
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
22647
22727
|
path: external_exports.string().default("").describe("Directory path to list (empty string for root)"),
|
|
22648
22728
|
ref: external_exports.string().optional().describe("Git ref (branch, tag, or commit SHA)")
|
|
22649
22729
|
},
|
|
22650
|
-
async ({
|
|
22730
|
+
async ({ workspaceId, workspaceName, path: path3, ref }) => {
|
|
22651
22731
|
try {
|
|
22652
|
-
const id = await
|
|
22653
|
-
const files = await api.
|
|
22654
|
-
|
|
22732
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
22733
|
+
const files = await api.workspaces.listFiles({
|
|
22734
|
+
workspaceId: id,
|
|
22655
22735
|
path: path3,
|
|
22656
22736
|
ref
|
|
22657
22737
|
});
|
|
@@ -22669,18 +22749,18 @@ function registerFileTools(server2) {
|
|
|
22669
22749
|
);
|
|
22670
22750
|
server2.tool(
|
|
22671
22751
|
"read_file",
|
|
22672
|
-
"Read the content of a file in a
|
|
22752
|
+
"Read the content of a file in a workspace",
|
|
22673
22753
|
{
|
|
22674
|
-
|
|
22675
|
-
|
|
22754
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
22755
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
22676
22756
|
path: external_exports.string().describe("File path to read"),
|
|
22677
22757
|
ref: external_exports.string().optional().describe("Git ref (branch, tag, or commit SHA)")
|
|
22678
22758
|
},
|
|
22679
|
-
async ({
|
|
22759
|
+
async ({ workspaceId, workspaceName, path: path3, ref }) => {
|
|
22680
22760
|
try {
|
|
22681
|
-
const id = await
|
|
22682
|
-
const result = await api.
|
|
22683
|
-
|
|
22761
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
22762
|
+
const result = await api.workspaces.readFile({
|
|
22763
|
+
workspaceId: id,
|
|
22684
22764
|
path: path3,
|
|
22685
22765
|
ref
|
|
22686
22766
|
});
|
|
@@ -22692,24 +22772,24 @@ function registerFileTools(server2) {
|
|
|
22692
22772
|
);
|
|
22693
22773
|
server2.tool(
|
|
22694
22774
|
"search_files",
|
|
22695
|
-
"Find files by name pattern in a
|
|
22775
|
+
"Find files by name pattern in a workspace (recursive, client-side filtering)",
|
|
22696
22776
|
{
|
|
22697
|
-
|
|
22698
|
-
|
|
22777
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
22778
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
22699
22779
|
pattern: external_exports.string().describe("Pattern to match against file names (case-insensitive substring match)"),
|
|
22700
22780
|
path: external_exports.string().default("").describe("Directory path to search from")
|
|
22701
22781
|
},
|
|
22702
|
-
async ({
|
|
22782
|
+
async ({ workspaceId, workspaceName, pattern, path: path3 }) => {
|
|
22703
22783
|
try {
|
|
22704
|
-
const id = await
|
|
22784
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
22705
22785
|
const matches = [];
|
|
22706
22786
|
const lowerPattern = pattern.toLowerCase();
|
|
22707
22787
|
const maxResults = 50;
|
|
22708
22788
|
const maxDepth = 5;
|
|
22709
22789
|
async function searchDir(dirPath, depth) {
|
|
22710
22790
|
if (depth > maxDepth || matches.length >= maxResults) return;
|
|
22711
|
-
const files = await api.
|
|
22712
|
-
|
|
22791
|
+
const files = await api.workspaces.listFiles({
|
|
22792
|
+
workspaceId: id,
|
|
22713
22793
|
path: dirPath
|
|
22714
22794
|
});
|
|
22715
22795
|
for (const f of files) {
|
|
@@ -22741,30 +22821,30 @@ function registerFileTools(server2) {
|
|
|
22741
22821
|
function registerChangeRequestTools(server2) {
|
|
22742
22822
|
server2.tool(
|
|
22743
22823
|
"list_change_requests",
|
|
22744
|
-
"List
|
|
22824
|
+
"List changes for a workspace, optionally filtered by status",
|
|
22745
22825
|
{
|
|
22746
|
-
|
|
22747
|
-
|
|
22826
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
22827
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
22748
22828
|
status: external_exports.enum(["draft", "open", "applied", "dismissed"]).optional().describe("Filter by status"),
|
|
22749
|
-
limit: external_exports.number().min(1).max(100).default(20).describe("Max results to return")
|
|
22829
|
+
limit: external_exports.coerce.number().min(1).max(100).default(20).describe("Max results to return")
|
|
22750
22830
|
},
|
|
22751
|
-
async ({
|
|
22831
|
+
async ({ workspaceId, workspaceName, status, limit }) => {
|
|
22752
22832
|
try {
|
|
22753
|
-
const id = await
|
|
22833
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
22754
22834
|
const crs = await api.changeRequests.list({
|
|
22755
|
-
|
|
22835
|
+
workspaceId: id,
|
|
22756
22836
|
status,
|
|
22757
22837
|
limit
|
|
22758
22838
|
});
|
|
22759
22839
|
if (crs.length === 0) {
|
|
22760
22840
|
return success(
|
|
22761
|
-
status ? `No ${status}
|
|
22841
|
+
status ? `No ${status} changes found.` : "No changes found."
|
|
22762
22842
|
);
|
|
22763
22843
|
}
|
|
22764
22844
|
const lines = crs.map(
|
|
22765
22845
|
(cr) => `- #${cr.number}: ${cr.title} [${cr.status}] (${cr.filesChanged} files, +${cr.additions}/-${cr.deletions})${cr.author ? ` by ${cr.author.name}` : ""}`
|
|
22766
22846
|
);
|
|
22767
|
-
return success(`Found ${crs.length} change
|
|
22847
|
+
return success(`Found ${crs.length} change(s):
|
|
22768
22848
|
|
|
22769
22849
|
${lines.join("\n")}`);
|
|
22770
22850
|
} catch (error2) {
|
|
@@ -22774,21 +22854,21 @@ ${lines.join("\n")}`);
|
|
|
22774
22854
|
);
|
|
22775
22855
|
server2.tool(
|
|
22776
22856
|
"get_change_request",
|
|
22777
|
-
"Get details of a specific change
|
|
22857
|
+
"Get details of a specific change including changed files and AI summary",
|
|
22778
22858
|
{
|
|
22779
|
-
|
|
22780
|
-
|
|
22781
|
-
number: external_exports.number().describe("Change
|
|
22859
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
22860
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
22861
|
+
number: external_exports.coerce.number().describe("Change number")
|
|
22782
22862
|
},
|
|
22783
|
-
async ({
|
|
22863
|
+
async ({ workspaceId, workspaceName, number: number3 }) => {
|
|
22784
22864
|
try {
|
|
22785
|
-
const id = await
|
|
22865
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
22786
22866
|
const cr = await api.changeRequests.getByNumber({
|
|
22787
|
-
|
|
22867
|
+
workspaceId: id,
|
|
22788
22868
|
number: number3
|
|
22789
22869
|
});
|
|
22790
22870
|
const changedFiles = await api.changeRequests.listChangedFiles({
|
|
22791
|
-
|
|
22871
|
+
workspaceId: id,
|
|
22792
22872
|
changeRequestId: cr.id
|
|
22793
22873
|
});
|
|
22794
22874
|
const parts = [
|
|
@@ -22823,15 +22903,15 @@ ${cr.aiSummary}`);
|
|
|
22823
22903
|
);
|
|
22824
22904
|
server2.tool(
|
|
22825
22905
|
"list_branches",
|
|
22826
|
-
"List branches available for creating
|
|
22906
|
+
"List branches available for creating changes. Shows branches ahead of main with linked session info.",
|
|
22827
22907
|
{
|
|
22828
|
-
|
|
22829
|
-
|
|
22908
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
22909
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)")
|
|
22830
22910
|
},
|
|
22831
|
-
async ({
|
|
22911
|
+
async ({ workspaceId, workspaceName }) => {
|
|
22832
22912
|
try {
|
|
22833
|
-
const id = await
|
|
22834
|
-
const branches = await api.changeRequests.listBranches({
|
|
22913
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
22914
|
+
const branches = await api.changeRequests.listBranches({ workspaceId: id });
|
|
22835
22915
|
if (branches.length === 0) {
|
|
22836
22916
|
return success("No draft branches found. Start a coding session to create one.");
|
|
22837
22917
|
}
|
|
@@ -22849,25 +22929,25 @@ ${lines.join("\n")}`);
|
|
|
22849
22929
|
);
|
|
22850
22930
|
server2.tool(
|
|
22851
22931
|
"create_change_request",
|
|
22852
|
-
"Create a new change
|
|
22932
|
+
"Create a new change from a source branch",
|
|
22853
22933
|
{
|
|
22854
|
-
|
|
22855
|
-
|
|
22856
|
-
title: external_exports.string().min(1).max(500).describe("Title for the change
|
|
22934
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
22935
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
22936
|
+
title: external_exports.string().min(1).max(500).describe("Title for the change"),
|
|
22857
22937
|
description: external_exports.string().max(5e3).optional().describe("Description of the changes"),
|
|
22858
22938
|
sourceBranch: external_exports.string().min(1).describe("Source branch name \u2014 all commits ahead of main form the CR")
|
|
22859
22939
|
},
|
|
22860
|
-
async ({
|
|
22940
|
+
async ({ workspaceId, workspaceName, title, description, sourceBranch }) => {
|
|
22861
22941
|
try {
|
|
22862
|
-
const id = await
|
|
22942
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
22863
22943
|
const cr = await api.changeRequests.create({
|
|
22864
|
-
|
|
22944
|
+
workspaceId: id,
|
|
22865
22945
|
title,
|
|
22866
22946
|
description,
|
|
22867
22947
|
sourceBranch
|
|
22868
22948
|
});
|
|
22869
22949
|
return success(
|
|
22870
|
-
`Created change
|
|
22950
|
+
`Created change #${cr.number}: "${cr.title}" (id: ${cr.id})`
|
|
22871
22951
|
);
|
|
22872
22952
|
} catch (error2) {
|
|
22873
22953
|
return formatError2(error2);
|
|
@@ -22876,24 +22956,24 @@ ${lines.join("\n")}`);
|
|
|
22876
22956
|
);
|
|
22877
22957
|
server2.tool(
|
|
22878
22958
|
"apply_change_request",
|
|
22879
|
-
"Apply (merge) a change
|
|
22959
|
+
"Apply (merge) a change",
|
|
22880
22960
|
{
|
|
22881
|
-
|
|
22882
|
-
|
|
22883
|
-
number: external_exports.number().describe("Change
|
|
22961
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
22962
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
22963
|
+
number: external_exports.coerce.number().describe("Change number")
|
|
22884
22964
|
},
|
|
22885
|
-
async ({
|
|
22965
|
+
async ({ workspaceId, workspaceName, number: number3 }) => {
|
|
22886
22966
|
try {
|
|
22887
|
-
const id = await
|
|
22967
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
22888
22968
|
const cr = await api.changeRequests.getByNumber({
|
|
22889
|
-
|
|
22969
|
+
workspaceId: id,
|
|
22890
22970
|
number: number3
|
|
22891
22971
|
});
|
|
22892
22972
|
await api.changeRequests.applyChanges({
|
|
22893
|
-
|
|
22973
|
+
workspaceId: id,
|
|
22894
22974
|
changeRequestId: cr.id
|
|
22895
22975
|
});
|
|
22896
|
-
return success(`Change
|
|
22976
|
+
return success(`Change #${number3} has been applied.`);
|
|
22897
22977
|
} catch (error2) {
|
|
22898
22978
|
return formatError2(error2);
|
|
22899
22979
|
}
|
|
@@ -22901,24 +22981,24 @@ ${lines.join("\n")}`);
|
|
|
22901
22981
|
);
|
|
22902
22982
|
server2.tool(
|
|
22903
22983
|
"dismiss_change_request",
|
|
22904
|
-
"Dismiss (close without applying) a change
|
|
22984
|
+
"Dismiss (close without applying) a change",
|
|
22905
22985
|
{
|
|
22906
|
-
|
|
22907
|
-
|
|
22908
|
-
number: external_exports.number().describe("Change
|
|
22986
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
22987
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
22988
|
+
number: external_exports.coerce.number().describe("Change number")
|
|
22909
22989
|
},
|
|
22910
|
-
async ({
|
|
22990
|
+
async ({ workspaceId, workspaceName, number: number3 }) => {
|
|
22911
22991
|
try {
|
|
22912
|
-
const id = await
|
|
22992
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
22913
22993
|
const cr = await api.changeRequests.getByNumber({
|
|
22914
|
-
|
|
22994
|
+
workspaceId: id,
|
|
22915
22995
|
number: number3
|
|
22916
22996
|
});
|
|
22917
22997
|
await api.changeRequests.dismiss({
|
|
22918
|
-
|
|
22998
|
+
workspaceId: id,
|
|
22919
22999
|
changeRequestId: cr.id
|
|
22920
23000
|
});
|
|
22921
|
-
return success(`Change
|
|
23001
|
+
return success(`Change #${number3} has been dismissed.`);
|
|
22922
23002
|
} catch (error2) {
|
|
22923
23003
|
return formatError2(error2);
|
|
22924
23004
|
}
|
|
@@ -22926,12 +23006,12 @@ ${lines.join("\n")}`);
|
|
|
22926
23006
|
);
|
|
22927
23007
|
server2.tool(
|
|
22928
23008
|
"add_comment",
|
|
22929
|
-
"Add a comment to a change
|
|
23009
|
+
"Add a comment to a change. Can be a general comment or an inline code comment (provide path, line, and side for inline).",
|
|
22930
23010
|
{
|
|
22931
|
-
changeRequestId: external_exports.string().uuid().describe("Change
|
|
23011
|
+
changeRequestId: external_exports.string().uuid().describe("Change ID"),
|
|
22932
23012
|
body: external_exports.string().min(1).max(1e4).describe("Comment text"),
|
|
22933
23013
|
path: external_exports.string().optional().describe("File path (for inline code comments)"),
|
|
22934
|
-
line: external_exports.number().int().positive().optional().describe("Line number (for inline code comments)"),
|
|
23014
|
+
line: external_exports.coerce.number().int().positive().optional().describe("Line number (for inline code comments)"),
|
|
22935
23015
|
side: external_exports.enum(["LEFT", "RIGHT"]).optional().describe("Diff side (for inline code comments)")
|
|
22936
23016
|
},
|
|
22937
23017
|
async ({ changeRequestId, body, path: path3, line, side }) => {
|
|
@@ -22965,18 +23045,18 @@ ${h.lines.join("\n")}`
|
|
|
22965
23045
|
function registerCommitTools(server2) {
|
|
22966
23046
|
server2.tool(
|
|
22967
23047
|
"list_commits",
|
|
22968
|
-
"List recent commit history for a
|
|
23048
|
+
"List recent commit history for a workspace",
|
|
22969
23049
|
{
|
|
22970
|
-
|
|
22971
|
-
|
|
23050
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
23051
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
22972
23052
|
ref: external_exports.string().optional().describe("Git ref (branch, tag, or commit SHA)"),
|
|
22973
|
-
limit: external_exports.number().min(1).max(200).default(20).describe("Max commits to return")
|
|
23053
|
+
limit: external_exports.coerce.number().min(1).max(200).default(20).describe("Max commits to return")
|
|
22974
23054
|
},
|
|
22975
|
-
async ({
|
|
23055
|
+
async ({ workspaceId, workspaceName, ref, limit }) => {
|
|
22976
23056
|
try {
|
|
22977
|
-
const id = await
|
|
22978
|
-
const commits = await api.
|
|
22979
|
-
|
|
23057
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23058
|
+
const commits = await api.workspaces.listCommits({
|
|
23059
|
+
workspaceId: id,
|
|
22980
23060
|
ref,
|
|
22981
23061
|
limit
|
|
22982
23062
|
});
|
|
@@ -22996,29 +23076,29 @@ ${lines.join("\n")}`);
|
|
|
22996
23076
|
);
|
|
22997
23077
|
server2.tool(
|
|
22998
23078
|
"get_diff",
|
|
22999
|
-
"Get the diff for a change
|
|
23079
|
+
"Get the diff for a change or a specific commit. Provide either changeRequestNumber or commitOid.",
|
|
23000
23080
|
{
|
|
23001
|
-
|
|
23002
|
-
|
|
23003
|
-
changeRequestNumber: external_exports.number().optional().describe("Change
|
|
23081
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
23082
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
23083
|
+
changeRequestNumber: external_exports.coerce.number().optional().describe("Change number (to get the full diff)"),
|
|
23004
23084
|
commitOid: external_exports.string().optional().describe("Commit OID (to get a single commit diff)")
|
|
23005
23085
|
},
|
|
23006
|
-
async ({
|
|
23086
|
+
async ({ workspaceId, workspaceName, changeRequestNumber, commitOid }) => {
|
|
23007
23087
|
try {
|
|
23008
23088
|
if (!changeRequestNumber && !commitOid) {
|
|
23009
23089
|
return formatError2(
|
|
23010
23090
|
new Error("Provide either changeRequestNumber or commitOid.")
|
|
23011
23091
|
);
|
|
23012
23092
|
}
|
|
23013
|
-
const id = await
|
|
23093
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23014
23094
|
const maxFiles = 10;
|
|
23015
23095
|
if (changeRequestNumber) {
|
|
23016
23096
|
const cr = await api.changeRequests.getByNumber({
|
|
23017
|
-
|
|
23097
|
+
workspaceId: id,
|
|
23018
23098
|
number: changeRequestNumber
|
|
23019
23099
|
});
|
|
23020
23100
|
const changedFiles2 = await api.changeRequests.listChangedFiles({
|
|
23021
|
-
|
|
23101
|
+
workspaceId: id,
|
|
23022
23102
|
changeRequestId: cr.id
|
|
23023
23103
|
});
|
|
23024
23104
|
const filesToShow2 = changedFiles2.slice(0, maxFiles);
|
|
@@ -23029,7 +23109,7 @@ ${lines.join("\n")}`);
|
|
|
23029
23109
|
];
|
|
23030
23110
|
for (const file of filesToShow2) {
|
|
23031
23111
|
const diff = await api.changeRequests.getFileDiff({
|
|
23032
|
-
|
|
23112
|
+
workspaceId: id,
|
|
23033
23113
|
changeRequestId: cr.id,
|
|
23034
23114
|
filePath: file.path
|
|
23035
23115
|
});
|
|
@@ -23045,8 +23125,8 @@ ${lines.join("\n")}`);
|
|
|
23045
23125
|
}
|
|
23046
23126
|
return success(parts2.join("\n"));
|
|
23047
23127
|
}
|
|
23048
|
-
const changedFiles = await api.
|
|
23049
|
-
|
|
23128
|
+
const changedFiles = await api.workspaces.listChangedFiles({
|
|
23129
|
+
workspaceId: id,
|
|
23050
23130
|
commitOid
|
|
23051
23131
|
});
|
|
23052
23132
|
const filesToShow = changedFiles.slice(0, maxFiles);
|
|
@@ -23056,8 +23136,8 @@ ${lines.join("\n")}`);
|
|
|
23056
23136
|
`
|
|
23057
23137
|
];
|
|
23058
23138
|
for (const file of filesToShow) {
|
|
23059
|
-
const diff = await api.
|
|
23060
|
-
|
|
23139
|
+
const diff = await api.workspaces.getFileDiff({
|
|
23140
|
+
workspaceId: id,
|
|
23061
23141
|
commitOid,
|
|
23062
23142
|
filePath: file.path
|
|
23063
23143
|
});
|
|
@@ -23083,24 +23163,34 @@ ${lines.join("\n")}`);
|
|
|
23083
23163
|
function registerSessionTools(server2) {
|
|
23084
23164
|
server2.tool(
|
|
23085
23165
|
"list_sessions",
|
|
23086
|
-
"List AI coding sessions
|
|
23166
|
+
"List AI coding sessions in a workspace with summaries of what was done. Use this to understand previous work before starting a new session.",
|
|
23087
23167
|
{
|
|
23088
|
-
|
|
23089
|
-
|
|
23168
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
23169
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
23090
23170
|
status: external_exports.enum(["active", "completed", "archived"]).optional().describe("Filter by status"),
|
|
23091
|
-
limit: external_exports.number().min(1).max(100).default(20).describe("Max results to return")
|
|
23171
|
+
limit: external_exports.coerce.number().min(1).max(100).default(20).describe("Max results to return")
|
|
23092
23172
|
},
|
|
23093
|
-
async ({
|
|
23173
|
+
async ({ workspaceId, workspaceName, status, limit }) => {
|
|
23094
23174
|
try {
|
|
23095
|
-
const id = await
|
|
23175
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23096
23176
|
const sessions = await api.sessions.list({
|
|
23097
|
-
|
|
23177
|
+
workspaceId: id,
|
|
23098
23178
|
status,
|
|
23099
23179
|
limit
|
|
23100
23180
|
});
|
|
23101
|
-
const lines = sessions.map(
|
|
23102
|
-
|
|
23103
|
-
|
|
23181
|
+
const lines = sessions.map((s) => {
|
|
23182
|
+
const parts = [
|
|
23183
|
+
`- ${s.title ?? "(untitled)"} (id: ${s.id}, by ${s.userName ?? "unknown"}, ${s.messageCount} messages, ${new Date(s.createdAt).toLocaleDateString()})`
|
|
23184
|
+
];
|
|
23185
|
+
if (s.summary) {
|
|
23186
|
+
try {
|
|
23187
|
+
const parsed = JSON.parse(s.summary);
|
|
23188
|
+
parts.push(` \u2192 ${parsed.whatWasDone}`);
|
|
23189
|
+
} catch {
|
|
23190
|
+
}
|
|
23191
|
+
}
|
|
23192
|
+
return parts.join("\n");
|
|
23193
|
+
});
|
|
23104
23194
|
return success(
|
|
23105
23195
|
lines.length > 0 ? `Found ${lines.length} session(s):
|
|
23106
23196
|
|
|
@@ -23115,25 +23205,41 @@ ${lines.join("\n")}` : "No sessions found."
|
|
|
23115
23205
|
"get_session",
|
|
23116
23206
|
"Get a session with its full chat history",
|
|
23117
23207
|
{
|
|
23118
|
-
|
|
23119
|
-
|
|
23208
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
23209
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
23120
23210
|
sessionId: external_exports.string().uuid().describe("Session ID")
|
|
23121
23211
|
},
|
|
23122
|
-
async ({
|
|
23212
|
+
async ({ workspaceId, workspaceName, sessionId }) => {
|
|
23123
23213
|
try {
|
|
23124
|
-
const id = await
|
|
23214
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23125
23215
|
const session = await api.sessions.getById({
|
|
23126
|
-
|
|
23216
|
+
workspaceId: id,
|
|
23127
23217
|
sessionId
|
|
23128
23218
|
});
|
|
23129
23219
|
const parts = [];
|
|
23130
23220
|
parts.push(`# ${session.title ?? "(untitled)"}`);
|
|
23131
|
-
if (session.
|
|
23221
|
+
if (session.client) parts.push(`Client: ${session.client}`);
|
|
23132
23222
|
parts.push(`Status: ${session.status}`);
|
|
23133
23223
|
if (session.branch) parts.push(`Branch: ${session.branch}`);
|
|
23134
23224
|
if (session.changeRequestNumber != null)
|
|
23135
|
-
parts.push(`Change
|
|
23225
|
+
parts.push(`Change: #${session.changeRequestNumber}`);
|
|
23226
|
+
if (session.task)
|
|
23227
|
+
parts.push(`Task: #${session.task.number} ${session.task.title} [${session.task.status}]`);
|
|
23136
23228
|
parts.push(`Created: ${session.createdAt}`);
|
|
23229
|
+
if (session.summary) {
|
|
23230
|
+
try {
|
|
23231
|
+
const s = JSON.parse(session.summary);
|
|
23232
|
+
parts.push("\n## Summary\n");
|
|
23233
|
+
parts.push(`**Asked:** ${s.whatWasAsked}`);
|
|
23234
|
+
parts.push(`**Done:** ${s.whatWasDone}`);
|
|
23235
|
+
if (s.whatToTest.length > 0) {
|
|
23236
|
+
parts.push("**To verify:**");
|
|
23237
|
+
s.whatToTest.forEach((item) => parts.push(`- ${item}`));
|
|
23238
|
+
}
|
|
23239
|
+
if (s.whatToWatch) parts.push(`**Watch out:** ${s.whatToWatch}`);
|
|
23240
|
+
} catch {
|
|
23241
|
+
}
|
|
23242
|
+
}
|
|
23137
23243
|
parts.push(`
|
|
23138
23244
|
## Conversation (${session.messages.length} messages)
|
|
23139
23245
|
`);
|
|
@@ -23157,23 +23263,23 @@ ${lines.join("\n")}` : "No sessions found."
|
|
|
23157
23263
|
function registerSearchTools(server2) {
|
|
23158
23264
|
server2.tool(
|
|
23159
23265
|
"search_project",
|
|
23160
|
-
|
|
23266
|
+
`Search the workspace's session and change history using natural language. This is the PRIMARY way to answer questions about why something was done, when a feature was added or removed, or what happened in past work. Always try this BEFORE falling back to git log or file diffs. Examples: "why did we remove the code tab?", "when did we add the serif font?", "who fixed the auth bug?"`,
|
|
23161
23267
|
{
|
|
23162
|
-
|
|
23163
|
-
"Project ID (auto-resolved from
|
|
23268
|
+
workspaceId: external_exports.string().uuid().optional().describe(
|
|
23269
|
+
"Project ID (auto-resolved from workspace link if omitted)"
|
|
23164
23270
|
),
|
|
23165
|
-
|
|
23271
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
23166
23272
|
query: external_exports.string().min(1).describe("Natural language search query"),
|
|
23167
23273
|
entityTypes: external_exports.array(external_exports.enum(["session", "change_request"])).optional().describe(
|
|
23168
|
-
"Filter by entity type. Omit to search both sessions and
|
|
23274
|
+
"Filter by entity type. Omit to search both sessions and changes."
|
|
23169
23275
|
),
|
|
23170
23276
|
limit: external_exports.number().min(1).max(50).default(10).describe("Max results to return")
|
|
23171
23277
|
},
|
|
23172
|
-
async ({
|
|
23278
|
+
async ({ workspaceId, workspaceName, query, entityTypes, limit }) => {
|
|
23173
23279
|
try {
|
|
23174
|
-
const id = await
|
|
23280
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23175
23281
|
const results = await api.search.semantic({
|
|
23176
|
-
|
|
23282
|
+
workspaceId: id,
|
|
23177
23283
|
query,
|
|
23178
23284
|
entityTypes,
|
|
23179
23285
|
limit
|
|
@@ -23184,7 +23290,7 @@ function registerSearchTools(server2) {
|
|
|
23184
23290
|
);
|
|
23185
23291
|
}
|
|
23186
23292
|
const lines = results.map((r, i) => {
|
|
23187
|
-
const type = r.entityType === "session" ? "Session" : "Change
|
|
23293
|
+
const type = r.entityType === "session" ? "Session" : "Change";
|
|
23188
23294
|
return `${i + 1}. [${type}] ${r.snippet} (id: ${r.entityId}, relevance: ${r.similarity.toFixed(2)})`;
|
|
23189
23295
|
});
|
|
23190
23296
|
return success(
|
|
@@ -23199,208 +23305,450 @@ ${lines.join("\n")}`
|
|
|
23199
23305
|
);
|
|
23200
23306
|
}
|
|
23201
23307
|
|
|
23202
|
-
// src/tools/
|
|
23203
|
-
|
|
23308
|
+
// src/tools/tasks.ts
|
|
23309
|
+
var STATUS_LABELS = {
|
|
23310
|
+
backlog: "Backlog",
|
|
23311
|
+
todo: "To Do",
|
|
23312
|
+
in_progress: "In Progress",
|
|
23313
|
+
done: "Done"
|
|
23314
|
+
};
|
|
23315
|
+
var PRIORITY_LABELS = {
|
|
23316
|
+
none: "None",
|
|
23317
|
+
urgent: "Urgent",
|
|
23318
|
+
high: "High",
|
|
23319
|
+
medium: "Medium",
|
|
23320
|
+
low: "Low"
|
|
23321
|
+
};
|
|
23322
|
+
function registerTaskTools(server2) {
|
|
23204
23323
|
server2.tool(
|
|
23205
|
-
"
|
|
23206
|
-
|
|
23324
|
+
"list_tasks",
|
|
23325
|
+
'List tasks in the workspace backlog. Use this at the START of your work to see what needs to be done. If you are working on a specific task, update its status to "in_progress" using update_task before you begin.',
|
|
23207
23326
|
{
|
|
23208
|
-
|
|
23209
|
-
|
|
23327
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
23328
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
23329
|
+
status: external_exports.enum(["backlog", "todo", "in_progress", "done"]).optional().describe("Filter by status"),
|
|
23330
|
+
priority: external_exports.enum(["none", "urgent", "high", "medium", "low"]).optional().describe("Filter by priority")
|
|
23210
23331
|
},
|
|
23211
|
-
async ({
|
|
23332
|
+
async ({ workspaceId, workspaceName, status, priority }) => {
|
|
23212
23333
|
try {
|
|
23213
|
-
const id = await
|
|
23214
|
-
const
|
|
23215
|
-
|
|
23334
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23335
|
+
const tasks = await api.tasks.list({ workspaceId: id, status, priority });
|
|
23336
|
+
if (tasks.length === 0) {
|
|
23337
|
+
return success("No tasks found.");
|
|
23338
|
+
}
|
|
23339
|
+
const lines = tasks.map((t) => {
|
|
23340
|
+
const parts = [`- #${t.number}: ${t.title}`];
|
|
23341
|
+
parts.push(`[${STATUS_LABELS[t.status] ?? t.status}]`);
|
|
23342
|
+
if (t.priority !== "none") parts.push(`(${PRIORITY_LABELS[t.priority] ?? t.priority})`);
|
|
23343
|
+
if (t.assignee) parts.push(`assigned to ${t.assignee.name}`);
|
|
23344
|
+
if (t.sessionCount > 0) parts.push(`(${t.sessionCount} session${t.sessionCount > 1 ? "s" : ""})`);
|
|
23345
|
+
return parts.join(" ");
|
|
23216
23346
|
});
|
|
23217
|
-
|
|
23218
|
-
|
|
23219
|
-
|
|
23220
|
-
|
|
23221
|
-
|
|
23222
|
-
|
|
23223
|
-
|
|
23224
|
-
|
|
23225
|
-
|
|
23226
|
-
|
|
23227
|
-
|
|
23228
|
-
|
|
23229
|
-
|
|
23230
|
-
|
|
23347
|
+
return success(`Found ${tasks.length} task(s):
|
|
23348
|
+
|
|
23349
|
+
${lines.join("\n")}`);
|
|
23350
|
+
} catch (error2) {
|
|
23351
|
+
return formatError2(error2);
|
|
23352
|
+
}
|
|
23353
|
+
}
|
|
23354
|
+
);
|
|
23355
|
+
server2.tool(
|
|
23356
|
+
"get_task",
|
|
23357
|
+
"Get full details of a task including its description, linked sessions, and history. Use this to understand requirements before starting work on a task.",
|
|
23358
|
+
{
|
|
23359
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
23360
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
23361
|
+
number: external_exports.coerce.number().describe("Task number (e.g. 1, 2, 3)")
|
|
23362
|
+
},
|
|
23363
|
+
async ({ workspaceId, workspaceName, number: number3 }) => {
|
|
23364
|
+
try {
|
|
23365
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23366
|
+
const task = await api.tasks.getByNumber({ workspaceId: id, number: number3 });
|
|
23367
|
+
const parts = [];
|
|
23368
|
+
parts.push(`# Task #${task.number}: ${task.title}`);
|
|
23369
|
+
parts.push(`Status: ${STATUS_LABELS[task.status] ?? task.status}`);
|
|
23370
|
+
parts.push(`Priority: ${PRIORITY_LABELS[task.priority] ?? task.priority}`);
|
|
23371
|
+
if (task.assignee) parts.push(`Assigned to: ${task.assignee.name}`);
|
|
23372
|
+
if (task.description) parts.push(`
|
|
23373
|
+
## Description
|
|
23374
|
+
|
|
23375
|
+
${task.description}`);
|
|
23376
|
+
if (task.sessions.length > 0) {
|
|
23377
|
+
parts.push(`
|
|
23378
|
+
## Linked Sessions (${task.sessions.length})
|
|
23379
|
+
`);
|
|
23380
|
+
for (const s of task.sessions) {
|
|
23381
|
+
const crInfo = s.changeRequestNumber != null ? ` (CR #${s.changeRequestNumber})` : "";
|
|
23382
|
+
parts.push(`- ${s.title ?? "(untitled)"} \u2014 ${s.status}${crInfo}`);
|
|
23383
|
+
if (s.summary) {
|
|
23384
|
+
try {
|
|
23385
|
+
const parsed = JSON.parse(s.summary);
|
|
23386
|
+
parts.push(` Done: ${parsed.whatWasDone}`);
|
|
23387
|
+
if (parsed.whatToWatch) parts.push(` Watch: ${parsed.whatToWatch}`);
|
|
23388
|
+
} catch {
|
|
23389
|
+
}
|
|
23390
|
+
}
|
|
23231
23391
|
}
|
|
23232
|
-
lines.push("");
|
|
23233
23392
|
}
|
|
23234
|
-
|
|
23235
|
-
|
|
23236
|
-
|
|
23237
|
-
|
|
23238
|
-
|
|
23393
|
+
return success(parts.join("\n"));
|
|
23394
|
+
} catch (error2) {
|
|
23395
|
+
return formatError2(error2);
|
|
23396
|
+
}
|
|
23397
|
+
}
|
|
23398
|
+
);
|
|
23399
|
+
server2.tool(
|
|
23400
|
+
"create_task",
|
|
23401
|
+
"Create a new task in the workspace backlog. Use this to add work items that need to be built. If you discover bugs or follow-up work while coding, create tasks for them rather than leaving TODOs in code.",
|
|
23402
|
+
{
|
|
23403
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
23404
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
23405
|
+
title: external_exports.string().min(1).max(500).describe("Task title \u2014 a plain-language description of what to build"),
|
|
23406
|
+
description: external_exports.string().max(5e3).optional().describe("Optional longer description with details or acceptance criteria"),
|
|
23407
|
+
status: external_exports.enum(["backlog", "todo", "in_progress", "done"]).optional().describe("Initial status (defaults to backlog)"),
|
|
23408
|
+
priority: external_exports.enum(["none", "urgent", "high", "medium", "low"]).optional().describe("Priority level")
|
|
23409
|
+
},
|
|
23410
|
+
async ({ workspaceId, workspaceName, title, description, status, priority }) => {
|
|
23411
|
+
try {
|
|
23412
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23413
|
+
const task = await api.tasks.create({
|
|
23414
|
+
workspaceId: id,
|
|
23415
|
+
title,
|
|
23416
|
+
description,
|
|
23417
|
+
status,
|
|
23418
|
+
priority
|
|
23419
|
+
});
|
|
23420
|
+
return success(
|
|
23421
|
+
`Created task #${task.number}: ${task.title}
|
|
23422
|
+
Status: ${STATUS_LABELS[task.status] ?? task.status}` + (task.priority !== "none" ? `
|
|
23423
|
+
Priority: ${PRIORITY_LABELS[task.priority] ?? task.priority}` : "")
|
|
23239
23424
|
);
|
|
23240
|
-
return success(lines.join("\n"));
|
|
23241
23425
|
} catch (error2) {
|
|
23242
23426
|
return formatError2(error2);
|
|
23243
23427
|
}
|
|
23244
23428
|
}
|
|
23245
23429
|
);
|
|
23246
23430
|
server2.tool(
|
|
23247
|
-
"
|
|
23248
|
-
|
|
23431
|
+
"update_task",
|
|
23432
|
+
`Update a task's status, title, description, or priority. IMPORTANT: You MUST use this tool to keep task status current as you work. Set status to "in_progress" when you start working on a task, and "done" when you finish. This is not optional \u2014 task tracking is how the team stays in sync.`,
|
|
23249
23433
|
{
|
|
23250
|
-
|
|
23251
|
-
|
|
23252
|
-
|
|
23434
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
23435
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
23436
|
+
number: external_exports.coerce.number().describe("Task number to update"),
|
|
23437
|
+
title: external_exports.string().min(1).max(500).optional().describe("New title"),
|
|
23438
|
+
description: external_exports.string().max(5e3).nullable().optional().describe("New description (null to clear)"),
|
|
23439
|
+
status: external_exports.enum(["backlog", "todo", "in_progress", "done"]).optional().describe('New status \u2014 set to "done" when the task is complete'),
|
|
23440
|
+
priority: external_exports.enum(["none", "urgent", "high", "medium", "low"]).optional().describe("New priority")
|
|
23253
23441
|
},
|
|
23254
|
-
async ({
|
|
23442
|
+
async ({ workspaceId, workspaceName, number: number3, title, description, status, priority }) => {
|
|
23255
23443
|
try {
|
|
23256
|
-
const id = await
|
|
23257
|
-
const
|
|
23258
|
-
|
|
23259
|
-
|
|
23444
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23445
|
+
const existing = await api.tasks.getByNumber({ workspaceId: id, number: number3 });
|
|
23446
|
+
const task = await api.tasks.update({
|
|
23447
|
+
taskId: existing.id,
|
|
23448
|
+
title,
|
|
23449
|
+
description,
|
|
23450
|
+
status,
|
|
23451
|
+
priority
|
|
23260
23452
|
});
|
|
23261
|
-
|
|
23262
|
-
|
|
23263
|
-
|
|
23264
|
-
|
|
23265
|
-
|
|
23266
|
-
for (const sym of symbols) {
|
|
23267
|
-
const exported = sym.exported ? " (exported)" : "";
|
|
23268
|
-
const parent = sym.parentName ? ` [${sym.parentName}]` : "";
|
|
23269
|
-
const sig = sym.signature ? `: ${sym.signature}` : "";
|
|
23270
|
-
lines.push(
|
|
23271
|
-
`- **${sym.symbolType}** \`${sym.name}\`${parent}${exported} (L${sym.startLine}-${sym.endLine})${sig}`
|
|
23272
|
-
);
|
|
23273
|
-
}
|
|
23274
|
-
return success(lines.join("\n"));
|
|
23453
|
+
return success(
|
|
23454
|
+
`Updated task #${task.number}: ${task.title}
|
|
23455
|
+
Status: ${STATUS_LABELS[task.status] ?? task.status}` + (task.priority !== "none" ? `
|
|
23456
|
+
Priority: ${PRIORITY_LABELS[task.priority] ?? task.priority}` : "")
|
|
23457
|
+
);
|
|
23275
23458
|
} catch (error2) {
|
|
23276
23459
|
return formatError2(error2);
|
|
23277
23460
|
}
|
|
23278
23461
|
}
|
|
23279
23462
|
);
|
|
23463
|
+
}
|
|
23464
|
+
|
|
23465
|
+
// src/tools/activity.ts
|
|
23466
|
+
function registerActivityTools(server2) {
|
|
23280
23467
|
server2.tool(
|
|
23281
|
-
"
|
|
23282
|
-
"Get
|
|
23468
|
+
"get_activity",
|
|
23469
|
+
"Get a timeline of recent activity in a workspace \u2014 sessions, changes, tasks, and commits. Use this to understand what has been happening recently.",
|
|
23283
23470
|
{
|
|
23284
|
-
|
|
23285
|
-
|
|
23286
|
-
|
|
23471
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
23472
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
23473
|
+
limit: external_exports.coerce.number().min(1).max(100).default(20).describe("Max events to return"),
|
|
23474
|
+
since: external_exports.string().optional().describe("ISO date \u2014 only show events after this date (default: 7 days ago)")
|
|
23287
23475
|
},
|
|
23288
|
-
async ({
|
|
23476
|
+
async ({ workspaceId, workspaceName, limit, since }) => {
|
|
23289
23477
|
try {
|
|
23290
|
-
const id = await
|
|
23291
|
-
const
|
|
23292
|
-
|
|
23293
|
-
|
|
23478
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23479
|
+
const events = await api.activity.feed({
|
|
23480
|
+
workspaceId: id,
|
|
23481
|
+
limit,
|
|
23482
|
+
since
|
|
23294
23483
|
});
|
|
23295
|
-
const lines =
|
|
23296
|
-
|
|
23297
|
-
|
|
23298
|
-
|
|
23299
|
-
|
|
23300
|
-
|
|
23301
|
-
|
|
23302
|
-
|
|
23303
|
-
|
|
23484
|
+
const lines = events.map((e) => {
|
|
23485
|
+
const date3 = new Date(e.timestamp).toLocaleString();
|
|
23486
|
+
const author = e.author ? ` by ${e.author}` : "";
|
|
23487
|
+
return `- [${e.type}] ${e.description}${author} (${date3})`;
|
|
23488
|
+
});
|
|
23489
|
+
return success(
|
|
23490
|
+
lines.length > 0 ? `Recent activity (${lines.length} events):
|
|
23491
|
+
|
|
23492
|
+
${lines.join("\n")}` : "No recent activity found."
|
|
23493
|
+
);
|
|
23494
|
+
} catch (error2) {
|
|
23495
|
+
return formatError2(error2);
|
|
23496
|
+
}
|
|
23497
|
+
}
|
|
23498
|
+
);
|
|
23499
|
+
server2.tool(
|
|
23500
|
+
"get_file_history",
|
|
23501
|
+
"Get the commit history for a specific file, including which AI sessions produced each change. Use this to understand how a file has evolved.",
|
|
23502
|
+
{
|
|
23503
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
23504
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
|
|
23505
|
+
filePath: external_exports.string().describe('Path to the file (e.g. "src/index.ts")'),
|
|
23506
|
+
limit: external_exports.coerce.number().min(1).max(100).default(10).describe("Max commits to return")
|
|
23507
|
+
},
|
|
23508
|
+
async ({ workspaceId, workspaceName, filePath, limit }) => {
|
|
23509
|
+
try {
|
|
23510
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23511
|
+
const history = await api.activity.fileHistory({
|
|
23512
|
+
workspaceId: id,
|
|
23513
|
+
filePath,
|
|
23514
|
+
limit
|
|
23515
|
+
});
|
|
23516
|
+
const lines = history.map((entry) => {
|
|
23517
|
+
const date3 = new Date(entry.timestamp).toLocaleString();
|
|
23518
|
+
const parts = [
|
|
23519
|
+
`- ${entry.commitOid.slice(0, 7)} ${entry.message} (${entry.author}, ${date3})`
|
|
23520
|
+
];
|
|
23521
|
+
if (entry.session) {
|
|
23522
|
+
parts.push(
|
|
23523
|
+
` Session: ${entry.session.title ?? "(untitled)"} (id: ${entry.session.id})`
|
|
23524
|
+
);
|
|
23525
|
+
}
|
|
23526
|
+
return parts.join("\n");
|
|
23527
|
+
});
|
|
23528
|
+
return success(
|
|
23529
|
+
lines.length > 0 ? `File history for ${filePath} (${lines.length} commits):
|
|
23530
|
+
|
|
23531
|
+
${lines.join("\n")}` : `No commit history found for ${filePath}.`
|
|
23532
|
+
);
|
|
23533
|
+
} catch (error2) {
|
|
23534
|
+
return formatError2(error2);
|
|
23535
|
+
}
|
|
23536
|
+
}
|
|
23537
|
+
);
|
|
23538
|
+
server2.tool(
|
|
23539
|
+
"get_workspace_stats",
|
|
23540
|
+
"Get high-level project statistics \u2014 sessions this week, total changes, active tasks, and total commits.",
|
|
23541
|
+
{
|
|
23542
|
+
workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
|
|
23543
|
+
workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)")
|
|
23544
|
+
},
|
|
23545
|
+
async ({ workspaceId, workspaceName }) => {
|
|
23546
|
+
try {
|
|
23547
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23548
|
+
const stats = await api.activity.stats({ workspaceId: id });
|
|
23549
|
+
const lines = [
|
|
23550
|
+
`Sessions this week: ${stats.sessionsThisWeek}`,
|
|
23551
|
+
`Total change requests: ${stats.totalChangeRequests}`,
|
|
23552
|
+
`Active tasks (in progress): ${stats.activeTasks}`,
|
|
23553
|
+
`Total commits: ${stats.totalCommits}`
|
|
23554
|
+
];
|
|
23555
|
+
return success(`Project stats:
|
|
23556
|
+
|
|
23557
|
+
${lines.join("\n")}`);
|
|
23558
|
+
} catch (error2) {
|
|
23559
|
+
return formatError2(error2);
|
|
23560
|
+
}
|
|
23561
|
+
}
|
|
23562
|
+
);
|
|
23563
|
+
}
|
|
23564
|
+
|
|
23565
|
+
// src/tools/intelligence.ts
|
|
23566
|
+
function registerIntelligenceTools(server2) {
|
|
23567
|
+
server2.tool(
|
|
23568
|
+
"scan_codebase",
|
|
23569
|
+
"Scan and index a codebase to build a structured map of all files, symbols, routes, endpoints, models, and copy. Run this once per workspace to enable fast lookups. Accepts an optional projectPath for local directories.",
|
|
23570
|
+
{
|
|
23571
|
+
workspaceId: external_exports.string().uuid().optional().describe("Workspace ID"),
|
|
23572
|
+
workspaceName: external_exports.string().optional().describe("Workspace name (alternative to ID)"),
|
|
23573
|
+
projectPath: external_exports.string().optional().describe("Local filesystem path to scan (overrides workspace path)")
|
|
23574
|
+
},
|
|
23575
|
+
async ({ workspaceId, workspaceName, projectPath }) => {
|
|
23576
|
+
try {
|
|
23577
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23578
|
+
const result = await api.intelligence.scan({
|
|
23579
|
+
workspaceId: id,
|
|
23580
|
+
projectPath
|
|
23581
|
+
});
|
|
23582
|
+
const lines = [
|
|
23583
|
+
`Codebase scanned in ${result.stats.durationMs}ms`,
|
|
23584
|
+
``,
|
|
23585
|
+
`Files: ${result.fileCount}`,
|
|
23586
|
+
`Symbols: ${result.symbolCount}`,
|
|
23587
|
+
`Routes: ${result.routeCount}`,
|
|
23588
|
+
`API endpoints: ${result.endpointCount}`,
|
|
23589
|
+
`DB models: ${result.modelCount}`,
|
|
23590
|
+
`Copy entries: ${result.copyCount}`,
|
|
23591
|
+
`Dependencies: ${result.dependencyCount}`,
|
|
23592
|
+
``,
|
|
23593
|
+
`Tech stack:`,
|
|
23594
|
+
` Languages: ${result.techStack.languages.join(", ")}`,
|
|
23595
|
+
` Frameworks: ${result.techStack.frameworks.join(", ")}`,
|
|
23596
|
+
` Databases: ${result.techStack.databases.join(", ")}`,
|
|
23597
|
+
` Package manager: ${result.techStack.packageManager ?? "unknown"}`
|
|
23598
|
+
];
|
|
23599
|
+
return success(lines.join("\n"));
|
|
23600
|
+
} catch (error2) {
|
|
23601
|
+
return formatError2(error2);
|
|
23602
|
+
}
|
|
23603
|
+
}
|
|
23604
|
+
);
|
|
23605
|
+
server2.tool(
|
|
23606
|
+
"query_codebase",
|
|
23607
|
+
"Search the codebase index for files, symbols, routes, endpoints, and copy matching a query. Returns ranked results with file paths and line numbers. The index must exist (run scan_codebase first).",
|
|
23608
|
+
{
|
|
23609
|
+
workspaceId: external_exports.string().uuid().optional().describe("Workspace ID"),
|
|
23610
|
+
workspaceName: external_exports.string().optional().describe("Workspace name (alternative to ID)"),
|
|
23611
|
+
query: external_exports.string().describe('Search query \u2014 e.g. "task creation", "auth middleware", "welcome page"'),
|
|
23612
|
+
limit: external_exports.number().optional().describe("Max results per category (default 20)")
|
|
23613
|
+
},
|
|
23614
|
+
async ({ workspaceId, workspaceName, query, limit }) => {
|
|
23615
|
+
try {
|
|
23616
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23617
|
+
const result = await api.intelligence.query({ workspaceId: id, query, limit });
|
|
23618
|
+
const sections = [];
|
|
23619
|
+
if (result.files.length > 0) {
|
|
23620
|
+
sections.push("## Files");
|
|
23621
|
+
for (const f of result.files) {
|
|
23622
|
+
const exports = f.exports.length > 0 ? ` (exports: ${f.exports.slice(0, 3).join(", ")})` : "";
|
|
23623
|
+
sections.push(`- \`${f.path}\` [${f.category}]${exports}`);
|
|
23304
23624
|
}
|
|
23305
23625
|
}
|
|
23306
|
-
|
|
23307
|
-
|
|
23308
|
-
|
|
23309
|
-
|
|
23310
|
-
|
|
23311
|
-
for (const d of deps.importedBy) {
|
|
23312
|
-
const specs = d.importSpecifiers?.join(", ") ?? "*";
|
|
23313
|
-
lines.push(`- ${d.sourceFilePath} \u2192 {${specs}}`);
|
|
23626
|
+
if (result.symbols.length > 0) {
|
|
23627
|
+
sections.push("\n## Symbols");
|
|
23628
|
+
for (const s of result.symbols) {
|
|
23629
|
+
const sig = s.signature ? ` \u2014 \`${s.signature}\`` : "";
|
|
23630
|
+
sections.push(`- \`${s.name}\` (${s.kind}) at \`${s.filePath}:${s.line}\`${sig}`);
|
|
23314
23631
|
}
|
|
23315
23632
|
}
|
|
23316
|
-
|
|
23633
|
+
if (result.copy.length > 0) {
|
|
23634
|
+
sections.push("\n## Copy/Text");
|
|
23635
|
+
for (const c of result.copy) {
|
|
23636
|
+
sections.push(`- "${c.text}" at \`${c.filePath}:${c.line}\` in ${c.context}`);
|
|
23637
|
+
}
|
|
23638
|
+
}
|
|
23639
|
+
if (result.routes.length > 0) {
|
|
23640
|
+
sections.push("\n## Routes");
|
|
23641
|
+
for (const r of result.routes) {
|
|
23642
|
+
sections.push(`- ${r.method ?? "ANY"} \`${r.path}\` \u2192 ${r.handler} at \`${r.filePath}:${r.line}\``);
|
|
23643
|
+
}
|
|
23644
|
+
}
|
|
23645
|
+
if (result.endpoints.length > 0) {
|
|
23646
|
+
sections.push("\n## API Endpoints");
|
|
23647
|
+
for (const e of result.endpoints) {
|
|
23648
|
+
sections.push(`- \`${e.router ? e.router + "." : ""}${e.name}\` (${e.type}) at \`${e.filePath}:${e.line}\``);
|
|
23649
|
+
}
|
|
23650
|
+
}
|
|
23651
|
+
if (sections.length === 0) {
|
|
23652
|
+
return success(`No results found for "${query}". Try different keywords or run scan_codebase first.`);
|
|
23653
|
+
}
|
|
23654
|
+
return success(sections.join("\n"));
|
|
23317
23655
|
} catch (error2) {
|
|
23318
23656
|
return formatError2(error2);
|
|
23319
23657
|
}
|
|
23320
23658
|
}
|
|
23321
23659
|
);
|
|
23322
23660
|
server2.tool(
|
|
23323
|
-
"
|
|
23324
|
-
"
|
|
23661
|
+
"find_symbol",
|
|
23662
|
+
"Find a specific function, class, component, type, or other symbol by name. Returns exact file path and line number.",
|
|
23325
23663
|
{
|
|
23326
|
-
|
|
23327
|
-
|
|
23328
|
-
|
|
23329
|
-
|
|
23664
|
+
workspaceId: external_exports.string().uuid().optional().describe("Workspace ID"),
|
|
23665
|
+
workspaceName: external_exports.string().optional().describe("Workspace name (alternative to ID)"),
|
|
23666
|
+
name: external_exports.string().describe('Symbol name to search for \u2014 e.g. "TaskCreateInline", "authorizeWorkspaceAccess"'),
|
|
23667
|
+
kind: external_exports.string().optional().describe("Filter by kind: function, class, component, type, interface, enum, hook, struct, method")
|
|
23330
23668
|
},
|
|
23331
|
-
async ({
|
|
23669
|
+
async ({ workspaceId, workspaceName, name, kind }) => {
|
|
23332
23670
|
try {
|
|
23333
|
-
|
|
23334
|
-
|
|
23335
|
-
|
|
23336
|
-
|
|
23337
|
-
|
|
23338
|
-
|
|
23339
|
-
|
|
23340
|
-
|
|
23341
|
-
|
|
23342
|
-
var renderNode = renderNode2;
|
|
23343
|
-
const id = await resolveProjectId(projectId, projectName);
|
|
23344
|
-
const tree = await api.codeIntelligence.getImpactAnalysis({
|
|
23345
|
-
projectId: id,
|
|
23346
|
-
filePath,
|
|
23347
|
-
depth
|
|
23671
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23672
|
+
const results = await api.intelligence.findSymbol({ workspaceId: id, name, kind });
|
|
23673
|
+
if (results.length === 0) {
|
|
23674
|
+
return success(`No symbol found matching "${name}"${kind ? ` (kind: ${kind})` : ""}`);
|
|
23675
|
+
}
|
|
23676
|
+
const lines = results.map((s) => {
|
|
23677
|
+
const sig = s.signature ? `
|
|
23678
|
+
${s.signature}` : "";
|
|
23679
|
+
return `- \`${s.name}\` (${s.kind}) at \`${s.filePath}:${s.line}\`${s.exported ? " [exported]" : ""}${sig}`;
|
|
23348
23680
|
});
|
|
23349
|
-
|
|
23350
|
-
`
|
|
23351
|
-
|
|
23352
|
-
|
|
23353
|
-
|
|
23354
|
-
|
|
23355
|
-
|
|
23356
|
-
|
|
23357
|
-
|
|
23358
|
-
|
|
23681
|
+
return success(`Found ${results.length} match(es):
|
|
23682
|
+
${lines.join("\n")}`);
|
|
23683
|
+
} catch (error2) {
|
|
23684
|
+
return formatError2(error2);
|
|
23685
|
+
}
|
|
23686
|
+
}
|
|
23687
|
+
);
|
|
23688
|
+
server2.tool(
|
|
23689
|
+
"find_copy",
|
|
23690
|
+
"Find user-facing text/copy in the codebase by content. Returns the file path, line number, and surrounding component context. Use this to quickly locate where a specific string appears in the UI.",
|
|
23691
|
+
{
|
|
23692
|
+
workspaceId: external_exports.string().uuid().optional().describe("Workspace ID"),
|
|
23693
|
+
workspaceName: external_exports.string().optional().describe("Workspace name (alternative to ID)"),
|
|
23694
|
+
text: external_exports.string().describe('Text to search for \u2014 e.g. "Welcome to Chapter", "Sign in", "No tasks yet"')
|
|
23695
|
+
},
|
|
23696
|
+
async ({ workspaceId, workspaceName, text }) => {
|
|
23697
|
+
try {
|
|
23698
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23699
|
+
const results = await api.intelligence.findCopy({ workspaceId: id, text });
|
|
23700
|
+
if (results.length === 0) {
|
|
23701
|
+
return success(`No copy found matching "${text}"`);
|
|
23359
23702
|
}
|
|
23360
|
-
|
|
23703
|
+
const lines = results.map(
|
|
23704
|
+
(c) => `- "${c.text}" at \`${c.filePath}:${c.line}\` in \`${c.context}\` (${c.type})`
|
|
23705
|
+
);
|
|
23706
|
+
return success(`Found ${results.length} match(es):
|
|
23707
|
+
${lines.join("\n")}`);
|
|
23361
23708
|
} catch (error2) {
|
|
23362
23709
|
return formatError2(error2);
|
|
23363
23710
|
}
|
|
23364
23711
|
}
|
|
23365
23712
|
);
|
|
23366
23713
|
server2.tool(
|
|
23367
|
-
"
|
|
23368
|
-
"
|
|
23714
|
+
"get_codebase_context",
|
|
23715
|
+
"Generate a structured context block for a task description. Returns relevant files, symbols, copy, routes, and endpoints that an agent should know about before starting work. This is the key tool for speeding up agent sessions \u2014 call it at the start of any task.",
|
|
23369
23716
|
{
|
|
23370
|
-
|
|
23371
|
-
|
|
23372
|
-
|
|
23373
|
-
symbolType: external_exports.enum([
|
|
23374
|
-
"function",
|
|
23375
|
-
"class",
|
|
23376
|
-
"interface",
|
|
23377
|
-
"type",
|
|
23378
|
-
"variable",
|
|
23379
|
-
"method",
|
|
23380
|
-
"export"
|
|
23381
|
-
]).optional().describe("Filter by symbol type")
|
|
23717
|
+
workspaceId: external_exports.string().uuid().optional().describe("Workspace ID"),
|
|
23718
|
+
workspaceName: external_exports.string().optional().describe("Workspace name (alternative to ID)"),
|
|
23719
|
+
taskDescription: external_exports.string().describe('Description of the task \u2014 e.g. "change the welcome message on the home page"')
|
|
23382
23720
|
},
|
|
23383
|
-
async ({
|
|
23721
|
+
async ({ workspaceId, workspaceName, taskDescription }) => {
|
|
23384
23722
|
try {
|
|
23385
|
-
const id = await
|
|
23386
|
-
const
|
|
23387
|
-
|
|
23388
|
-
|
|
23389
|
-
symbolType: symbolType2
|
|
23723
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23724
|
+
const result = await api.intelligence.contextForTask({
|
|
23725
|
+
workspaceId: id,
|
|
23726
|
+
taskDescription
|
|
23390
23727
|
});
|
|
23391
|
-
|
|
23392
|
-
|
|
23393
|
-
|
|
23394
|
-
|
|
23395
|
-
|
|
23396
|
-
|
|
23397
|
-
|
|
23398
|
-
|
|
23399
|
-
|
|
23400
|
-
|
|
23401
|
-
|
|
23402
|
-
|
|
23403
|
-
|
|
23728
|
+
return success(result.context);
|
|
23729
|
+
} catch (error2) {
|
|
23730
|
+
return formatError2(error2);
|
|
23731
|
+
}
|
|
23732
|
+
}
|
|
23733
|
+
);
|
|
23734
|
+
server2.tool(
|
|
23735
|
+
"get_file_map",
|
|
23736
|
+
"Get a compact map of every file in the codebase with its category and key exports. Useful for understanding project structure at a glance.",
|
|
23737
|
+
{
|
|
23738
|
+
workspaceId: external_exports.string().uuid().optional().describe("Workspace ID"),
|
|
23739
|
+
workspaceName: external_exports.string().optional().describe("Workspace name (alternative to ID)")
|
|
23740
|
+
},
|
|
23741
|
+
async ({ workspaceId, workspaceName }) => {
|
|
23742
|
+
try {
|
|
23743
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23744
|
+
const result = await api.intelligence.fileMap({ workspaceId: id });
|
|
23745
|
+
const lines = [
|
|
23746
|
+
`# File Map (${Object.keys(result.tree).length} files)`,
|
|
23747
|
+
`Tech stack: ${result.techStack.frameworks.join(", ")} | ${result.techStack.languages.join(", ")}`,
|
|
23748
|
+
""
|
|
23749
|
+
];
|
|
23750
|
+
for (const [path3, desc] of Object.entries(result.tree)) {
|
|
23751
|
+
lines.push(`${path3} \u2014 ${desc}`);
|
|
23404
23752
|
}
|
|
23405
23753
|
return success(lines.join("\n"));
|
|
23406
23754
|
} catch (error2) {
|
|
@@ -23409,33 +23757,33 @@ Total: ${totalFiles} files, ${totalSymbols} symbols across ${buckets.length} dir
|
|
|
23409
23757
|
}
|
|
23410
23758
|
);
|
|
23411
23759
|
server2.tool(
|
|
23412
|
-
"
|
|
23413
|
-
"
|
|
23760
|
+
"get_file_deps",
|
|
23761
|
+
"Get the dependency graph for a specific file \u2014 what it imports (dependencies) or what imports it (dependents).",
|
|
23414
23762
|
{
|
|
23415
|
-
|
|
23416
|
-
|
|
23763
|
+
workspaceId: external_exports.string().uuid().optional().describe("Workspace ID"),
|
|
23764
|
+
workspaceName: external_exports.string().optional().describe("Workspace name (alternative to ID)"),
|
|
23765
|
+
filePath: external_exports.string().describe('Relative file path \u2014 e.g. "src/components/task-card.tsx"'),
|
|
23766
|
+
direction: external_exports.enum(["dependents", "dependencies"]).optional().describe('Direction: "dependents" (what imports this file) or "dependencies" (what this file imports). Default: dependents')
|
|
23417
23767
|
},
|
|
23418
|
-
async ({
|
|
23768
|
+
async ({ workspaceId, workspaceName, filePath, direction }) => {
|
|
23419
23769
|
try {
|
|
23420
|
-
const id = await
|
|
23421
|
-
const
|
|
23422
|
-
|
|
23423
|
-
|
|
23424
|
-
|
|
23425
|
-
|
|
23426
|
-
|
|
23427
|
-
|
|
23428
|
-
|
|
23429
|
-
],
|
|
23430
|
-
isError: true
|
|
23431
|
-
};
|
|
23770
|
+
const id = await resolveWorkspaceId(workspaceId, workspaceName);
|
|
23771
|
+
const result = await api.intelligence.deps({
|
|
23772
|
+
workspaceId: id,
|
|
23773
|
+
filePath,
|
|
23774
|
+
direction
|
|
23775
|
+
});
|
|
23776
|
+
const label = direction === "dependencies" ? "depends on" : "is imported by";
|
|
23777
|
+
if (result.files.length === 0) {
|
|
23778
|
+
return success(`\`${filePath}\` ${label} no other files`);
|
|
23432
23779
|
}
|
|
23433
|
-
const
|
|
23434
|
-
|
|
23435
|
-
|
|
23436
|
-
|
|
23437
|
-
|
|
23438
|
-
|
|
23780
|
+
const lines = [`\`${filePath}\` ${label}:`];
|
|
23781
|
+
for (const f of result.files) {
|
|
23782
|
+
lines.push(` - \`${f}\``);
|
|
23783
|
+
}
|
|
23784
|
+
return success(lines.join("\n"));
|
|
23785
|
+
} catch (error2) {
|
|
23786
|
+
return formatError2(error2);
|
|
23439
23787
|
}
|
|
23440
23788
|
}
|
|
23441
23789
|
);
|
|
@@ -23443,13 +23791,15 @@ Total: ${totalFiles} files, ${totalSymbols} symbols across ${buckets.length} dir
|
|
|
23443
23791
|
|
|
23444
23792
|
// src/tools/index.ts
|
|
23445
23793
|
function registerAllTools(server2) {
|
|
23446
|
-
|
|
23794
|
+
registerWorkspaceTools(server2);
|
|
23447
23795
|
registerFileTools(server2);
|
|
23448
23796
|
registerChangeRequestTools(server2);
|
|
23449
23797
|
registerCommitTools(server2);
|
|
23450
23798
|
registerSessionTools(server2);
|
|
23451
23799
|
registerSearchTools(server2);
|
|
23452
|
-
|
|
23800
|
+
registerTaskTools(server2);
|
|
23801
|
+
registerActivityTools(server2);
|
|
23802
|
+
registerIntelligenceTools(server2);
|
|
23453
23803
|
}
|
|
23454
23804
|
|
|
23455
23805
|
// src/index.ts
|