@schoolai/shipyard-mcp 0.2.2-next.485 → 0.2.2-next.489

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.
@@ -42603,10 +42603,9 @@ var PlanMetadataSchema = external_exports.discriminatedUnion("status", [
42603
42603
  var BaseArtifactSchema = external_exports.object({
42604
42604
  id: external_exports.string(),
42605
42605
  type: external_exports.enum([
42606
- "screenshot",
42607
- "video",
42608
- "test_results",
42609
- "diff"
42606
+ "html",
42607
+ "image",
42608
+ "video"
42610
42609
  ]),
42611
42610
  filename: external_exports.string(),
42612
42611
  description: external_exports.string().optional(),
@@ -43793,6 +43792,29 @@ var GitHubPRResponseSchema = external_exports.object({
43793
43792
  merged: external_exports.boolean(),
43794
43793
  head: external_exports.object({ ref: external_exports.string() })
43795
43794
  });
43795
+ var ROUTES = {
43796
+ REGISTRY_LIST: "/registry",
43797
+ REGISTRY_REGISTER: "/register",
43798
+ REGISTRY_UNREGISTER: "/unregister",
43799
+ PLAN_STATUS: (planId) => `/api/plan/${planId}/status`,
43800
+ PLAN_HAS_CONNECTIONS: (planId) => `/api/plan/${planId}/has-connections`,
43801
+ PLAN_TRANSCRIPT: (planId) => `/api/plan/${planId}/transcript`,
43802
+ PLAN_SUBSCRIBE: (planId) => `/api/plan/${planId}/subscribe`,
43803
+ PLAN_CHANGES: (planId) => `/api/plan/${planId}/changes`,
43804
+ PLAN_UNSUBSCRIBE: (planId) => `/api/plan/${planId}/unsubscribe`,
43805
+ PLAN_PR_DIFF: (planId, prNumber) => `/api/plans/${planId}/pr-diff/${prNumber}`,
43806
+ PLAN_PR_FILES: (planId, prNumber) => `/api/plans/${planId}/pr-files/${prNumber}`,
43807
+ HOOK_SESSION: "/api/hook/session",
43808
+ HOOK_CONTENT: (planId) => `/api/hook/plan/${planId}/content`,
43809
+ HOOK_REVIEW: (planId) => `/api/hook/plan/${planId}/review`,
43810
+ HOOK_SESSION_TOKEN: (planId) => `/api/hook/plan/${planId}/session-token`,
43811
+ HOOK_PRESENCE: (planId) => `/api/hook/plan/${planId}/presence`,
43812
+ CONVERSATION_IMPORT: "/api/conversation/import",
43813
+ WEB_TASK: (planId) => `/task/${planId}`
43814
+ };
43815
+ function createPlanWebUrl(baseUrl, planId) {
43816
+ return `${baseUrl.replace(/\/$/, "")}${ROUTES.WEB_TASK(planId)}`;
43817
+ }
43796
43818
  var InviteTokenSchema = external_exports.object({
43797
43819
  id: external_exports.string(),
43798
43820
  tokenHash: external_exports.string(),
@@ -45750,7 +45772,7 @@ async function handleUpdatedPlanReview(sessionId, planId, planContent, _originMe
45750
45772
  }
45751
45773
  const baseUrl = webConfig.SHIPYARD_WEB_URL;
45752
45774
  logger.info(
45753
- { planId, url: `${baseUrl}/plan/${planId}` },
45775
+ { planId, url: createPlanWebUrl(baseUrl, planId) },
45754
45776
  "Content synced, browser already open. Waiting for server approval..."
45755
45777
  );
45756
45778
  const decision = await waitForReviewDecision(planId, "");
@@ -45907,7 +45929,7 @@ Reviewer comment: ${decision.reviewComment}` : "";
45907
45929
  allow: false,
45908
45930
  message: `Plan is pending review.
45909
45931
 
45910
- Open: ${baseUrl}/plan/${planId}`,
45932
+ Open: ${createPlanWebUrl(baseUrl, planId)}`,
45911
45933
  planId
45912
45934
  };
45913
45935
  case "draft":
@@ -45915,7 +45937,7 @@ Open: ${baseUrl}/plan/${planId}`,
45915
45937
  allow: false,
45916
45938
  message: `Plan is still in draft.
45917
45939
 
45918
- Submit for review at: ${baseUrl}/plan/${planId}`,
45940
+ Submit for review at: ${createPlanWebUrl(baseUrl, planId)}`,
45919
45941
  planId
45920
45942
  };
45921
45943
  case "in_progress":
@@ -3,7 +3,7 @@ import {
3
3
  YDOC_KEYS,
4
4
  createInputRequest,
5
5
  logPlanEvent
6
- } from "./chunk-BBYZISY2.js";
6
+ } from "./chunk-CYHEHTLS.js";
7
7
  import {
8
8
  logger
9
9
  } from "./chunk-GSGLHRWX.js";
@@ -760,10 +760,9 @@ var PlanMetadataSchema = z.discriminatedUnion("status", [
760
760
  var BaseArtifactSchema = z.object({
761
761
  id: z.string(),
762
762
  type: z.enum([
763
- "screenshot",
764
- "video",
765
- "test_results",
766
- "diff"
763
+ "html",
764
+ "image",
765
+ "video"
767
766
  ]),
768
767
  filename: z.string(),
769
768
  description: z.string().optional(),
@@ -2274,6 +2273,29 @@ function asWebRTCPeerId(id) {
2274
2273
  function asGitHubUsername(username) {
2275
2274
  return username;
2276
2275
  }
2276
+ var ROUTES = {
2277
+ REGISTRY_LIST: "/registry",
2278
+ REGISTRY_REGISTER: "/register",
2279
+ REGISTRY_UNREGISTER: "/unregister",
2280
+ PLAN_STATUS: (planId) => `/api/plan/${planId}/status`,
2281
+ PLAN_HAS_CONNECTIONS: (planId) => `/api/plan/${planId}/has-connections`,
2282
+ PLAN_TRANSCRIPT: (planId) => `/api/plan/${planId}/transcript`,
2283
+ PLAN_SUBSCRIBE: (planId) => `/api/plan/${planId}/subscribe`,
2284
+ PLAN_CHANGES: (planId) => `/api/plan/${planId}/changes`,
2285
+ PLAN_UNSUBSCRIBE: (planId) => `/api/plan/${planId}/unsubscribe`,
2286
+ PLAN_PR_DIFF: (planId, prNumber) => `/api/plans/${planId}/pr-diff/${prNumber}`,
2287
+ PLAN_PR_FILES: (planId, prNumber) => `/api/plans/${planId}/pr-files/${prNumber}`,
2288
+ HOOK_SESSION: "/api/hook/session",
2289
+ HOOK_CONTENT: (planId) => `/api/hook/plan/${planId}/content`,
2290
+ HOOK_REVIEW: (planId) => `/api/hook/plan/${planId}/review`,
2291
+ HOOK_SESSION_TOKEN: (planId) => `/api/hook/plan/${planId}/session-token`,
2292
+ HOOK_PRESENCE: (planId) => `/api/hook/plan/${planId}/presence`,
2293
+ CONVERSATION_IMPORT: "/api/conversation/import",
2294
+ WEB_TASK: (planId) => `/task/${planId}`
2295
+ };
2296
+ function createPlanWebUrl(baseUrl, planId) {
2297
+ return `${baseUrl.replace(/\/$/, "")}${ROUTES.WEB_TASK(planId)}`;
2298
+ }
2277
2299
  var InviteTokenSchema = z3.object({
2278
2300
  id: z3.string(),
2279
2301
  tokenHash: z3.string(),
@@ -2307,7 +2329,7 @@ function parseInviteFromUrl(url) {
2307
2329
  }
2308
2330
  function buildInviteUrl(baseUrl, planId, tokenId, tokenValue) {
2309
2331
  const normalizedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
2310
- const url = new URL(`${normalizedBase}/task/${planId}`);
2332
+ const url = new URL(`${normalizedBase}${ROUTES.WEB_TASK(planId)}`);
2311
2333
  url.searchParams.set("invite", `${tokenId}:${tokenValue}`);
2312
2334
  return url.toString();
2313
2335
  }
@@ -2588,25 +2610,6 @@ function getAllEventViewedByForPlan(ydoc, planId) {
2588
2610
  }
2589
2611
  return result;
2590
2612
  }
2591
- var ROUTES = {
2592
- REGISTRY_LIST: "/registry",
2593
- REGISTRY_REGISTER: "/register",
2594
- REGISTRY_UNREGISTER: "/unregister",
2595
- PLAN_STATUS: (planId) => `/api/plan/${planId}/status`,
2596
- PLAN_HAS_CONNECTIONS: (planId) => `/api/plan/${planId}/has-connections`,
2597
- PLAN_TRANSCRIPT: (planId) => `/api/plan/${planId}/transcript`,
2598
- PLAN_SUBSCRIBE: (planId) => `/api/plan/${planId}/subscribe`,
2599
- PLAN_CHANGES: (planId) => `/api/plan/${planId}/changes`,
2600
- PLAN_UNSUBSCRIBE: (planId) => `/api/plan/${planId}/unsubscribe`,
2601
- PLAN_PR_DIFF: (planId, prNumber) => `/api/plans/${planId}/pr-diff/${prNumber}`,
2602
- PLAN_PR_FILES: (planId, prNumber) => `/api/plans/${planId}/pr-files/${prNumber}`,
2603
- HOOK_SESSION: "/api/hook/session",
2604
- HOOK_CONTENT: (planId) => `/api/hook/plan/${planId}/content`,
2605
- HOOK_REVIEW: (planId) => `/api/hook/plan/${planId}/review`,
2606
- HOOK_SESSION_TOKEN: (planId) => `/api/hook/plan/${planId}/session-token`,
2607
- HOOK_PRESENCE: (planId) => `/api/hook/plan/${planId}/presence`,
2608
- CONVERSATION_IMPORT: "/api/conversation/import"
2609
- };
2610
2613
  function formatThreadsForLLM(threads, options = {}) {
2611
2614
  const { includeResolved = false, selectedTextMaxLength = 100, resolveUser } = options;
2612
2615
  const unresolvedThreads = threads.filter((t$1) => !t$1.resolved);
@@ -2947,6 +2950,8 @@ export {
2947
2950
  asAwarenessClientId,
2948
2951
  asWebRTCPeerId,
2949
2952
  asGitHubUsername,
2953
+ ROUTES,
2954
+ createPlanWebUrl,
2950
2955
  InviteTokenSchema,
2951
2956
  InviteRedemptionSchema,
2952
2957
  parseInviteFromUrl,
@@ -2987,7 +2992,6 @@ export {
2987
2992
  clearEventViewedBy,
2988
2993
  isEventUnread,
2989
2994
  getAllEventViewedByForPlan,
2990
- ROUTES,
2991
2995
  formatThreadsForLLM,
2992
2996
  PlanIdSchema,
2993
2997
  PlanStatusResponseSchema,
@@ -109,6 +109,7 @@ import {
109
109
  createPlanSnapshot,
110
110
  createPlanUrl,
111
111
  createPlanUrlWithHistory,
112
+ createPlanWebUrl,
112
113
  createUserResolver,
113
114
  declineInputRequest,
114
115
  decodeChunkMessage,
@@ -205,7 +206,7 @@ import {
205
206
  updateLinkedPRStatus,
206
207
  updatePlanIndexViewedBy,
207
208
  validateA2AMessages
208
- } from "./chunk-BBYZISY2.js";
209
+ } from "./chunk-CYHEHTLS.js";
209
210
  import "./chunk-JSBRDJBE.js";
210
211
  export {
211
212
  A2ADataPartSchema,
@@ -318,6 +319,7 @@ export {
318
319
  createPlanSnapshot,
319
320
  createPlanUrl,
320
321
  createPlanUrlWithHistory,
322
+ createPlanWebUrl,
321
323
  createUserResolver,
322
324
  declineInputRequest,
323
325
  decodeChunkMessage,
@@ -20,7 +20,7 @@ import {
20
20
  } from "./chunk-EBNL5ZX7.js";
21
21
  import {
22
22
  InputRequestManager
23
- } from "./chunk-NH6GXGQO.js";
23
+ } from "./chunk-7ISZ4RKB.js";
24
24
  import {
25
25
  ArtifactSchema,
26
26
  DeliverableSchema,
@@ -46,6 +46,7 @@ import {
46
46
  createLinkedPR,
47
47
  createPlanSnapshot,
48
48
  createPlanUrlWithHistory,
49
+ createPlanWebUrl,
49
50
  createUserResolver,
50
51
  extractDeliverables,
51
52
  extractMentions,
@@ -71,7 +72,7 @@ import {
71
72
  setPlanMetadata,
72
73
  touchPlanIndexEntry,
73
74
  transitionPlanStatus
74
- } from "./chunk-BBYZISY2.js";
75
+ } from "./chunk-CYHEHTLS.js";
75
76
  import {
76
77
  loadEnv,
77
78
  logger
@@ -592,8 +593,7 @@ function extractTitleFromBlocks(blocks) {
592
593
  async function createSessionHandler(input, ctx) {
593
594
  const existingSession = getSessionState(input.sessionId);
594
595
  if (existingSession) {
595
- const webUrl2 = webConfig.SHIPYARD_WEB_URL;
596
- const url2 = `${webUrl2}/plan/${existingSession.planId}`;
596
+ const url2 = createPlanWebUrl(webConfig.SHIPYARD_WEB_URL, existingSession.planId);
597
597
  ctx.logger.info(
598
598
  { planId: existingSession.planId, sessionId: input.sessionId },
599
599
  "Returning existing session (idempotent)"
@@ -658,8 +658,7 @@ async function createSessionHandler(input, ctx) {
658
658
  ownerId,
659
659
  deleted: false
660
660
  });
661
- const webUrl = webConfig.SHIPYARD_WEB_URL;
662
- const url = `${webUrl}/plan/${planId}`;
661
+ const url = createPlanWebUrl(webConfig.SHIPYARD_WEB_URL, planId);
663
662
  ctx.logger.info({ url }, "Plan URL generated");
664
663
  setSessionState(input.sessionId, {
665
664
  lifecycle: "created",
@@ -838,8 +837,7 @@ async function setSessionTokenHandler(planId, sessionTokenHash, ctx) {
838
837
  setPlanMetadata(ydoc, {
839
838
  sessionTokenHash
840
839
  });
841
- const webUrl = webConfig.SHIPYARD_WEB_URL;
842
- const url = `${webUrl}/plan/${planId}`;
840
+ const url = createPlanWebUrl(webConfig.SHIPYARD_WEB_URL, planId);
843
841
  const session = getSessionStateByPlanId(planId);
844
842
  const sessionId = getSessionIdByPlanId(planId);
845
843
  if (session && sessionId) {
@@ -1019,7 +1017,7 @@ async function waitForApprovalHandler(planId, _reviewRequestIdParam, ctx) {
1019
1017
  setSessionState(sessionId, {
1020
1018
  lifecycle: "approved_awaiting_token",
1021
1019
  ...baseState,
1022
- url: `${webUrl}/plan/${baseState.planId}`,
1020
+ url: createPlanWebUrl(webUrl, baseState.planId),
1023
1021
  approvedAt: extraData.approvedAt,
1024
1022
  deliverables: extraData.deliverables,
1025
1023
  reviewComment,
@@ -1038,7 +1036,7 @@ async function waitForApprovalHandler(planId, _reviewRequestIdParam, ctx) {
1038
1036
  ...baseState,
1039
1037
  contentHash: syncedFields?.contentHash ?? "",
1040
1038
  sessionToken: syncedFields?.sessionToken ?? "",
1041
- url: syncedFields?.url ?? `${webUrl}/plan/${baseState.planId}`,
1039
+ url: syncedFields?.url ?? createPlanWebUrl(webUrl, baseState.planId),
1042
1040
  deliverables,
1043
1041
  reviewComment: reviewComment || "",
1044
1042
  reviewedBy,
@@ -1231,8 +1229,7 @@ async function getDeliverableContextHandler(planId, sessionToken, ctx) {
1231
1229
  });
1232
1230
  }
1233
1231
  const deliverables = getDeliverables(ydoc);
1234
- const webUrl = webConfig.SHIPYARD_WEB_URL;
1235
- const url = `${webUrl}/plan/${planId}`;
1232
+ const url = createPlanWebUrl(webConfig.SHIPYARD_WEB_URL, planId);
1236
1233
  let deliverablesSection = "";
1237
1234
  if (deliverables.length > 0) {
1238
1235
  deliverablesSection = `
@@ -2551,7 +2548,7 @@ var TOOL_NAMES = {
2551
2548
  var AddArtifactInputBase = z3.object({
2552
2549
  planId: z3.string().describe("The plan ID to add artifact to"),
2553
2550
  sessionToken: z3.string().describe("Session token from create_plan"),
2554
- type: z3.enum(["screenshot", "video", "test_results", "diff"]).describe("Artifact type"),
2551
+ type: z3.enum(["html", "image", "video"]).describe("Artifact type"),
2555
2552
  filename: z3.string().describe("Filename for the artifact"),
2556
2553
  description: z3.string().optional().describe("What this artifact proves (deliverable name)"),
2557
2554
  deliverableId: z3.string().optional().describe("ID of the deliverable this artifact fulfills")
@@ -2570,6 +2567,30 @@ var AddArtifactInput = z3.discriminatedUnion("source", [
2570
2567
  content: z3.string().describe("Base64 encoded file content")
2571
2568
  })
2572
2569
  ]);
2570
+ function validateArtifactType(type, filename) {
2571
+ const ext = filename.split(".").pop()?.toLowerCase();
2572
+ const validExtensions = {
2573
+ html: ["html", "htm"],
2574
+ image: ["png", "jpg", "jpeg", "gif", "webp", "svg"],
2575
+ video: ["mp4", "webm", "mov", "avi"]
2576
+ };
2577
+ const valid = validExtensions[type];
2578
+ if (!valid || !ext || !valid.includes(ext)) {
2579
+ const suggestions = {
2580
+ html: "HTML is the primary format for test results, terminal output, code reviews, and structured data. Use self-contained HTML with inline CSS and base64 images.",
2581
+ image: 'Images are for actual UI screenshots only. For terminal output or test results, use type: "html" instead.',
2582
+ video: 'Videos are for browser automation flows and complex interactions. For static content, use type: "image" or "html".'
2583
+ };
2584
+ throw new Error(
2585
+ `Invalid file extension for artifact type '${type}'.
2586
+
2587
+ Expected: ${valid?.join(", ") || "unknown"}
2588
+ Got: ${ext || "no extension"}
2589
+
2590
+ Tip: ${suggestions[type]}`
2591
+ );
2592
+ }
2593
+ }
2573
2594
  var addArtifactTool = {
2574
2595
  definition: {
2575
2596
  name: TOOL_NAMES.ADD_ARTIFACT,
@@ -2613,7 +2634,7 @@ ARTIFACT TYPES:
2613
2634
  sessionToken: { type: "string", description: "Session token from create_plan" },
2614
2635
  type: {
2615
2636
  type: "string",
2616
- enum: ["screenshot", "video", "test_results", "diff"],
2637
+ enum: ["html", "image", "video"],
2617
2638
  description: "Artifact type for rendering"
2618
2639
  },
2619
2640
  filename: {
@@ -2653,6 +2674,7 @@ ARTIFACT TYPES:
2653
2674
  handler: async (args) => {
2654
2675
  const input = AddArtifactInput.parse(args);
2655
2676
  const { planId, sessionToken, type, filename } = input;
2677
+ validateArtifactType(type, filename);
2656
2678
  const actorName = await getGitHubUsername();
2657
2679
  logger.info({ planId, type, filename }, "Adding artifact");
2658
2680
  let content;
@@ -3630,7 +3652,7 @@ Bad deliverables (not provable):
3630
3652
  deleted: false
3631
3653
  });
3632
3654
  logger.info({ planId }, "Plan index updated");
3633
- const url = `${webConfig.SHIPYARD_WEB_URL}/plan/${planId}`;
3655
+ const url = createPlanWebUrl(webConfig.SHIPYARD_WEB_URL, planId);
3634
3656
  await openPlanInBrowser(planId, url);
3635
3657
  const repoInfo = repo ? `Repo: ${repo}${!input.repo ? " (auto-detected)" : ""}` : "Repo: Not set (provide repo and prNumber for artifact uploads)";
3636
3658
  return {
@@ -4966,7 +4988,7 @@ Upload proof-of-work artifact.
4966
4988
  Parameters:
4967
4989
  - planId (string): The plan ID
4968
4990
  - sessionToken (string): Session token
4969
- - type (string): 'screenshot' | 'video' | 'test_results' | 'diff'
4991
+ - type (string): 'html' | 'image' | 'video'
4970
4992
  - filename (string): e.g., "screenshot.png"
4971
4993
  - source (string): Content source type - 'file' | 'url' | 'base64'
4972
4994
  - filePath (string): Local file path (required when source='file') - RECOMMENDED
@@ -5386,7 +5408,7 @@ async function setupReviewNotification(planId, pollIntervalSeconds) {
5386
5408
  return { script, fullResponse: text };
5387
5409
  }
5388
5410
  async function requestUserInput(opts) {
5389
- const { InputRequestManager: InputRequestManager2 } = await import("./input-request-manager-3STPPPYU.js");
5411
+ const { InputRequestManager: InputRequestManager2 } = await import("./input-request-manager-YOBSG7RN.js");
5390
5412
  const ydoc = await getOrCreateDoc3(PLAN_INDEX_DOC_NAME);
5391
5413
  const manager = new InputRequestManager2();
5392
5414
  const params = opts.type === "choice" ? {
@@ -5430,7 +5452,7 @@ async function requestUserInput(opts) {
5430
5452
  };
5431
5453
  }
5432
5454
  async function postActivityUpdate(opts) {
5433
- const { logPlanEvent: logPlanEvent2 } = await import("./dist-D4DG2R4R.js");
5455
+ const { logPlanEvent: logPlanEvent2 } = await import("./dist-HCVQNHBC.js");
5434
5456
  const { getGitHubUsername: getGitHubUsername2 } = await import("./server-identity-LSZ4CZRK.js");
5435
5457
  const { nanoid: nanoid8 } = await import("nanoid");
5436
5458
  const doc = await getOrCreateDoc3(opts.planId);
@@ -5453,7 +5475,7 @@ async function postActivityUpdate(opts) {
5453
5475
  return { success: true, eventId, requestId };
5454
5476
  }
5455
5477
  async function resolveActivityRequest(opts) {
5456
- const { logPlanEvent: logPlanEvent2, getPlanEvents } = await import("./dist-D4DG2R4R.js");
5478
+ const { logPlanEvent: logPlanEvent2, getPlanEvents } = await import("./dist-HCVQNHBC.js");
5457
5479
  const { getGitHubUsername: getGitHubUsername2 } = await import("./server-identity-LSZ4CZRK.js");
5458
5480
  const doc = await getOrCreateDoc3(opts.planId);
5459
5481
  const actorName = await getGitHubUsername2();
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  InputRequestManager
3
- } from "./chunk-NH6GXGQO.js";
4
- import "./chunk-BBYZISY2.js";
3
+ } from "./chunk-7ISZ4RKB.js";
4
+ import "./chunk-CYHEHTLS.js";
5
5
  import "./chunk-GSGLHRWX.js";
6
6
  import "./chunk-JSBRDJBE.js";
7
7
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schoolai/shipyard-mcp",
3
- "version": "0.2.2-next.485",
3
+ "version": "0.2.2-next.489",
4
4
  "description": "Shipyard MCP server and CLI tools for distributed planning with CRDTs",
5
5
  "type": "module",
6
6
  "bin": {