@schoolai/shipyard-mcp 0.2.2-next.494 → 0.2.2-next.499

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.
@@ -28830,7 +28830,7 @@ init_cjs_shims();
28830
28830
  // ../../packages/schema/dist/index.mjs
28831
28831
  init_cjs_shims();
28832
28832
 
28833
- // ../../packages/schema/dist/yjs-helpers-A0hIPiRs.mjs
28833
+ // ../../packages/schema/dist/yjs-helpers-BQ7VTqc2.mjs
28834
28834
  init_cjs_shims();
28835
28835
 
28836
28836
  // ../../packages/schema/dist/plan.mjs
@@ -43001,7 +43001,7 @@ var PRReviewCommentSchema = external_exports.object({
43001
43001
  resolved: external_exports.boolean().optional()
43002
43002
  });
43003
43003
 
43004
- // ../../packages/schema/dist/yjs-helpers-A0hIPiRs.mjs
43004
+ // ../../packages/schema/dist/yjs-helpers-BQ7VTqc2.mjs
43005
43005
  function assertNever2(value) {
43006
43006
  throw new Error(`Unhandled discriminated union member: ${JSON.stringify(value)}`);
43007
43007
  }
@@ -43106,7 +43106,7 @@ var InputRequestBaseSchema = external_exports.object({
43106
43106
  message: external_exports.string().min(1, "Message cannot be empty"),
43107
43107
  status: external_exports.enum(InputRequestStatusValues),
43108
43108
  defaultValue: external_exports.string().optional(),
43109
- timeout: external_exports.number().int().min(10, "Timeout must be at least 10 seconds").max(14400, "Timeout cannot exceed 4 hours").optional(),
43109
+ timeout: external_exports.number().int().min(300, "Timeout must be at least 5 minutes (300 seconds)").max(1800, "Timeout cannot exceed 30 minutes (1800 seconds)").optional(),
43110
43110
  planId: external_exports.string().optional(),
43111
43111
  response: external_exports.unknown().optional(),
43112
43112
  answeredAt: external_exports.number().optional(),
@@ -44411,9 +44411,29 @@ var planRouter = router({
44411
44411
  if (!cwd) return {
44412
44412
  available: false,
44413
44413
  reason: "no_cwd",
44414
- message: "Plan has no associated working directory. Only Claude Code plans support local changes."
44414
+ message: "Plan has no associated working directory. Local changes are only available for plans created with working directory metadata."
44415
44415
  };
44416
44416
  return ctx.getLocalChanges(cwd);
44417
+ }),
44418
+ getFileContent: publicProcedure.input(external_exports.object({
44419
+ planId: PlanIdSchema.shape.planId,
44420
+ filePath: external_exports.string()
44421
+ })).output(external_exports.object({
44422
+ content: external_exports.string().nullable(),
44423
+ error: external_exports.string().optional()
44424
+ })).query(async ({ input, ctx }) => {
44425
+ const metadata = getPlanMetadata(await ctx.getOrCreateDoc(input.planId));
44426
+ if (!metadata) throw new TRPCError({
44427
+ code: "NOT_FOUND",
44428
+ message: "Plan not found"
44429
+ });
44430
+ const origin = metadata.origin;
44431
+ const cwd = origin?.platform === "claude-code" ? origin.cwd : void 0;
44432
+ if (!cwd) return {
44433
+ content: null,
44434
+ error: "No working directory available"
44435
+ };
44436
+ return ctx.getFileContent(cwd, input.filePath);
44417
44437
  })
44418
44438
  });
