passalong 0.16.0 → 0.17.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/package.json +1 -1
- package/src/api.js +27 -3
- package/src/mcp.js +164 -14
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -301,10 +301,13 @@ export const join = (code) =>
|
|
|
301
301
|
call(`/v1/invites/${encodeURIComponent(code)}/accept`, { method: "POST" });
|
|
302
302
|
|
|
303
303
|
export const board = () => call("/v1/board");
|
|
304
|
-
export const folders = (scope = "all"
|
|
305
|
-
|
|
306
|
-
|
|
304
|
+
export const folders = (scope = "all", { query = "", guide = "" } = {}) =>
|
|
305
|
+
call(`/v1/folders${q({ scope, q: query, guide })}`);
|
|
306
|
+
export const createFolder = (title, description = "", team = "", parent = "", color = "") =>
|
|
307
|
+
call("/v1/folders", { method: "POST", body: { title, description, team, parent, color } });
|
|
307
308
|
export const folder = (id) => call(`/v1/folders/${encodeURIComponent(id)}`);
|
|
309
|
+
export const updateFolder = (id, changes) =>
|
|
310
|
+
call(`/v1/folders/${encodeURIComponent(id)}`, { method: "PATCH", body: changes });
|
|
308
311
|
export const folderDocument = (folderId, documentId) =>
|
|
309
312
|
call(`/v1/folders/${encodeURIComponent(folderId)}/documents/${encodeURIComponent(documentId)}`);
|
|
310
313
|
export const folderAsset = (folderId, assetId) =>
|
|
@@ -329,8 +332,29 @@ export const saveFolderDocument = (folderId, documentId, version, body) =>
|
|
|
329
332
|
method: "PUT",
|
|
330
333
|
body: { version, body },
|
|
331
334
|
});
|
|
335
|
+
export const renameFolderDocument = (folderId, documentId, name) =>
|
|
336
|
+
call(`/v1/folders/${encodeURIComponent(folderId)}/documents/${encodeURIComponent(documentId)}`, {
|
|
337
|
+
method: "PATCH",
|
|
338
|
+
body: { name },
|
|
339
|
+
});
|
|
340
|
+
export const folderDocumentHistory = (folderId, documentId, version = 0) =>
|
|
341
|
+
call(
|
|
342
|
+
`/v1/folders/${encodeURIComponent(folderId)}/documents/${encodeURIComponent(documentId)}/revisions${version ? `/${version}` : ""}`,
|
|
343
|
+
);
|
|
332
344
|
export const linkFolderGuide = (folderId, guide) =>
|
|
333
345
|
call(`/v1/folders/${encodeURIComponent(folderId)}/guides`, { method: "POST", body: { guide } });
|
|
346
|
+
export const unlinkFolderGuide = (folderId, guide) =>
|
|
347
|
+
call(`/v1/folders/${encodeURIComponent(folderId)}/guides/${encodeURIComponent(guide)}`, {
|
|
348
|
+
method: "DELETE",
|
|
349
|
+
});
|
|
350
|
+
export const deleteFolderDocument = (folderId, documentId) =>
|
|
351
|
+
call(`/v1/folders/${encodeURIComponent(folderId)}/documents/${encodeURIComponent(documentId)}`, {
|
|
352
|
+
method: "DELETE",
|
|
353
|
+
});
|
|
354
|
+
export const deleteFolderAsset = (folderId, assetId) =>
|
|
355
|
+
call(`/v1/folders/${encodeURIComponent(folderId)}/assets/${encodeURIComponent(assetId)}`, {
|
|
356
|
+
method: "DELETE",
|
|
357
|
+
});
|
|
334
358
|
/** What you did, newest first. `since` is a date prefix: 2026, 2026-09, 2026-09-11. */
|
|
335
359
|
export const log = ({ repo = "", since = "", limit = 0 } = {}) =>
|
|
336
360
|
call(`/v1/log${q({ repo, since, limit: limit || "" })}`);
|
package/src/mcp.js
CHANGED
|
@@ -219,6 +219,8 @@ const passOut = openObject({ id: z.string(), passed: z.boolean() });
|
|
|
219
219
|
|
|
220
220
|
const READS = { readOnlyHint: true, destructiveHint: false, openWorldHint: false };
|
|
221
221
|
const ADDS = { readOnlyHint: false, destructiveHint: false, openWorldHint: false };
|
|
222
|
+
// Removes something that cannot be brought back. Only a person's request should lead here.
|
|
223
|
+
const REMOVES = { readOnlyHint: false, destructiveHint: true, openWorldHint: false };
|
|
222
224
|
const fail = (err) => ({ content: [{ type: "text", text: err.message }], isError: true });
|
|
223
225
|
|
|
224
226
|
/**
|
|
@@ -347,6 +349,8 @@ export function buildServer() {
|
|
|
347
349
|
"FOLDERS ARE REUSABLE PROJECT CONTEXT, NOT GUIDES. When the user asks for a folder for a " +
|
|
348
350
|
"script, brief, assets or work spanning several guides, call list_folders first to avoid " +
|
|
349
351
|
"making a duplicate. If none fits, call create_folder with a title and short description. " +
|
|
352
|
+
"Use its parent id to nest a subfolder at any depth; keep a related project together. " +
|
|
353
|
+
"A folder's color can mark an area, and update_folder can recolor or move it later. " +
|
|
350
354
|
"A short request is enough: name it from the person's purpose and draft the first Markdown " +
|
|
351
355
|
"document with create_folder_document. Do not leave an empty folder or make them fill a form. " +
|
|
352
356
|
"Pass `team` only when the user wants it shared with that team; use the slug they named " +
|
|
@@ -355,8 +359,16 @@ export function buildServer() {
|
|
|
355
359
|
"get_folder_document to read text and its version, and save_folder_document with that " +
|
|
356
360
|
"version when editing. On a stale-version refusal, reread before retrying so another " +
|
|
357
361
|
"person's work is not lost. Use add_folder_asset for a screenshot or file available on " +
|
|
358
|
-
"this machine, get_folder_asset to read one already there
|
|
359
|
-
"
|
|
362
|
+
"this machine, and get_folder_asset to read one already there. " +
|
|
363
|
+
"To put a guide in a folder — a task, bug or handoff, new or already published — publish it " +
|
|
364
|
+
"as usual (for a team folder, to that folder's team, or its members cannot see it), then call " +
|
|
365
|
+
"link_folder_guide with the folder id and the guide id. Linking leaves the guide unchanged; " +
|
|
366
|
+
"unlink_folder_guide takes it out again. Delete a document or file with " +
|
|
367
|
+
"delete_folder_document or delete_folder_asset only when the person asked for it to go: " +
|
|
368
|
+
"neither can be undone. No tool deletes a whole folder; a person does that in the hub. " +
|
|
369
|
+
"list_folders with query finds folders and documents by their words, and with guide the " +
|
|
370
|
+
"folders a guide is in. rename_folder_document renames a document; " +
|
|
371
|
+
"get_folder_document_history reads earlier versions, so a bad edit can be put back.\n" +
|
|
360
372
|
"A FOLLOW-UP IS MORE CONTEXT FOR A GUIDE, WRITTEN AS ITS OWN GUIDE. When a guide needs " +
|
|
361
373
|
"more context — a missing detail, a step that needed explaining, what changed since, what " +
|
|
362
374
|
"you found doing it — publish that context with publish_guide `parent` set to the guide's " +
|
|
@@ -526,12 +538,16 @@ export function buildServer() {
|
|
|
526
538
|
{
|
|
527
539
|
title: "List project folders",
|
|
528
540
|
annotations: READS,
|
|
529
|
-
description:
|
|
530
|
-
inputSchema: {
|
|
541
|
+
description: `Find project folders of documents, assets and guides. Pass query to find the folders whose name, description, or any document's name or text mention those words; each then names its matching documents. Pass guide to find the folders a guide is in. Requires sync sign-in.`,
|
|
542
|
+
inputSchema: {
|
|
543
|
+
scope: z.string().optional().describe('"all", "mine", or a team slug'),
|
|
544
|
+
query: z.string().optional().describe("Words to look for in folders and their documents"),
|
|
545
|
+
guide: z.string().optional().describe("A guide id: only the folders it is linked in"),
|
|
546
|
+
},
|
|
531
547
|
},
|
|
532
|
-
async ({ scope }) => {
|
|
548
|
+
async ({ scope, query, guide }) => {
|
|
533
549
|
try {
|
|
534
|
-
return json(await api.folders(scope || "all"));
|
|
550
|
+
return json(await api.folders(scope || "all", { query, guide }));
|
|
535
551
|
} catch (err) {
|
|
536
552
|
return fail(err);
|
|
537
553
|
}
|
|
@@ -543,16 +559,57 @@ export function buildServer() {
|
|
|
543
559
|
title: "Create project folder",
|
|
544
560
|
annotations: ADDS,
|
|
545
561
|
description:
|
|
546
|
-
"Create a folder
|
|
562
|
+
"Create a folder or nested subfolder after checking list_folders. Pass parent to nest it; omit team for a private root, and pass a team slug only when sharing was requested. A subfolder inherits its parent's space.",
|
|
547
563
|
inputSchema: {
|
|
548
564
|
title: z.string().describe("Name of the project or body of work"),
|
|
549
565
|
description: z.string().optional().describe("Short purpose of the folder"),
|
|
550
566
|
team: z.string().optional().describe("Team slug; omit for a private folder"),
|
|
567
|
+
parent: z
|
|
568
|
+
.string()
|
|
569
|
+
.optional()
|
|
570
|
+
.describe("Parent folder id for a nested subfolder; omit for a top-level folder"),
|
|
571
|
+
color: z
|
|
572
|
+
.enum(["coral", "amber", "green", "blue", "violet"])
|
|
573
|
+
.optional()
|
|
574
|
+
.describe("Optional grouping color"),
|
|
575
|
+
},
|
|
576
|
+
},
|
|
577
|
+
async ({ title, description, team, parent, color }) => {
|
|
578
|
+
try {
|
|
579
|
+
return json(
|
|
580
|
+
await api.createFolder(title, description || "", team || "", parent || "", color || ""),
|
|
581
|
+
);
|
|
582
|
+
} catch (err) {
|
|
583
|
+
return fail(err);
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
);
|
|
587
|
+
server.registerTool(
|
|
588
|
+
"update_folder",
|
|
589
|
+
{
|
|
590
|
+
title: "Organize project folder",
|
|
591
|
+
annotations: ADDS,
|
|
592
|
+
description:
|
|
593
|
+
"Rename, describe, recolor or move a folder. Pass parent as an empty string to move it to the top level. A folder cannot move inside itself or a descendant, or into another team/private space.",
|
|
594
|
+
inputSchema: {
|
|
595
|
+
folder: z.string(),
|
|
596
|
+
title: z.string().optional(),
|
|
597
|
+
description: z.string().optional(),
|
|
598
|
+
parent: z
|
|
599
|
+
.string()
|
|
600
|
+
.optional()
|
|
601
|
+
.describe("New parent folder id; empty string means top level"),
|
|
602
|
+
color: z.enum(["", "coral", "amber", "green", "blue", "violet"]).optional(),
|
|
551
603
|
},
|
|
552
604
|
},
|
|
553
|
-
async ({ title, description,
|
|
605
|
+
async ({ folder, title, description, parent, color }) => {
|
|
554
606
|
try {
|
|
555
|
-
|
|
607
|
+
const changes = Object.fromEntries(
|
|
608
|
+
Object.entries({ title, description, parent, color }).filter(
|
|
609
|
+
([, value]) => value !== undefined,
|
|
610
|
+
),
|
|
611
|
+
);
|
|
612
|
+
return json(await api.updateFolder(folder, changes));
|
|
556
613
|
} catch (err) {
|
|
557
614
|
return fail(err);
|
|
558
615
|
}
|
|
@@ -678,13 +735,51 @@ export function buildServer() {
|
|
|
678
735
|
}
|
|
679
736
|
},
|
|
680
737
|
);
|
|
738
|
+
server.registerTool(
|
|
739
|
+
"rename_folder_document",
|
|
740
|
+
{
|
|
741
|
+
title: "Rename folder document",
|
|
742
|
+
annotations: ADDS,
|
|
743
|
+
description:
|
|
744
|
+
"Rename a folder document. Its id, text and history stay; no version is needed, because a rename cannot overwrite anyone's text.",
|
|
745
|
+
inputSchema: { folder: z.string(), document: z.string(), name: z.string() },
|
|
746
|
+
},
|
|
747
|
+
async ({ folder, document, name }) => {
|
|
748
|
+
try {
|
|
749
|
+
return json(await api.renameFolderDocument(folder, document, name));
|
|
750
|
+
} catch (err) {
|
|
751
|
+
return fail(err);
|
|
752
|
+
}
|
|
753
|
+
},
|
|
754
|
+
);
|
|
755
|
+
server.registerTool(
|
|
756
|
+
"get_folder_document_history",
|
|
757
|
+
{
|
|
758
|
+
title: "Read folder document history",
|
|
759
|
+
annotations: READS,
|
|
760
|
+
description:
|
|
761
|
+
"Read a folder document's earlier versions. Without version, lists them newest first; with version, returns that version's text. To bring one back, save it with save_folder_document over the current version.",
|
|
762
|
+
inputSchema: {
|
|
763
|
+
folder: z.string(),
|
|
764
|
+
document: z.string(),
|
|
765
|
+
version: z.number().int().positive().optional(),
|
|
766
|
+
},
|
|
767
|
+
},
|
|
768
|
+
async ({ folder, document, version }) => {
|
|
769
|
+
try {
|
|
770
|
+
return json(await api.folderDocumentHistory(folder, document, version || 0));
|
|
771
|
+
} catch (err) {
|
|
772
|
+
return fail(err);
|
|
773
|
+
}
|
|
774
|
+
},
|
|
775
|
+
);
|
|
681
776
|
server.registerTool(
|
|
682
777
|
"link_folder_guide",
|
|
683
778
|
{
|
|
684
779
|
title: "Link guide to folder",
|
|
685
780
|
annotations: ADDS,
|
|
686
781
|
description:
|
|
687
|
-
"Put
|
|
782
|
+
"Put a task, bug or handoff guide in the folder, beside the documents and assets it uses: one just published or one that already existed.",
|
|
688
783
|
inputSchema: { folder: z.string(), guide: z.string() },
|
|
689
784
|
},
|
|
690
785
|
async ({ folder, guide }) => {
|
|
@@ -696,6 +791,60 @@ export function buildServer() {
|
|
|
696
791
|
},
|
|
697
792
|
);
|
|
698
793
|
|
|
794
|
+
server.registerTool(
|
|
795
|
+
"unlink_folder_guide",
|
|
796
|
+
{
|
|
797
|
+
title: "Unlink guide from folder",
|
|
798
|
+
annotations: ADDS,
|
|
799
|
+
description:
|
|
800
|
+
"Take a guide out of a folder. Only the link goes: the guide itself is unchanged, and link_folder_guide puts it back.",
|
|
801
|
+
inputSchema: { folder: z.string(), guide: z.string() },
|
|
802
|
+
},
|
|
803
|
+
async ({ folder, guide }) => {
|
|
804
|
+
try {
|
|
805
|
+
return json(await api.unlinkFolderGuide(folder, guide));
|
|
806
|
+
} catch (err) {
|
|
807
|
+
return fail(err);
|
|
808
|
+
}
|
|
809
|
+
},
|
|
810
|
+
);
|
|
811
|
+
|
|
812
|
+
server.registerTool(
|
|
813
|
+
"delete_folder_document",
|
|
814
|
+
{
|
|
815
|
+
title: "Delete folder document",
|
|
816
|
+
annotations: REMOVES,
|
|
817
|
+
description:
|
|
818
|
+
"Delete one document from a folder, with its earlier versions. It cannot be brought back, so do it only when the person asked for that document to go; to change what it says, use save_folder_document instead.",
|
|
819
|
+
inputSchema: { folder: z.string(), document: z.string() },
|
|
820
|
+
},
|
|
821
|
+
async ({ folder, document }) => {
|
|
822
|
+
try {
|
|
823
|
+
return json(await api.deleteFolderDocument(folder, document));
|
|
824
|
+
} catch (err) {
|
|
825
|
+
return fail(err);
|
|
826
|
+
}
|
|
827
|
+
},
|
|
828
|
+
);
|
|
829
|
+
|
|
830
|
+
server.registerTool(
|
|
831
|
+
"delete_folder_asset",
|
|
832
|
+
{
|
|
833
|
+
title: "Delete folder file",
|
|
834
|
+
annotations: REMOVES,
|
|
835
|
+
description:
|
|
836
|
+
"Delete one file from a folder. It cannot be brought back, so do it only when the person asked for that file to go.",
|
|
837
|
+
inputSchema: { folder: z.string(), asset: z.string() },
|
|
838
|
+
},
|
|
839
|
+
async ({ folder, asset }) => {
|
|
840
|
+
try {
|
|
841
|
+
return json(await api.deleteFolderAsset(folder, asset));
|
|
842
|
+
} catch (err) {
|
|
843
|
+
return fail(err);
|
|
844
|
+
}
|
|
845
|
+
},
|
|
846
|
+
);
|
|
847
|
+
|
|
699
848
|
server.registerTool(
|
|
700
849
|
"search_guides",
|
|
701
850
|
{
|
|
@@ -769,8 +918,8 @@ export function buildServer() {
|
|
|
769
918
|
"delivery of, and every verdict and ack they gave. Each item has a rendered `text` line " +
|
|
770
919
|
"and the guide's repo. Use it to answer 'what have I been working on', to write a " +
|
|
771
920
|
"standup or a weekly summary, or to find work from a repo by when it happened rather " +
|
|
772
|
-
"than by what it was called.
|
|
773
|
-
"people did. IMPORTANT: it records what was passed along, not what was worked on — work " +
|
|
921
|
+
"than by what it was called. The hub calls this page Activities, so 'my activities' means " +
|
|
922
|
+
"this tool. It is the opposite of `activity`, which is what other people did. IMPORTANT: it records what was passed along, not what was worked on — work " +
|
|
774
923
|
"that never became a guide has no entry, so never present it as a complete record of " +
|
|
775
924
|
"this user's work, and never infer that a quiet period was an idle one.",
|
|
776
925
|
inputSchema: {
|
|
@@ -797,8 +946,9 @@ export function buildServer() {
|
|
|
797
946
|
annotations: READS,
|
|
798
947
|
description:
|
|
799
948
|
"What has happened to this user's guides and handoffs: who pulled one, who archived one, " +
|
|
800
|
-
"who was handed what, who joined a team. Each item has a ready-made `text` line. This
|
|
801
|
-
"
|
|
949
|
+
"who was handed what, who joined a team. Each item has a ready-made `text` line. This is " +
|
|
950
|
+
"the hub's notifications, not its Activities page — for the user's own acts ('my " +
|
|
951
|
+
"activities'), use log. This only reads; clear_activity is what marks the feed seen.",
|
|
802
952
|
inputSchema: {
|
|
803
953
|
all: z.boolean().default(false).describe("include what the user has already seen"),
|
|
804
954
|
},
|