44419
44439
  var subscriptionRouter = router({
@@ -3,7 +3,7 @@ import {
3
3
  YDOC_KEYS,
4
4
  createInputRequest,
5
5
  logPlanEvent
6
- } from "./chunk-5V7HUMDU.js";
6
+ } from "./chunk-X4N6BYDY.js";
7
7
  import {
8
8
  logger
9
9
  } from "./chunk-GSGLHRWX.js";
@@ -864,7 +864,7 @@ function createHandedOffConversationVersion(params) {
864
864
  return ConversationVersionSchema.parse(version);
865
865
  }
866
866
 
867
- // ../../packages/schema/dist/yjs-helpers-A0hIPiRs.mjs
867
+ // ../../packages/schema/dist/yjs-helpers-BQ7VTqc2.mjs
868
868
  import { z as z2 } from "zod";
869
869
  import { nanoid as nanoid2 } from "nanoid";
870
870
  import * as Y from "yjs";
@@ -979,7 +979,7 @@ var InputRequestBaseSchema = z2.object({
979
979
  message: z2.string().min(1, "Message cannot be empty"),
980
980
  status: z2.enum(InputRequestStatusValues),
981
981
  defaultValue: z2.string().optional(),
982
- timeout: z2.number().int().min(10, "Timeout must be at least 10 seconds").max(14400, "Timeout cannot exceed 4 hours").optional(),
982
+ timeout: z2.number().int().min(300, "Timeout must be at least 5 minutes (300 seconds)").max(1800, "Timeout cannot exceed 30 minutes (1800 seconds)").optional(),
983
983
  planId: z2.string().optional(),
984
984
  response: z2.unknown().optional(),
985
985
  answeredAt: z2.number().optional(),
@@ -2811,9 +2811,29 @@ var planRouter = router({
2811
2811
  if (!cwd) return {
2812
2812
  available: false,
2813
2813
  reason: "no_cwd",
2814
- message: "Plan has no associated working directory. Only Claude Code plans support local changes."
2814
+ message: "Plan has no associated working directory. Local changes are only available for plans created with working directory metadata."
2815
2815
  };
2816
2816
  return ctx.getLocalChanges(cwd);
2817
+ }),
2818
+ getFileContent: publicProcedure.input(z3.object({
2819
+ planId: PlanIdSchema.shape.planId,
2820
+ filePath: z3.string()
2821
+ })).output(z3.object({
2822
+ content: z3.string().nullable(),
2823
+ error: z3.string().optional()
2824
+ })).query(async ({ input, ctx }) => {
2825
+ const metadata = getPlanMetadata(await ctx.getOrCreateDoc(input.planId));
2826
+ if (!metadata) throw new TRPCError({
2827
+ code: "NOT_FOUND",
2828
+ message: "Plan not found"
2829
+ });
2830
+ const origin = metadata.origin;
2831
+ const cwd = origin?.platform === "claude-code" ? origin.cwd : void 0;
2832
+ if (!cwd) return {
2833
+ content: null,
2834
+ error: "No working directory available"
2835
+ };
2836
+ return ctx.getFileContent(cwd, input.filePath);
2817
2837
  })
2818
2838
  });
2819
2839
  var subscriptionRouter = router({
@@ -212,7 +212,7 @@ import {
212
212
  updateLinkedPRStatus,
213
213
  updatePlanIndexViewedBy,
214
214
  validateA2AMessages
215
- } from "./chunk-5V7HUMDU.js";
215
+ } from "./chunk-X4N6BYDY.js";
216
216
  import "./chunk-JSBRDJBE.js";
217
217
  export {
218
218
  A2ADataPartSchema,
@@ -20,7 +20,7 @@ import {
20
20
  } from "./chunk-EBNL5ZX7.js";
21
21
  import {
22
22
  InputRequestManager
23
- } from "./chunk-ZD2SHV5V.js";
23
+ } from "./chunk-CSSWBPUN.js";
24
24
  import {
25
25
  ArtifactSchema,
26
26
  DeliverableSchema,
@@ -72,7 +72,7 @@ import {
72
72
  setPlanMetadata,
73
73
  touchPlanIndexEntry,
74
74
  transitionPlanStatus
75
- } from "./chunk-5V7HUMDU.js";
75
+ } from "./chunk-X4N6BYDY.js";
76
76
  import {
77
77
  loadEnv,
78
78
  logger
@@ -165,11 +165,11 @@ async function hasActiveConnections(planId) {
165
165
  }
166
166
 
167
167
  // src/registry-server.ts
168
- import { mkdirSync, readFileSync, unlinkSync } from "fs";
168
+ import { mkdirSync, readFileSync as readFileSync2, unlinkSync } from "fs";
169
169
  import { readFile, unlink, writeFile as writeFile2 } from "fs/promises";
170
170
  import http from "http";
171
171
  import { homedir as homedir3 } from "os";
172
- import { join as join3, resolve, sep } from "path";
172
+ import { join as join4, resolve, sep } from "path";
173
173
  import { createExpressMiddleware } from "@trpc/server/adapters/express";
174
174
  import express from "express";
175
175
  import * as decoding from "lib0/decoding";
@@ -385,6 +385,8 @@ function attachCRDTValidation(planId, doc) {
385
385
 
386
386
  // src/git-local-changes.ts
387
387
  import { execSync } from "child_process";
388
+ import { readFileSync } from "fs";
389
+ import { isAbsolute, join as join3, normalize } from "path";
388
390
  function getLocalChanges(cwd) {
389
391
  try {
390
392
  try {
@@ -395,11 +397,24 @@ function getLocalChanges(cwd) {
395
397
  stdio: ["pipe", "pipe", "pipe"]
396
398
  });
397
399
  } catch {
398
- return {
399
- available: false,
400
- reason: "not_git_repo",
401
- message: `Directory is not a git repository: ${cwd}`
402
- };
400
+ logger.info({ cwd }, "Not a git repo, initializing with git init");
401
+ try {
402
+ execSync("git init", {
403
+ cwd,
404
+ encoding: "utf-8",
405
+ timeout: 5e3,
406
+ stdio: ["pipe", "pipe", "pipe"]
407
+ });
408
+ logger.info({ cwd }, "Git repository initialized");
409
+ } catch (initError) {
410
+ const message = initError instanceof Error ? initError.message : "Unknown error";
411
+ logger.error({ error: initError, cwd }, "Failed to initialize git repository");
412
+ return {
413
+ available: false,
414
+ reason: "git_error",
415
+ message: `Failed to initialize git repository: ${message}`
416
+ };
417
+ }
403
418
  }
404
419
  let branch;
405
420
  try {
@@ -623,6 +638,32 @@ function detectStatus(fileDiff) {
623
638
  }
624
639
  return "modified";
625
640
  }
641
+ function getFileContent(cwd, filePath) {
642
+ try {
643
+ const normalizedPath = normalize(filePath);
644
+ if (isAbsolute(normalizedPath) || normalizedPath.startsWith("..")) {
645
+ return { content: null, error: "Invalid file path" };
646
+ }
647
+ const fullPath = join3(cwd, normalizedPath);
648
+ if (!fullPath.startsWith(cwd)) {
649
+ return { content: null, error: "Invalid file path" };
650
+ }
651
+ const content = readFileSync(fullPath, { encoding: "utf-8" });
652
+ if (content.length > 10 * 1024 * 1024) {
653
+ return { content: null, error: "File too large to display" };
654
+ }
655
+ return { content };
656
+ } catch (error) {
657
+ const message = error instanceof Error ? error.message : "Unknown error";
658
+ if (message.includes("ENOENT")) {
659
+ return { content: null, error: "File not found" };
660
+ }
661
+ if (message.includes("EISDIR")) {
662
+ return { content: null, error: "Path is a directory" };
663
+ }
664
+ return { content: null, error: `Failed to read file: ${message}` };
665
+ }
666
+ }
626
667
 
627
668
  // src/github-artifacts.ts
628
669
  import { Octokit } from "@octokit/rest";
@@ -1929,9 +1970,9 @@ function getParam(value) {
1929
1970
  if (Array.isArray(value)) return value[0];
1930
1971
  return value;
1931
1972
  }
1932
- var PERSISTENCE_DIR = join3(homedir3(), ".shipyard", "plans");
1933
- var HUB_LOCK_FILE = join3(homedir3(), ".shipyard", "hub.lock");
1934
- var SHIPYARD_DIR = join3(homedir3(), ".shipyard");
1973
+ var PERSISTENCE_DIR = join4(homedir3(), ".shipyard", "plans");
1974
+ var HUB_LOCK_FILE = join4(homedir3(), ".shipyard", "hub.lock");
1975
+ var SHIPYARD_DIR = join4(homedir3(), ".shipyard");
1935
1976
  var MAX_LOCK_RETRIES = 3;
1936
1977
  var messageSync = 0;
1937
1978
  var messageAwareness = 1;
@@ -2029,9 +2070,9 @@ function isProcessAlive(pid) {
2029
2070
  }
2030
2071
  }
2031
2072
  function tryRecoverStaleLock(originalError) {
2032
- const lockFile = join3(PERSISTENCE_DIR, "LOCK");
2073
+ const lockFile = join4(PERSISTENCE_DIR, "LOCK");
2033
2074
  try {
2034
- const hubLockContent = readFileSync(HUB_LOCK_FILE, "utf-8");
2075
+ const hubLockContent = readFileSync2(HUB_LOCK_FILE, "utf-8");
2035
2076
  const pidStr = hubLockContent.split("\n")[0] ?? "";
2036
2077
  const pid = Number.parseInt(pidStr, 10);
2037
2078
  if (isProcessAlive(pid)) {
@@ -2365,7 +2406,8 @@ function createContext() {
2365
2406
  logger,
2366
2407
  hookHandlers: createHookHandlers(),
2367
2408
  conversationHandlers: createConversationHandlers(),
2368
- getLocalChanges
2409
+ getLocalChanges,
2410
+ getFileContent
2369
2411
  };
2370
2412
  }
2371
2413
  function createApp() {
@@ -2408,7 +2450,7 @@ function createApp() {
2408
2450
  res.status(400).json({ error: "Missing planId or filename" });
2409
2451
  return;
2410
2452
  }
2411
- const ARTIFACTS_DIR2 = join3(homedir3(), ".shipyard", "artifacts");
2453
+ const ARTIFACTS_DIR2 = join4(homedir3(), ".shipyard", "artifacts");
2412
2454
  const fullPath = resolve(ARTIFACTS_DIR2, planId, filename);
2413
2455
  if (!fullPath.startsWith(ARTIFACTS_DIR2 + sep)) {
2414
2456
  res.status(400).json({ error: "Invalid artifact path" });
@@ -2725,11 +2767,11 @@ import { z as z3 } from "zod";
2725
2767
  // src/local-artifacts.ts
2726
2768
  import { mkdir as mkdir2, readFile as readFile2, rm, writeFile as writeFile3 } from "fs/promises";
2727
2769
  import { homedir as homedir4 } from "os";
2728
- import { join as join4, resolve as resolve2, sep as sep2 } from "path";
2770
+ import { join as join5, resolve as resolve2, sep as sep2 } from "path";
2729
2771
  async function storeLocalArtifact(planId, filename, buffer) {
2730
- const planDir = join4(ARTIFACTS_DIR, planId);
2772
+ const planDir = join5(ARTIFACTS_DIR, planId);
2731
2773
  await mkdir2(planDir, { recursive: true });
2732
- const filepath = join4(planDir, filename);
2774
+ const filepath = join5(planDir, filename);
2733
2775
  await writeFile3(filepath, buffer);
2734
2776
  logger.info({ planId, filename, size: buffer.length }, "Artifact stored locally");
2735
2777
  return `${planId}/${filename}`;
@@ -2752,7 +2794,7 @@ async function deleteLocalArtifact(artifactId) {
2752
2794
  return false;
2753
2795
  }
2754
2796
  }
2755
- var ARTIFACTS_DIR = join4(homedir4(), ".shipyard", "artifacts");
2797
+ var ARTIFACTS_DIR = join5(homedir4(), ".shipyard", "artifacts");
2756
2798
 
2757
2799
  // src/session-token.ts
2758
2800
  import { timingSafeEqual } from "crypto";
@@ -3177,11 +3219,11 @@ Linked to deliverable: ${input.deliverableId}` : "";
3177
3219
  }
3178
3220
  logger.info({ planId, snapshotUrl }, "Task auto-completed");
3179
3221
  const { homedir: homedir5 } = await import("os");
3180
- const { join: join6 } = await import("path");
3222
+ const { join: join7 } = await import("path");
3181
3223
  const { mkdir: mkdir3 } = await import("fs/promises");
3182
- const snapshotsDir = join6(homedir5(), ".shipyard", "snapshots");
3224
+ const snapshotsDir = join7(homedir5(), ".shipyard", "snapshots");
3183
3225
  await mkdir3(snapshotsDir, { recursive: true });
3184
- const snapshotFile = join6(snapshotsDir, `${planId}.txt`);
3226
+ const snapshotFile = join7(snapshotsDir, `${planId}.txt`);
3185
3227
  await writeFile4(snapshotFile, snapshotUrl, "utf-8");
3186
3228
  logger.info({ planId, snapshotFile }, "Snapshot URL written to file");
3187
3229
  let prText = "";
@@ -5319,7 +5361,11 @@ Parameters:
5319
5361
  - options (string[], optional): For 'choice' type - available options (required for choice)
5320
5362
  - multiSelect (boolean, optional): For 'choice' type - allow selecting multiple options (uses checkboxes instead of radio buttons)
5321
5363
  - defaultValue (string, optional): Pre-filled value for text/multiline inputs
5322
- - timeout (number, optional): Timeout in seconds (default: 1800, min: 10, max: 14400)
5364
+ - timeout (number, optional): Timeout in seconds (default: 1800, min: 300, max: 1800)
5365
+ - Simple yes/no or quick choices: 300-600 seconds (5-10 minutes)
5366
+ - Complex questions with code examples: 600-1200 seconds (10-20 minutes)
5367
+ - Default (1800 = 30 minutes) is suitable for most cases
5368
+ - Note: System-level timeouts may cause earlier cancellation
5323
5369
  - planId (string, optional): Optional metadata to link request to plan (for activity log filtering)
5324
5370
 
5325
5371
  Returns:
@@ -5339,7 +5385,7 @@ const result = await requestUserInput({
5339
5385
  message: "Which database should we use?",
5340
5386
  type: "choice",
5341
5387
  options: ["PostgreSQL", "SQLite", "MongoDB"],
5342
- timeout: 120 // 2 minutes
5388
+ timeout: 600 // 10 minutes
5343
5389
  });
5344
5390
 
5345
5391
  if (result.success) {
@@ -5650,7 +5696,7 @@ async function setupReviewNotification(planId, pollIntervalSeconds) {
5650
5696
  return { script, fullResponse: text };
5651
5697
  }
5652
5698
  async function requestUserInput(opts) {
5653
- const { InputRequestManager: InputRequestManager2 } = await import("./input-request-manager-QX7MYHH7.js");
5699
+ const { InputRequestManager: InputRequestManager2 } = await import("./input-request-manager-4SL6YBFD.js");
5654
5700
  const ydoc = await getOrCreateDoc3(PLAN_INDEX_DOC_NAME);
5655
5701
  const manager = new InputRequestManager2();
5656
5702
  const params = opts.type === "choice" ? {
@@ -5694,7 +5740,7 @@ async function requestUserInput(opts) {
5694
5740
  };
5695
5741
  }
5696
5742
  async function postActivityUpdate(opts) {
5697
- const { logPlanEvent: logPlanEvent2 } = await import("./dist-2W7US5HB.js");
5743
+ const { logPlanEvent: logPlanEvent2 } = await import("./dist-R4AR63HS.js");
5698
5744
  const { getGitHubUsername: getGitHubUsername2 } = await import("./server-identity-LSZ4CZRK.js");
5699
5745
  const { nanoid: nanoid8 } = await import("nanoid");
5700
5746
  const doc = await getOrCreateDoc3(opts.planId);
@@ -5717,7 +5763,7 @@ async function postActivityUpdate(opts) {
5717
5763
  return { success: true, eventId, requestId };
5718
5764
  }
5719
5765
  async function resolveActivityRequest(opts) {
5720
- const { logPlanEvent: logPlanEvent2, getPlanEvents } = await import("./dist-2W7US5HB.js");
5766
+ const { logPlanEvent: logPlanEvent2, getPlanEvents } = await import("./dist-R4AR63HS.js");
5721
5767
  const { getGitHubUsername: getGitHubUsername2 } = await import("./server-identity-LSZ4CZRK.js");
5722
5768
  const doc = await getOrCreateDoc3(opts.planId);
5723
5769
  const actorName = await getGitHubUsername2();
@@ -5885,7 +5931,7 @@ var RequestUserInputInput = z14.object({
5885
5931
  options: z14.array(z14.string()).optional().describe("For 'choice' type - available options (required for choice)"),
5886
5932
  multiSelect: z14.boolean().optional().describe("For 'choice' type - allow selecting multiple options"),
5887
5933
  defaultValue: z14.string().optional().describe("Pre-filled value for text/multiline inputs"),
5888
- timeout: z14.number().optional().describe("Timeout in seconds (default: 1800, min: 10, max: 14400)"),
5934
+ timeout: z14.number().optional().describe("Timeout in seconds (default: 1800, min: 300, max: 1800)"),
5889
5935
  planId: z14.string().optional().describe("Optional metadata to link request to plan (for activity log filtering)")
5890
5936
  });
5891
5937
  var requestUserInputTool = {
@@ -5914,6 +5960,12 @@ For 'choice' type:
5914
5960
  This tool is analogous to AskUserQuestion, prompt(), or other agent question mechanisms,
5915
5961
  but shows responses in the browser UI where users are already viewing plans.
5916
5962
 
5963
+ Timeout guidelines:
5964
+ - Simple yes/no or quick choices: 300-600 seconds (5-10 minutes)
5965
+ - Complex questions with code examples: 600-1200 seconds (10-20 minutes)
5966
+ - Default (1800 = 30 minutes) is suitable for most cases
5967
+ - Note: System-level timeouts may cause earlier cancellation regardless of this value
5968
+
5917
5969
  NOTE: This is also available as requestUserInput() inside execute_code for multi-step workflows.`,
5918
5970
  inputSchema: {
5919
5971
  type: "object",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  InputRequestManager
3
- } from "./chunk-ZD2SHV5V.js";
4
- import "./chunk-5V7HUMDU.js";
3
+ } from "./chunk-CSSWBPUN.js";
4
+ import "./chunk-X4N6BYDY.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.494",
3
+ "version": "0.2.2-next.499",
4
4
  "description": "Shipyard MCP server and CLI tools for distributed planning with CRDTs",
5
5
  "type": "module",
6
6
  "bin": {