@schoolai/shipyard-mcp 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5970,7 +5970,7 @@ var require_util = __commonJS({
5970
5970
  return state && state.objectMode === false && state.ended === true && Number.isFinite(state.length) ? state.length : null;
5971
5971
  } else if (isBlobLike(body)) {
5972
5972
  return body.size != null ? body.size : null;
5973
- } else if (isBuffer(body)) {
5973
+ } else if (isBuffer2(body)) {
5974
5974
  return body.byteLength;
5975
5975
  }
5976
5976
  return null;
@@ -6065,7 +6065,7 @@ var require_util = __commonJS({
6065
6065
  }
6066
6066
  return headers.map((x) => Buffer.from(x));
6067
6067
  }
6068
- function isBuffer(buffer) {
6068
+ function isBuffer2(buffer) {
6069
6069
  return buffer instanceof Uint8Array || Buffer.isBuffer(buffer);
6070
6070
  }
6071
6071
  function assertRequestHandler(handler, method, upgrade) {
@@ -6586,7 +6586,7 @@ var require_util = __commonJS({
6586
6586
  bodyLength,
6587
6587
  deepClone,
6588
6588
  ReadableStreamFrom,
6589
- isBuffer,
6589
+ isBuffer: isBuffer2,
6590
6590
  assertRequestHandler,
6591
6591
  getSocketInfo,
6592
6592
  isFormDataLike,
@@ -6877,7 +6877,7 @@ var require_request = __commonJS({
6877
6877
  isValidHeaderValue,
6878
6878
  isStream,
6879
6879
  destroy,
6880
- isBuffer,
6880
+ isBuffer: isBuffer2,
6881
6881
  isFormDataLike,
6882
6882
  isIterable,
6883
6883
  isBlobLike,
@@ -6965,7 +6965,7 @@ var require_request = __commonJS({
6965
6965
  }
6966
6966
  };
6967
6967
  this.body.on("error", this.errorHandler);
6968
- } else if (isBuffer(body)) {
6968
+ } else if (isBuffer2(body)) {
6969
6969
  this.body = body.byteLength ? body : null;
6970
6970
  } else if (ArrayBuffer.isView(body)) {
6971
6971
  this.body = body.buffer.byteLength ? Buffer.from(body.buffer, body.byteOffset, body.byteLength) : null;
@@ -28488,459 +28488,10 @@ var require_undici = __commonJS({
28488
28488
  // src/index.ts
28489
28489
  init_cjs_shims();
28490
28490
 
28491
- // ../../packages/shared/dist/instructions/index.mjs
28492
- init_cjs_shims();
28493
- var TOOL_NAMES = {
28494
- ADD_ARTIFACT: "add_artifact",
28495
- ADD_PR_REVIEW_COMMENT: "add_pr_review_comment",
28496
- COMPLETE_TASK: "complete_task",
28497
- CREATE_PLAN: "create_plan",
28498
- EXECUTE_CODE: "execute_code",
28499
- LINK_PR: "link_pr",
28500
- READ_PLAN: "read_plan",
28501
- REGENERATE_SESSION_TOKEN: "regenerate_session_token",
28502
- REQUEST_USER_INPUT: "request_user_input",
28503
- SETUP_REVIEW_NOTIFICATION: "setup_review_notification",
28504
- UPDATE_BLOCK_CONTENT: "update_block_content",
28505
- UPDATE_PLAN: "update_plan"
28506
- };
28507
- var CRITICAL_USAGE_SECTION = `## CRITICAL: When to Use Shipyard
28508
-
28509
- \u26A0\uFE0F **MANDATORY USAGE RULE:**
28510
-
28511
- If Shipyard is available, you MUST use it for:
28512
- - Creating implementation plans
28513
- - Tracking work that needs human review
28514
- - Documenting proof of work (screenshots, videos, test results)
28515
- - ANY user request involving "plan", "track", "verify", or "prove"
28516
-
28517
- **DO NOT:**
28518
- - Create plans manually in chat or as markdown files
28519
- - Write implementation docs yourself when Shipyard is available
28520
- - Suggest alternatives to Shipyard for trackable work
28521
- - Overthink whether to use it - WHEN IN DOUBT, USE SHIPYARD
28522
-
28523
- **Decision Tree:**
28524
- \`\`\`
28525
- Need to create/track/verify work?
28526
- \u2502
28527
- \u25BC
28528
- Shipyard available?
28529
- \u2502 \u2502
28530
- YES NO
28531
- \u2502 \u2502
28532
- \u25BC \u25BC
28533
- USE IT Manual approach
28534
- NOW (tell user why)
28535
- \`\`\``;
28536
- var DELIVERABLES_SECTION = `## What are Deliverables?
28537
-
28538
- Deliverables are measurable outcomes you can **prove** with artifacts (screenshots, videos, test results).
28539
-
28540
- **Good deliverables (provable):**
28541
- \`\`\`
28542
- - [ ] Screenshot of working login page {#deliverable}
28543
- - [ ] Video showing drag-and-drop feature {#deliverable}
28544
- - [ ] Test results showing all tests pass {#deliverable}
28545
- \`\`\`
28546
-
28547
- **Bad deliverables (not provable - these are tasks, not deliverables):**
28548
- \`\`\`
28549
- - [ ] Implement getUserMedia API \u2190 Implementation detail, not provable
28550
- - [ ] Add error handling \u2190 Can't capture this with an artifact
28551
- - [ ] Refactor authentication \u2190 Too vague, no visual proof
28552
- \`\`\`
28553
-
28554
- **Rule:** If you can't screenshot/record/export it, it's not a deliverable.`;
28555
- var ARTIFACT_TYPES_SECTION = `## Artifact Types
28556
-
28557
- | Type | Use For | File Formats |
28558
- |------|---------|--------------|
28559
- | \`screenshot\` | UI changes, visual proof, error states | .png, .jpg, .webp |
28560
- | \`video\` | Complex flows, interactions, animations | .mp4, .webm |
28561
- | \`test_results\` | Test output, coverage reports | .json, .txt, .xml |
28562
- | \`diff\` | Code changes, before/after comparisons | .diff, .patch |`;
28563
- var TIPS_SECTION = `## Tips for Effective Use
28564
-
28565
- 1. **Plan deliverables first** - Decide what proves success before coding
28566
- 2. **Capture during work** - Take screenshots as you implement, not after
28567
- 3. **Be specific** - "Login page with error state" beats "Screenshot"
28568
- 4. **Link every artifact** - Always set \`deliverableId\` for auto-completion
28569
- 5. **Check feedback** - Read reviewer comments and iterate`;
28570
- var WHEN_NOT_TO_USE_SECTION = `## When NOT to Use Shipyard
28571
-
28572
- Skip Shipyard for:
28573
- - Quick answers or research questions (no artifacts to capture)
28574
- - Internal refactoring with no visible output
28575
- - Tasks where proof adds no value (trivial fixes)
28576
- - Exploration or debugging sessions
28577
- - Pure documentation without implementation`;
28578
- var USER_INPUT_SECTION = `## Human-Agent Communication
28579
-
28580
- **\`${TOOL_NAMES.REQUEST_USER_INPUT}\` is THE primary way to communicate with humans during active work.**
28581
-
28582
- Shipyard is the central hub where humans manage AI agents. When you need to ask a question, get clarification, or request a decision - use \`${TOOL_NAMES.REQUEST_USER_INPUT}\`. The human is already in the browser viewing your plan. That's where conversations should happen.
28583
-
28584
- ### Why Use ${TOOL_NAMES.REQUEST_USER_INPUT}
28585
-
28586
- - **Context:** The human sees your question alongside the plan, artifacts, and comments
28587
- - **History:** All exchanges are logged in the plan's activity feed
28588
- - **Continuity:** The conversation stays attached to the work, not scattered across chat windows
28589
- - **Flexibility:** 8 input types, multi-question forms, "Other" escape hatch for custom answers
28590
-
28591
- ### Replace Platform Tools
28592
-
28593
- **ALWAYS prefer \`${TOOL_NAMES.REQUEST_USER_INPUT}\` over platform-specific tools:**
28594
-
28595
- | Platform | DON'T Use | Use Instead |
28596
- |----------|-----------|-------------|
28597
- | Claude Code | \`AskUserQuestion\` | \`${TOOL_NAMES.REQUEST_USER_INPUT}\` |
28598
- | Cursor | Built-in prompts | \`${TOOL_NAMES.REQUEST_USER_INPUT}\` |
28599
- | Windsurf | Native dialogs | \`${TOOL_NAMES.REQUEST_USER_INPUT}\` |
28600
- | Claude Desktop | Chat questions | \`${TOOL_NAMES.REQUEST_USER_INPUT}\` |
28601
-
28602
- ### When to Ask
28603
-
28604
- Use \`${TOOL_NAMES.REQUEST_USER_INPUT}\` when you need:
28605
- - Clarification on requirements ("Which auth provider?")
28606
- - Decisions that affect implementation ("PostgreSQL or SQLite?")
28607
- - Confirmation before destructive actions ("Delete this file?")
28608
- - User preferences ("Rate this approach 1-5")
28609
- - Any information you can't infer from context
28610
-
28611
- ### Example
28612
-
28613
- \`\`\`typescript
28614
- const result = await requestUserInput({
28615
- message: "Which database should we use?",
28616
- type: "choice",
28617
- options: ["PostgreSQL", "SQLite", "MongoDB"],
28618
- timeout: 600 // 10 minutes
28619
- });
28620
-
28621
- if (result.success) {
28622
- console.log("User chose:", result.response);
28623
- }
28624
- \`\`\`
28625
-
28626
- **Note:** The MCP tool is named \`${TOOL_NAMES.REQUEST_USER_INPUT}\` (snake_case). Inside \`${TOOL_NAMES.EXECUTE_CODE}\`, it's available as \`requestUserInput()\` (camelCase).`;
28627
- var TROUBLESHOOTING_SECTION = `## Troubleshooting
28628
-
28629
- **Browser doesn't open:** Check MCP server is running and accessible.
28630
-
28631
- **Upload fails:** Verify file path exists. For GitHub uploads, check \`GITHUB_TOKEN\` has repo write access.
28632
-
28633
- **No auto-complete:** Ensure every deliverable has an artifact with matching \`deliverableId\`.
28634
-
28635
- **Plan not syncing:** Check WebSocket connection to registry server.
28636
-
28637
- **Input request times out:** User may not have seen it or needs more time. Default timeout is 30 minutes. Try again with a longer timeout or rephrase the question.
28638
-
28639
- **Input request declined:** User clicked "Decline." Rephrase your question, proceed with a reasonable default, or use a different approach.
28640
-
28641
- **No response to input:** Check if browser is connected to the plan. User may have closed the browser window.`;
28642
- var COMMON_INSTRUCTIONS = [
28643
- CRITICAL_USAGE_SECTION,
28644
- USER_INPUT_SECTION,
28645
- DELIVERABLES_SECTION,
28646
- ARTIFACT_TYPES_SECTION,
28647
- TIPS_SECTION,
28648
- WHEN_NOT_TO_USE_SECTION,
28649
- TROUBLESHOOTING_SECTION
28650
- ].join("\n\n");
28651
- var CLAUDE_CODE_HEADER = `[SHIPYARD] Collaborative planning with human review & proof-of-work tracking.`;
28652
- var PLAN_MODE_WORKFLOW = `## How to Use (Claude Code with Hooks)
28653
-
28654
- You have the **full Shipyard experience** with automatic hooks. Use native plan mode:
28655
-
28656
- ### Workflow
28657
-
28658
- 1. **Enter plan mode** (Shift+Tab) \u2192 Browser opens with live plan automatically
28659
- 2. **Write your plan** with \`{#deliverable}\` markers for provable outcomes
28660
- 3. **Exit plan mode** \u2192 Hook **BLOCKS** until human approves or requests changes
28661
- 4. **On approval** \u2192 You automatically receive: planId, sessionToken, deliverable IDs
28662
- 5. **Do the work** \u2192 Take screenshots/videos as you implement
28663
- 6. **Upload artifacts** \u2192 \`${TOOL_NAMES.ADD_ARTIFACT}(filePath, deliverableId)\` for each deliverable
28664
- 7. **Auto-complete** \u2192 When all deliverables have artifacts, task completes with snapshot URL
28665
-
28666
- ### After Approval
28667
-
28668
- You only need ONE tool: \`${TOOL_NAMES.ADD_ARTIFACT}\`
28669
-
28670
- The hook automatically injects everything you need (planId, sessionToken, deliverables).
28671
- Just call \`${TOOL_NAMES.ADD_ARTIFACT}\` with the file path and deliverable ID.
28672
-
28673
- \`\`\`typescript
28674
- // Example: After approval, you'll have these in context
28675
- // planId: "abc123"
28676
- // sessionToken: "xyz..."
28677
- // deliverables: [{ id: "del_xxx", text: "Screenshot of login" }]
28678
-
28679
- await addArtifact({
28680
- planId,
28681
- sessionToken,
28682
- type: 'screenshot',
28683
- filename: 'login-page.png',
28684
- source: 'file',
28685
- filePath: '/tmp/screenshot.png',
28686
- deliverableId: deliverables[0].id
28687
- });
28688
- \`\`\`
28689
-
28690
- When the last deliverable gets an artifact, the task auto-completes and returns a snapshot URL.`;
28691
- var IMPORTANT_NOTES = `## Important Notes for Claude Code
28692
-
28693
- - **DO NOT call \`createPlan()\` directly** - The hook handles plan creation when you enter plan mode
28694
- - **DO NOT use the Shipyard skill** - The hook provides everything you need
28695
- - **DO NOT poll for approval** - The hook blocks automatically until human decides
28696
- - **DO use plan mode** for ANY work that needs tracking, verification, or human review
28697
- - **DO use \`${TOOL_NAMES.REQUEST_USER_INPUT}\`** instead of \`AskUserQuestion\` - The human is in the browser viewing your plan, questions should appear there`;
28698
- var CLAUDE_CODE_INSTRUCTIONS = [
28699
- CLAUDE_CODE_HEADER,
28700
- "",
28701
- CRITICAL_USAGE_SECTION,
28702
- "",
28703
- USER_INPUT_SECTION,
28704
- "",
28705
- PLAN_MODE_WORKFLOW,
28706
- "",
28707
- DELIVERABLES_SECTION,
28708
- "",
28709
- ARTIFACT_TYPES_SECTION,
28710
- "",
28711
- IMPORTANT_NOTES,
28712
- "",
28713
- TIPS_SECTION,
28714
- "",
28715
- WHEN_NOT_TO_USE_SECTION,
28716
- "",
28717
- TROUBLESHOOTING_SECTION
28718
- ].join("\n");
28719
- var MCP_DIRECT_HEADER = `# Shipyard: Your Agent Management Hub
28720
-
28721
- > **Shipyard is the central interface where humans manage AI agents.** Plans, artifacts, feedback, and communication all happen here.
28722
-
28723
- Shipyard turns invisible agent work into reviewable, verifiable tasks. Instead of trusting that code was written correctly, reviewers see screenshots, videos, and test results as proof.
28724
-
28725
- **Key principle:** When you're working in Shipyard, ALL human-agent communication should happen through Shipyard's \`${TOOL_NAMES.REQUEST_USER_INPUT}\` tool. The human is already in the browser viewing your plan - that's where they expect to interact with you.`;
28726
- var MCP_TOOLS_OVERVIEW = `## Available MCP Tools
28727
-
28728
- | Tool | Purpose |
28729
- |------|---------|
28730
- | \`${TOOL_NAMES.REQUEST_USER_INPUT}\` | **THE primary human-agent communication channel** - Ask questions, get decisions, request clarification |
28731
- | \`${TOOL_NAMES.EXECUTE_CODE}\` | Run TypeScript that calls Shipyard APIs (recommended for multi-step operations) |
28732
-
28733
- ### ${TOOL_NAMES.REQUEST_USER_INPUT}: Your Direct Line to the Human
28734
-
28735
- This is how you talk to humans during active work. Don't use your platform's built-in question tools (AskUserQuestion, etc.) - use this instead. The human is in the browser viewing your plan, and that's where they expect to see your questions.
28736
-
28737
- **Preferred approach:** Use \`${TOOL_NAMES.EXECUTE_CODE}\` to chain multiple API calls in one step.`;
28738
- var MCP_WORKFLOW = `## Workflow (MCP Direct)
28739
-
28740
- ### Step 1: Create Plan
28741
-
28742
- \`\`\`typescript
28743
- const plan = await createPlan({
28744
- title: "Add user authentication",
28745
- content: \`
28746
- ## Deliverables
28747
- - [ ] Screenshot of login page {#deliverable}
28748
- - [ ] Screenshot of error handling {#deliverable}
28749
-
28750
- ## Implementation
28751
- 1. Create login form component
28752
- 2. Add validation
28753
- 3. Connect to auth API
28754
- \`
28755
- });
28756
-
28757
- const { planId, sessionToken, deliverables, monitoringScript } = plan;
28758
- // deliverables = [{ id: "del_xxx", text: "Screenshot of login page" }, ...]
28759
- \`\`\`
28760
-
28761
- ### Step 2: Wait for Approval
28762
-
28763
- For platforms without hooks, run the monitoring script in the background:
28764
-
28765
- \`\`\`bash
28766
- # The monitoringScript polls for approval status
28767
- # Run it in background while you wait
28768
- bash <(echo "$monitoringScript") &
28769
- \`\`\`
28770
-
28771
- Or poll manually:
28772
-
28773
- \`\`\`typescript
28774
- const status = await readPlan(planId, sessionToken);
28775
- if (status.status === "in_progress") {
28776
- // Approved! Proceed with work
28777
- }
28778
- if (status.status === "changes_requested") {
28779
- // Read feedback, make changes
28780
- }
28781
- \`\`\`
28782
-
28783
- ### Step 3: Do the Work
28784
-
28785
- Implement the feature, taking screenshots/recordings as you go.
28786
-
28787
- ### Step 4: Upload Artifacts
28788
-
28789
- \`\`\`typescript
28790
- await addArtifact({
28791
- planId,
28792
- sessionToken,
28793
- type: 'screenshot',
28794
- filename: 'login-page.png',
28795
- source: 'file',
28796
- filePath: '/path/to/screenshot.png',
28797
- deliverableId: deliverables[0].id // Links to specific deliverable
28798
- });
28799
-
28800
- const result = await addArtifact({
28801
- planId,
28802
- sessionToken,
28803
- type: 'screenshot',
28804
- filename: 'error-handling.png',
28805
- source: 'file',
28806
- filePath: '/path/to/error.png',
28807
- deliverableId: deliverables[1].id
28808
- });
28809
-
28810
- // Auto-complete triggers when ALL deliverables have artifacts
28811
- if (result.allDeliverablesComplete) {
28812
- console.log('Done!', result.snapshotUrl);
28813
- }
28814
- \`\`\``;
28815
- var API_REFERENCE = `## API Reference
28816
-
28817
- ### createPlan(options)
28818
-
28819
- Creates a new plan and opens it in the browser.
28820
-
28821
- **Parameters:**
28822
- - \`title\` (string) - Plan title
28823
- - \`content\` (string) - Markdown content with \`{#deliverable}\` markers
28824
- - \`repo\` (string, optional) - GitHub repo for artifact storage
28825
- - \`prNumber\` (number, optional) - PR number to link
28826
-
28827
- **Returns:** \`{ planId, sessionToken, url, deliverables, monitoringScript }\`
28828
-
28829
- ### readPlan(planId, sessionToken, options?)
28830
-
28831
- Reads current plan state.
28832
-
28833
- **Parameters:**
28834
- - \`planId\` (string) - Plan ID
28835
- - \`sessionToken\` (string) - Session token from createPlan
28836
- - \`options.includeAnnotations\` (boolean) - Include reviewer comments
28837
-
28838
- **Returns:** \`{ content, status, title, deliverables }\`
28839
-
28840
- ### addArtifact(options)
28841
-
28842
- Uploads proof-of-work artifact.
28843
-
28844
- **Parameters:**
28845
- - \`planId\` (string) - Plan ID
28846
- - \`sessionToken\` (string) - Session token
28847
- - \`type\` ('screenshot' | 'video' | 'test_results' | 'diff')
28848
- - \`filename\` (string) - File name
28849
- - \`source\` ('file' | 'url' | 'base64')
28850
- - \`filePath\` (string) - Local file path (when source='file')
28851
- - \`deliverableId\` (string, optional) - Links artifact to deliverable
28852
-
28853
- **Returns:** \`{ artifactId, url, allDeliverablesComplete, snapshotUrl? }\`
28854
-
28855
- ### requestUserInput(options)
28856
-
28857
- Asks user a question via browser modal.
28858
-
28859
- **Parameters:**
28860
- - \`message\` (string) - Question to ask
28861
- - \`type\` (string) - Input type (see below)
28862
- - \`options\` (string[], for 'choice') - Available choices
28863
- - \`timeout\` (number, optional) - Timeout in seconds
28864
- - Type-specific parameters (min, max, format, etc.)
28865
-
28866
- **Returns:** \`{ success, response?, status }\`
28867
-
28868
- **Supported types (8 total):**
28869
- 1. \`text\` - Single-line text
28870
- 2. \`multiline\` - Multi-line text area
28871
- 3. \`choice\` - Radio/checkbox/dropdown (auto-adds "Other" option)
28872
- - Auto-switches: 1-8 options = radio/checkbox, 9+ = dropdown
28873
- - \`multiSelect: true\` for checkboxes
28874
- - \`displayAs: 'dropdown'\` to force dropdown UI
28875
- 4. \`confirm\` - Yes/No buttons
28876
- 5. \`number\` - Numeric input with validation
28877
- - \`min\`, \`max\`, \`format\` ('integer' | 'decimal' | 'currency' | 'percentage')
28878
- 6. \`email\` - Email validation
28879
- - \`domain\` for restriction
28880
- 7. \`date\` - Date picker with range
28881
- - \`minDate\`, \`maxDate\` (YYYY-MM-DD format)
28882
- 8. \`rating\` - Scale rating (auto-selects stars/numbers)
28883
- - \`min\`, \`max\`, \`style\` ('stars' | 'numbers' | 'emoji'), \`labels\`
28884
-
28885
- **Response format:**
28886
- - All responses are strings
28887
- - choice (single): \`"PostgreSQL"\` or custom text from "Other"
28888
- - choice (multi): \`"option1, option2"\` (comma-space separated)
28889
- - confirm: \`"yes"\` or \`"no"\` (lowercase)
28890
- - number: \`"42"\` or \`"3.14"\`
28891
- - email: \`"user@example.com"\`
28892
- - date: \`"2026-01-24"\` (ISO 8601)
28893
- - rating: \`"4"\` (integer as string)
28894
- - See docs/INPUT-RESPONSE-FORMATS.md for complete specification`;
28895
- var HANDLING_FEEDBACK = `## Handling Reviewer Feedback
28896
-
28897
- \`\`\`typescript
28898
- const status = await readPlan(planId, sessionToken, {
28899
- includeAnnotations: true
28900
- });
28901
-
28902
- if (status.status === "changes_requested") {
28903
- // Read the content for inline comments
28904
- console.log(status.content);
28905
-
28906
- // Make changes based on feedback
28907
- // Upload new artifacts
28908
- // Plan will transition back to pending_review
28909
- }
28910
- \`\`\``;
28911
- var MCP_DIRECT_INSTRUCTIONS = [
28912
- MCP_DIRECT_HEADER,
28913
- "",
28914
- CRITICAL_USAGE_SECTION,
28915
- "",
28916
- USER_INPUT_SECTION,
28917
- "",
28918
- MCP_TOOLS_OVERVIEW,
28919
- "",
28920
- MCP_WORKFLOW,
28921
- "",
28922
- DELIVERABLES_SECTION,
28923
- "",
28924
- ARTIFACT_TYPES_SECTION,
28925
- "",
28926
- API_REFERENCE,
28927
- "",
28928
- HANDLING_FEEDBACK,
28929
- "",
28930
- TIPS_SECTION,
28931
- "",
28932
- WHEN_NOT_TO_USE_SECTION,
28933
- "",
28934
- TROUBLESHOOTING_SECTION
28935
- ].join("\n");
28936
-
28937
- // src/adapters/claude-code.ts
28938
- init_cjs_shims();
28939
-
28940
28491
  // ../../packages/schema/dist/index.mjs
28941
28492
  init_cjs_shims();
28942
28493
 
28943
- // ../../packages/schema/dist/yjs-helpers-DxqhtEAu.mjs
28494
+ // ../../packages/schema/dist/yjs-helpers-CmikQBE7.mjs
28944
28495
  init_cjs_shims();
28945
28496
 
28946
28497
  // ../../packages/schema/dist/plan.mjs
@@ -42837,7 +42388,10 @@ var CursorOriginMetadataSchema = external_exports.object({
42837
42388
  conversationId: external_exports.string(),
42838
42389
  generationId: external_exports.string().optional()
42839
42390
  });
42840
- var UnknownOriginMetadataSchema = external_exports.object({ platform: external_exports.literal("unknown") });
42391
+ var UnknownOriginMetadataSchema = external_exports.object({
42392
+ platform: external_exports.literal("unknown"),
42393
+ cwd: external_exports.string()
42394
+ });
42841
42395
  var OriginMetadataSchema = external_exports.discriminatedUnion("platform", [
42842
42396
  ClaudeCodeOriginMetadataSchema,
42843
42397
  DevinOriginMetadataSchema,
@@ -42864,28 +42418,10 @@ var PlanEventBaseSchema = external_exports.object({
42864
42418
  inboxWorthy: external_exports.boolean().optional(),
42865
42419
  inboxFor: external_exports.union([external_exports.string(), external_exports.array(external_exports.string())]).optional()
42866
42420
  });
42867
- var AgentActivityDataSchema = external_exports.discriminatedUnion("activityType", [
42868
- external_exports.object({
42869
- activityType: external_exports.literal("help_request"),
42870
- requestId: external_exports.string(),
42871
- message: external_exports.string()
42872
- }),
42873
- external_exports.object({
42874
- activityType: external_exports.literal("help_request_resolved"),
42875
- requestId: external_exports.string(),
42876
- resolution: external_exports.string().optional()
42877
- }),
42878
- external_exports.object({
42879
- activityType: external_exports.literal("blocker"),
42880
- message: external_exports.string(),
42881
- requestId: external_exports.string()
42882
- }),
42883
- external_exports.object({
42884
- activityType: external_exports.literal("blocker_resolved"),
42885
- requestId: external_exports.string(),
42886
- resolution: external_exports.string().optional()
42887
- })
42888
- ]);
42421
+ var AgentActivityDataSchema = external_exports.object({
42422
+ activityType: external_exports.literal("update"),
42423
+ message: external_exports.string()
42424
+ });
42889
42425
  var PlanEventSchema = external_exports.discriminatedUnion("type", [
42890
42426
  PlanEventBaseSchema.extend({ type: external_exports.enum([
42891
42427
  "plan_created",
@@ -42996,7 +42532,19 @@ var PlanEventSchema = external_exports.discriminatedUnion("type", [
42996
42532
  data: external_exports.object({
42997
42533
  requestId: external_exports.string(),
42998
42534
  response: external_exports.unknown(),
42999
- answeredBy: external_exports.string()
42535
+ answeredBy: external_exports.string(),
42536
+ requestMessage: external_exports.string().optional(),
42537
+ requestType: external_exports.enum([
42538
+ "text",
42539
+ "multiline",
42540
+ "choice",
42541
+ "confirm",
42542
+ "number",
42543
+ "email",
42544
+ "date",
42545
+ "rating",
42546
+ "multi"
42547
+ ]).optional()
43000
42548
  })
43001
42549
  }),
43002
42550
  PlanEventBaseSchema.extend({
@@ -43118,11 +42666,74 @@ var PRReviewCommentSchema = external_exports.object({
43118
42666
  createdAt: external_exports.number(),
43119
42667
  resolved: external_exports.boolean().optional()
43120
42668
  });
42669
+ var LocalDiffCommentSchema = external_exports.object({
42670
+ id: external_exports.string(),
42671
+ type: external_exports.literal("local"),
42672
+ path: external_exports.string(),
42673
+ line: external_exports.number(),
42674
+ body: external_exports.string(),
42675
+ author: external_exports.string(),
42676
+ createdAt: external_exports.number(),
42677
+ baseRef: external_exports.string(),
42678
+ resolved: external_exports.boolean().optional(),
42679
+ lineContentHash: external_exports.string().optional(),
42680
+ machineId: external_exports.string().optional()
42681
+ });
42682
+ var GitHubArtifactParseSchema = external_exports.object({
42683
+ id: external_exports.string(),
42684
+ type: external_exports.enum([
42685
+ "html",
42686
+ "image",
42687
+ "video"
42688
+ ]),
42689
+ filename: external_exports.string(),
42690
+ description: external_exports.string().optional(),
42691
+ uploadedAt: external_exports.number().optional(),
42692
+ storage: external_exports.literal("github"),
42693
+ url: external_exports.string()
42694
+ });
42695
+ var LocalArtifactParseSchema = external_exports.object({
42696
+ id: external_exports.string(),
42697
+ type: external_exports.enum([
42698
+ "html",
42699
+ "image",
42700
+ "video"
42701
+ ]),
42702
+ filename: external_exports.string(),
42703
+ description: external_exports.string().optional(),
42704
+ uploadedAt: external_exports.number().optional(),
42705
+ storage: external_exports.literal("local"),
42706
+ localArtifactId: external_exports.string()
42707
+ });
43121
42708
 
43122
- // ../../packages/schema/dist/yjs-helpers-DxqhtEAu.mjs
42709
+ // ../../packages/schema/dist/yjs-helpers-CmikQBE7.mjs
43123
42710
  function assertNever2(value) {
43124
42711
  throw new Error(`Unhandled discriminated union member: ${JSON.stringify(value)}`);
43125
42712
  }
42713
+ var SyncedFileChangeSchema = external_exports.object({
42714
+ path: external_exports.string(),
42715
+ status: external_exports.enum([
42716
+ "added",
42717
+ "modified",
42718
+ "deleted",
42719
+ "renamed"
42720
+ ]),
42721
+ patch: external_exports.string(),
42722
+ staged: external_exports.boolean()
42723
+ });
42724
+ var ChangeSnapshotSchema = external_exports.object({
42725
+ machineId: external_exports.string(),
42726
+ machineName: external_exports.string(),
42727
+ ownerId: external_exports.string(),
42728
+ headSha: external_exports.string(),
42729
+ branch: external_exports.string(),
42730
+ cwd: external_exports.string(),
42731
+ isLive: external_exports.boolean(),
42732
+ updatedAt: external_exports.number(),
42733
+ files: external_exports.array(SyncedFileChangeSchema),
42734
+ totalAdditions: external_exports.number(),
42735
+ totalDeletions: external_exports.number()
42736
+ });
43126
42737
  var AgentPresenceSchema = external_exports.object({
43127
42738
  agentType: external_exports.string(),
43128
42739
  sessionId: external_exports.string(),
@@ -43205,8 +42816,15 @@ var UnregisterServerResponseSchema = external_exports.object({
43205
42816
  success: external_exports.boolean(),
43206
42817
  existed: external_exports.boolean()
43207
42818
  });
42819
+ var ChangeTypeSchema = external_exports.enum([
42820
+ "status",
42821
+ "comments",
42822
+ "resolved",
42823
+ "content",
42824
+ "artifacts"
42825
+ ]);
43208
42826
  var CreateSubscriptionRequestSchema = external_exports.object({
43209
- subscribe: external_exports.array(external_exports.string()).optional(),
42827
+ subscribe: external_exports.array(ChangeTypeSchema).optional(),
43210
42828
  windowMs: external_exports.number().positive().optional(),
43211
42829
  maxWindowMs: external_exports.number().positive().optional(),
43212
42830
  threshold: external_exports.number().positive().optional()
@@ -43318,7 +42936,7 @@ var ChoiceQuestionSchema = QuestionBaseSchema.extend({
43318
42936
  placeholder: external_exports.string().optional()
43319
42937
  });
43320
42938
  var ConfirmQuestionSchema = QuestionBaseSchema.extend({ type: external_exports.literal("confirm") });
43321
- var NumberQuestionSchema = QuestionBaseSchema.extend({
42939
+ var NumberQuestionBaseSchema = QuestionBaseSchema.extend({
43322
42940
  type: external_exports.literal("number"),
43323
42941
  min: external_exports.number().optional(),
43324
42942
  max: external_exports.number().optional(),
@@ -43328,17 +42946,17 @@ var NumberQuestionSchema = QuestionBaseSchema.extend({
43328
42946
  "currency",
43329
42947
  "percentage"
43330
42948
  ]).optional()
43331
- }).refine((data) => data.min === void 0 || data.max === void 0 || data.min <= data.max, { message: "min must be <= max" });
42949
+ });
43332
42950
  var EmailQuestionSchema = QuestionBaseSchema.extend({
43333
42951
  type: external_exports.literal("email"),
43334
42952
  domain: external_exports.string().optional()
43335
42953
  });
43336
- var DateQuestionSchema = QuestionBaseSchema.extend({
42954
+ var DateQuestionBaseSchema = QuestionBaseSchema.extend({
43337
42955
  type: external_exports.literal("date"),
43338
42956
  min: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").optional(),
43339
42957
  max: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").optional()
43340
- }).refine((data) => data.min === void 0 || data.max === void 0 || new Date(data.min) <= new Date(data.max), { message: "min date must be before or equal to max date" });
43341
- var RatingQuestionSchema = QuestionBaseSchema.extend({
42958
+ });
42959
+ var RatingQuestionBaseSchema = QuestionBaseSchema.extend({
43342
42960
  type: external_exports.literal("rating"),
43343
42961
  min: external_exports.number().int().optional(),
43344
42962
  max: external_exports.number().int().optional(),
@@ -43351,20 +42969,58 @@ var RatingQuestionSchema = QuestionBaseSchema.extend({
43351
42969
  low: external_exports.string().optional(),
43352
42970
  high: external_exports.string().optional()
43353
42971
  }).optional()
43354
- }).refine((data) => {
43355
- if (data.min === void 0 || data.max === void 0) return true;
43356
- return data.min <= data.max && data.max - data.min <= 20;
43357
- }, { message: "Rating scale must have min <= max and at most 20 items" });
42972
+ });
43358
42973
  var QuestionSchema = external_exports.discriminatedUnion("type", [
43359
42974
  TextQuestionSchema,
43360
42975
  MultilineQuestionSchema,
43361
42976
  ChoiceQuestionSchema,
43362
42977
  ConfirmQuestionSchema,
43363
- NumberQuestionSchema,
42978
+ NumberQuestionBaseSchema,
43364
42979
  EmailQuestionSchema,
43365
- DateQuestionSchema,
43366
- RatingQuestionSchema
42980
+ DateQuestionBaseSchema,
42981
+ RatingQuestionBaseSchema
43367
42982
  ]);
42983
+ function validateNumberQuestion(q, index, ctx) {
42984
+ if (q.min !== void 0 && q.max !== void 0 && q.min > q.max) ctx.addIssue({
42985
+ code: external_exports.ZodIssueCode.custom,
42986
+ message: "min must be <= max",
42987
+ path: ["questions", index]
42988
+ });
42989
+ }
42990
+ function validateDateQuestion(q, index, ctx) {
42991
+ if (q.min !== void 0 && q.max !== void 0 && new Date(q.min) > new Date(q.max)) ctx.addIssue({
42992
+ code: external_exports.ZodIssueCode.custom,
42993
+ message: "min date must be before or equal to max date",
42994
+ path: ["questions", index]
42995
+ });
42996
+ }
42997
+ function validateRatingQuestion(q, index, ctx) {
42998
+ if (q.min === void 0 || q.max === void 0) return;
42999
+ if (q.min > q.max || q.max - q.min > 20) ctx.addIssue({
43000
+ code: external_exports.ZodIssueCode.custom,
43001
+ message: "Rating scale must have min <= max and at most 20 items",
43002
+ path: ["questions", index]
43003
+ });
43004
+ }
43005
+ function validateQuestionConstraints(questions, ctx) {
43006
+ for (let i = 0; i < questions.length; i++) {
43007
+ const q = questions[i];
43008
+ if (!q) continue;
43009
+ switch (q.type) {
43010
+ case "number":
43011
+ validateNumberQuestion(q, i, ctx);
43012
+ break;
43013
+ case "date":
43014
+ validateDateQuestion(q, i, ctx);
43015
+ break;
43016
+ case "rating":
43017
+ validateRatingQuestion(q, i, ctx);
43018
+ break;
43019
+ default:
43020
+ break;
43021
+ }
43022
+ }
43023
+ }
43368
43024
  var MultiQuestionInputRequestSchema = external_exports.object({
43369
43025
  id: external_exports.string(),
43370
43026
  createdAt: external_exports.number(),
@@ -43377,6 +43033,8 @@ var MultiQuestionInputRequestSchema = external_exports.object({
43377
43033
  answeredAt: external_exports.number().optional(),
43378
43034
  answeredBy: external_exports.string().optional(),
43379
43035
  isBlocker: external_exports.boolean().optional()
43036
+ }).superRefine((data, ctx) => {
43037
+ validateQuestionConstraints(data.questions, ctx);
43380
43038
  });
43381
43039
  var AnyInputRequestSchema = external_exports.union([InputRequestSchema, MultiQuestionInputRequestSchema]);
43382
43040
  var YDOC_KEYS = {
@@ -43392,8 +43050,11 @@ var YDOC_KEYS = {
43392
43050
  PR_REVIEW_COMMENTS: "prReviewComments",
43393
43051
  EVENTS: "events",
43394
43052
  SNAPSHOTS: "snapshots",
43395
- INPUT_REQUESTS: "inputRequests"
43053
+ INPUT_REQUESTS: "inputRequests",
43054
+ LOCAL_DIFF_COMMENTS: "localDiffComments",
43055
+ CHANGE_SNAPSHOTS: "changeSnapshots"
43396
43056
  };
43057
+ var validKeys = new Set(Object.values(YDOC_KEYS));
43397
43058
  var CommentBodySchema = external_exports.union([external_exports.string(), external_exports.array(external_exports.unknown())]);
43398
43059
  var ThreadCommentSchema = external_exports.object({
43399
43060
  id: external_exports.string(),
@@ -43413,10 +43074,13 @@ function extractTextFromCommentBody(body) {
43413
43074
  return body.map((block) => {
43414
43075
  if (typeof block === "string") return block;
43415
43076
  if (typeof block !== "object" || block === null) return "";
43416
- const blockObj = block;
43417
- if (Array.isArray(blockObj.content)) return blockObj.content.map((item) => {
43077
+ const content = Object.fromEntries(Object.entries(block)).content;
43078
+ if (Array.isArray(content)) return content.map((item) => {
43418
43079
  if (typeof item === "string") return item;
43419
- if (typeof item === "object" && item !== null && "text" in item) return item.text;
43080
+ if (typeof item === "object" && item !== null && "text" in item) {
43081
+ const text = Object.fromEntries(Object.entries(item)).text;
43082
+ return typeof text === "string" ? text : "";
43083
+ }
43420
43084
  return "";
43421
43085
  }).join("");
43422
43086
  return "";
@@ -43446,6 +43110,48 @@ function getPlanMetadataWithValidation(ydoc) {
43446
43110
  // ../../packages/schema/dist/url-encoding.mjs
43447
43111
  init_cjs_shims();
43448
43112
  var import_lz_string = __toESM(require_lz_string(), 1);
43113
+ var UrlSnapshotRefSchema = external_exports.object({
43114
+ id: external_exports.string(),
43115
+ status: external_exports.enum(PlanStatusValues),
43116
+ createdBy: external_exports.string(),
43117
+ reason: external_exports.string(),
43118
+ createdAt: external_exports.number(),
43119
+ threads: external_exports.object({
43120
+ total: external_exports.number(),
43121
+ unresolved: external_exports.number()
43122
+ }).optional()
43123
+ });
43124
+ var UrlKeyVersionSchema = external_exports.object({
43125
+ id: external_exports.string(),
43126
+ content: external_exports.array(external_exports.unknown())
43127
+ });
43128
+ var UrlEncodedPlanV1Schema = external_exports.object({
43129
+ v: external_exports.literal(1),
43130
+ id: external_exports.string(),
43131
+ title: external_exports.string(),
43132
+ status: external_exports.enum(PlanStatusValues),
43133
+ repo: external_exports.string().optional(),
43134
+ pr: external_exports.number().optional(),
43135
+ content: external_exports.array(external_exports.unknown()),
43136
+ artifacts: external_exports.array(ArtifactSchema).optional(),
43137
+ deliverables: external_exports.array(DeliverableSchema).optional(),
43138
+ comments: external_exports.array(external_exports.unknown()).optional()
43139
+ });
43140
+ var UrlEncodedPlanV2Schema = external_exports.object({
43141
+ v: external_exports.literal(2),
43142
+ id: external_exports.string(),
43143
+ title: external_exports.string(),
43144
+ status: external_exports.enum(PlanStatusValues),
43145
+ repo: external_exports.string().optional(),
43146
+ pr: external_exports.number().optional(),
43147
+ content: external_exports.array(external_exports.unknown()),
43148
+ artifacts: external_exports.array(ArtifactSchema).optional(),
43149
+ deliverables: external_exports.array(DeliverableSchema).optional(),
43150
+ comments: external_exports.array(external_exports.unknown()).optional(),
43151
+ versionRefs: external_exports.array(UrlSnapshotRefSchema).optional(),
43152
+ keyVersions: external_exports.array(UrlKeyVersionSchema).optional()
43153
+ });
43154
+ var UrlEncodedPlanSchema = external_exports.discriminatedUnion("v", [UrlEncodedPlanV1Schema, UrlEncodedPlanV2Schema]);
43449
43155
 
43450
43156
  // ../../node_modules/.pnpm/@trpc+server@11.8.1_typescript@5.9.3/node_modules/@trpc/server/dist/index.mjs
43451
43157
  init_cjs_shims();
@@ -44235,13 +43941,20 @@ var A2AFilePartSchema = external_exports.object({
44235
43941
  mediaType: external_exports.string().optional(),
44236
43942
  name: external_exports.string().optional()
44237
43943
  });
43944
+ function hasStringProperty(obj, prop) {
43945
+ return prop in obj && typeof obj[prop] === "string";
43946
+ }
43947
+ function toRecord(obj) {
43948
+ return Object.fromEntries(Object.entries(obj));
43949
+ }
44238
43950
  var A2APartSchema = external_exports.object({ type: external_exports.enum([
44239
43951
  "text",
44240
43952
  "data",
44241
43953
  "file"
44242
43954
  ]) }).passthrough().superRefine((val, ctx) => {
43955
+ const record2 = toRecord(val);
44243
43956
  if (val.type === "text") {
44244
- if (typeof val.text !== "string") ctx.addIssue({
43957
+ if (!hasStringProperty(record2, "text")) ctx.addIssue({
44245
43958
  code: external_exports.ZodIssueCode.custom,
44246
43959
  message: "text part must have a string text field"
44247
43960
  });
@@ -44251,7 +43964,7 @@ var A2APartSchema = external_exports.object({ type: external_exports.enum([
44251
43964
  message: "data part must have a data field"
44252
43965
  });
44253
43966
  } else if (val.type === "file") {
44254
- if (typeof val.uri !== "string") ctx.addIssue({
43967
+ if (!hasStringProperty(record2, "uri")) ctx.addIssue({
44255
43968
  code: external_exports.ZodIssueCode.custom,
44256
43969
  message: "file part must have a string uri field"
44257
43970
  });
@@ -44259,7 +43972,7 @@ var A2APartSchema = external_exports.object({ type: external_exports.enum([
44259
43972
  });
44260
43973
  function isValidA2APart(part) {
44261
43974
  if (!part || typeof part !== "object") return false;
44262
- const p = part;
43975
+ const p = toRecord(part);
44263
43976
  const t$1 = p.type;
44264
43977
  if (t$1 === "text") return typeof p.text === "string";
44265
43978
  else if (t$1 === "data") return "data" in p;
@@ -44277,17 +43990,38 @@ var A2AMessageSchema = external_exports.object({
44277
43990
  taskId: external_exports.string().optional(),
44278
43991
  referenceTaskIds: external_exports.array(external_exports.string()).optional(),
44279
43992
  metadata: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
44280
- extensions: external_exports.array(external_exports.string()).optional()
43993
+ extensions: external_exports.array(external_exports.string()).optional(),
43994
+ parts: external_exports.array(external_exports.unknown())
44281
43995
  }).passthrough().refine((val) => {
44282
- const parts = val.parts;
44283
- return isValidA2AParts(parts);
43996
+ return isValidA2AParts(val.parts);
44284
43997
  }, {
44285
43998
  message: "Invalid parts array - each part must have valid type and required fields",
44286
43999
  path: ["parts"]
44287
- }).transform((val) => ({
44288
- ...val,
44289
- parts: val.parts
44290
- }));
44000
+ }).transform((val) => {
44001
+ const parts = val.parts.map((p) => {
44002
+ if (!p || typeof p !== "object") throw new Error("Invalid part: not an object");
44003
+ const record2 = toRecord(p);
44004
+ const partType = record2.type;
44005
+ if (partType === "text") return {
44006
+ type: "text",
44007
+ text: String(record2.text)
44008
+ };
44009
+ else if (partType === "data") return {
44010
+ type: "data",
44011
+ data: record2.data
44012
+ };
44013
+ else return {
44014
+ type: "file",
44015
+ uri: String(record2.uri),
44016
+ mediaType: typeof record2.mediaType === "string" ? record2.mediaType : void 0,
44017
+ name: typeof record2.name === "string" ? record2.name : void 0
44018
+ };
44019
+ });
44020
+ return {
44021
+ ...val,
44022
+ parts
44023
+ };
44024
+ });
44291
44025
  var ConversationExportMetaSchema = external_exports.object({
44292
44026
  exportId: external_exports.string(),
44293
44027
  sourcePlatform: external_exports.string(),
@@ -44319,7 +44053,7 @@ var ClaudeCodeContentBlockSchema = external_exports.object({ type: external_expo
44319
44053
  "tool_use",
44320
44054
  "tool_result"
44321
44055
  ]) }).passthrough().superRefine((val, ctx) => {
44322
- const typedVal = val;
44056
+ const typedVal = toRecord(val);
44323
44057
  if (val.type === "text") {
44324
44058
  if (typeof typedVal.text !== "string") ctx.addIssue({
44325
44059
  code: external_exports.ZodIssueCode.custom,
@@ -44344,6 +44078,32 @@ var ClaudeCodeContentBlockSchema = external_exports.object({ type: external_expo
44344
44078
  message: "tool_result block must have a string tool_use_id field"
44345
44079
  });
44346
44080
  }
44081
+ }).transform((val) => {
44082
+ const record2 = toRecord(val);
44083
+ switch (val.type) {
44084
+ case "text":
44085
+ return {
44086
+ type: "text",
44087
+ text: String(record2.text)
44088
+ };
44089
+ case "tool_use": {
44090
+ const inputVal = record2.input;
44091
+ if (!inputVal || typeof inputVal !== "object") throw new Error("Invalid tool_use: input is not an object");
44092
+ return {
44093
+ type: "tool_use",
44094
+ id: String(record2.id),
44095
+ name: String(record2.name),
44096
+ input: toRecord(inputVal)
44097
+ };
44098
+ }
44099
+ case "tool_result":
44100
+ return {
44101
+ type: "tool_result",
44102
+ tool_use_id: String(record2.tool_use_id),
44103
+ content: record2.content,
44104
+ is_error: typeof record2.is_error === "boolean" ? record2.is_error : void 0
44105
+ };
44106
+ }
44347
44107
  });
44348
44108
  var ClaudeCodeUsageSchema = external_exports.object({
44349
44109
  input_tokens: external_exports.number(),
@@ -44372,6 +44132,18 @@ var ClaudeCodeMessageSchema = external_exports.object({
44372
44132
  costUSD: external_exports.number().optional(),
44373
44133
  durationMs: external_exports.number().optional()
44374
44134
  });
44135
+ var UsageMetadataSchema = external_exports.object({
44136
+ input_tokens: external_exports.number(),
44137
+ output_tokens: external_exports.number(),
44138
+ cache_creation_input_tokens: external_exports.number().optional(),
44139
+ cache_read_input_tokens: external_exports.number().optional()
44140
+ });
44141
+ var EnvironmentContextSchema = external_exports.object({
44142
+ projectName: external_exports.string().optional(),
44143
+ branch: external_exports.string().optional(),
44144
+ hostname: external_exports.string().optional(),
44145
+ repo: external_exports.string().optional()
44146
+ });
44375
44147
  var GitHubPRResponseSchema = external_exports.object({
44376
44148
  number: external_exports.number(),
44377
44149
  html_url: external_exports.string().url(),
@@ -44440,6 +44212,7 @@ var LocalChangesResponseSchema = external_exports.object({
44440
44212
  available: external_exports.literal(true),
44441
44213
  branch: external_exports.string(),
44442
44214
  baseBranch: external_exports.string(),
44215
+ headSha: external_exports.string().optional(),
44443
44216
  staged: external_exports.array(LocalFileChangeSchema),
44444
44217
  unstaged: external_exports.array(LocalFileChangeSchema),
44445
44218
  untracked: external_exports.array(external_exports.string()),
@@ -44530,19 +44303,20 @@ function truncate(text, maxLength) {
44530
44303
  if (cleaned.length <= maxLength) return cleaned;
44531
44304
  return `${cleaned.slice(0, maxLength)}...`;
44532
44305
  }
44533
- var TOOL_NAMES2 = {
44306
+ var TOOL_NAMES = {
44534
44307
  ADD_ARTIFACT: "add_artifact",
44535
- ADD_PR_REVIEW_COMMENT: "add_pr_review_comment",
44536
44308
  COMPLETE_TASK: "complete_task",
44537
- CREATE_PLAN: "create_plan",
44309
+ CREATE_TASK: "create_task",
44538
44310
  EXECUTE_CODE: "execute_code",
44539
44311
  LINK_PR: "link_pr",
44540
- READ_PLAN: "read_plan",
44312
+ POST_UPDATE: "post_update",
44313
+ READ_DIFF_COMMENTS: "read_diff_comments",
44314
+ READ_TASK: "read_task",
44541
44315
  REGENERATE_SESSION_TOKEN: "regenerate_session_token",
44542
44316
  REQUEST_USER_INPUT: "request_user_input",
44543
44317
  SETUP_REVIEW_NOTIFICATION: "setup_review_notification",
44544
44318
  UPDATE_BLOCK_CONTENT: "update_block_content",
44545
- UPDATE_PLAN: "update_plan"
44319
+ UPDATE_TASK: "update_task"
44546
44320
  };
44547
44321
  var PlanIdSchema = external_exports.object({ planId: external_exports.string().min(1) });
44548
44322
  var PlanStatusResponseSchema = external_exports.object({ status: external_exports.string() });
@@ -44551,13 +44325,6 @@ var SubscriptionClientIdSchema = external_exports.object({
44551
44325
  planId: external_exports.string().min(1),
44552
44326
  clientId: external_exports.string().min(1)
44553
44327
  });
44554
- var ChangeTypeSchema = external_exports.enum([
44555
- "status",
44556
- "comments",
44557
- "resolved",
44558
- "content",
44559
- "artifacts"
44560
- ]);
44561
44328
  var ChangeSchema = external_exports.object({
44562
44329
  type: ChangeTypeSchema,
44563
44330
  timestamp: external_exports.number(),
@@ -44595,6 +44362,12 @@ var ImportConversationResponseSchema = external_exports.discriminatedUnion("succ
44595
44362
  success: external_exports.literal(false),
44596
44363
  error: external_exports.string()
44597
44364
  })]);
44365
+ var MachineInfoResponseSchema = external_exports.object({
44366
+ machineId: external_exports.string(),
44367
+ machineName: external_exports.string(),
44368
+ ownerId: external_exports.string(),
44369
+ cwd: external_exports.string()
44370
+ });
44598
44371
  var t = initTRPC.context().create({ allowOutsideOfServer: true });
44599
44372
  var router = t.router;
44600
44373
  var publicProcedure = t.procedure;
@@ -44627,7 +44400,7 @@ var hookRouter = router({
44627
44400
  })).output(external_exports.object({
44628
44401
  approved: external_exports.boolean(),
44629
44402
  feedback: external_exports.string().optional(),
44630
- deliverables: external_exports.array(external_exports.any()).optional(),
44403
+ deliverables: external_exports.array(DeliverableSchema).optional(),
44631
44404
  reviewComment: external_exports.string().optional(),
44632
44405
  reviewedBy: external_exports.string().optional(),
44633
44406
  status: external_exports.string().optional()
@@ -44674,7 +44447,7 @@ var planRouter = router({
44674
44447
  message: "Plan not found"
44675
44448
  });
44676
44449
  const origin = metadata.origin;
44677
- const cwd = origin?.platform === "claude-code" ? origin.cwd : void 0;
44450
+ const cwd = origin?.platform === "claude-code" || origin?.platform === "unknown" ? origin.cwd : void 0;
44678
44451
  if (!cwd) return {
44679
44452
  available: false,
44680
44453
  reason: "no_cwd",
@@ -44695,12 +44468,15 @@ var planRouter = router({
44695
44468
  message: "Plan not found"
44696
44469
  });
44697
44470
  const origin = metadata.origin;
44698
- const cwd = origin?.platform === "claude-code" ? origin.cwd : void 0;
44471
+ const cwd = origin?.platform === "claude-code" || origin?.platform === "unknown" ? origin.cwd : void 0;
44699
44472
  if (!cwd) return {
44700
44473
  content: null,
44701
44474
  error: "No working directory available"
44702
44475
  };
44703
44476
  return ctx.getFileContent(cwd, input.filePath);
44477
+ }),
44478
+ getMachineInfo: publicProcedure.input(PlanIdSchema).output(MachineInfoResponseSchema).query(async ({ ctx }) => {
44479
+ return ctx.getMachineInfo();
44704
44480
  })
44705
44481
  });
44706
44482
  var subscriptionRouter = router({
@@ -44708,7 +44484,7 @@ var subscriptionRouter = router({
44708
44484
  const { planId, subscribe, windowMs, maxWindowMs, threshold } = input;
44709
44485
  return { clientId: ctx.getPlanStore().createSubscription({
44710
44486
  planId,
44711
- subscribe: subscribe || ["status"],
44487
+ subscribe: subscribe ?? ["status"],
44712
44488
  windowMs: windowMs ?? 5e3,
44713
44489
  maxWindowMs: maxWindowMs ?? 3e4,
44714
44490
  threshold: threshold ?? 1
@@ -44734,6 +44510,547 @@ var appRouter = router({
44734
44510
  subscription: subscriptionRouter,
44735
44511
  conversation: conversationRouter
44736
44512
  });
44513
+ function isBuffer(value) {
44514
+ return Buffer.isBuffer(value);
44515
+ }
44516
+
44517
+ // ../../packages/shared/dist/instructions/index.mjs
44518
+ init_cjs_shims();
44519
+ var TOOL_NAMES2 = {
44520
+ ADD_ARTIFACT: "add_artifact",
44521
+ COMPLETE_TASK: "complete_task",
44522
+ CREATE_TASK: "create_task",
44523
+ EXECUTE_CODE: "execute_code",
44524
+ LINK_PR: "link_pr",
44525
+ POST_UPDATE: "post_update",
44526
+ READ_DIFF_COMMENTS: "read_diff_comments",
44527
+ READ_TASK: "read_task",
44528
+ REGENERATE_SESSION_TOKEN: "regenerate_session_token",
44529
+ REQUEST_USER_INPUT: "request_user_input",
44530
+ SETUP_REVIEW_NOTIFICATION: "setup_review_notification",
44531
+ UPDATE_BLOCK_CONTENT: "update_block_content",
44532
+ UPDATE_TASK: "update_task"
44533
+ };
44534
+ var CRITICAL_USAGE_SECTION = `## CRITICAL: When to Use Shipyard
44535
+
44536
+ \u26A0\uFE0F **MANDATORY USAGE RULE:**
44537
+
44538
+ If Shipyard is available, you MUST use it for:
44539
+ - Creating implementation tasks
44540
+ - Tracking work that needs human review
44541
+ - Documenting proof of work (screenshots, videos, test results)
44542
+ - ANY user request involving "task", "track", "verify", or "prove"
44543
+
44544
+ **DO NOT:**
44545
+ - Create tasks manually in chat or as markdown files
44546
+ - Write implementation docs yourself when Shipyard is available
44547
+ - Suggest alternatives to Shipyard for trackable work
44548
+ - Overthink whether to use it - WHEN IN DOUBT, USE SHIPYARD
44549
+
44550
+ **Decision Tree:**
44551
+ \`\`\`
44552
+ Need to create/track/verify work?
44553
+ \u2502
44554
+ \u25BC
44555
+ Shipyard available?
44556
+ \u2502 \u2502
44557
+ YES NO
44558
+ \u2502 \u2502
44559
+ \u25BC \u25BC
44560
+ USE IT Manual approach
44561
+ NOW (tell user why)
44562
+ \`\`\``;
44563
+ var DELIVERABLES_SECTION = `## What are Deliverables?
44564
+
44565
+ Deliverables are measurable outcomes you can **prove** with artifacts (screenshots, videos, test results).
44566
+
44567
+ **Good deliverables (provable):**
44568
+ \`\`\`
44569
+ - [ ] Screenshot of working login page {#deliverable}
44570
+ - [ ] Video showing drag-and-drop feature {#deliverable}
44571
+ - [ ] Test results showing all tests pass {#deliverable}
44572
+ \`\`\`
44573
+
44574
+ **Bad deliverables (not provable - these are tasks, not deliverables):**
44575
+ \`\`\`
44576
+ - [ ] Implement getUserMedia API \u2190 Implementation detail, not provable
44577
+ - [ ] Add error handling \u2190 Can't capture this with an artifact
44578
+ - [ ] Refactor authentication \u2190 Too vague, no visual proof
44579
+ \`\`\`
44580
+
44581
+ **Rule:** If you can't screenshot/record/export it, it's not a deliverable.`;
44582
+ var ARTIFACT_TYPES_SECTION = `## Artifact Types
44583
+
44584
+ | Type | Use For | File Formats |
44585
+ |------|---------|--------------|
44586
+ | \`html\` | Test results, code reviews, reports, terminal output | .html |
44587
+ | \`image\` | UI screenshots, visual proof, error states | .png, .jpg, .webp |
44588
+ | \`video\` | Complex flows, interactions, animations | .mp4, .webm |
44589
+
44590
+ **Note:** HTML is the primary format for most artifacts. Use it for test results, coverage reports, code reviews, and any text-based output. Only use \`image\` for actual UI screenshots and \`video\` for multi-step flows.`;
44591
+ var TIPS_SECTION = `## Tips for Effective Use
44592
+
44593
+ 1. **Define deliverables first** - Decide what proves success before coding
44594
+ 2. **Capture during work** - Take screenshots as you implement, not after
44595
+ 3. **Be specific** - "Login page with error state" beats "Screenshot"
44596
+ 4. **Link every artifact** - Always set \`deliverableId\` for auto-completion
44597
+ 5. **Check feedback** - Read reviewer comments and iterate`;
44598
+ var WHEN_NOT_TO_USE_SECTION = `## When NOT to Use Shipyard
44599
+
44600
+ Skip Shipyard for:
44601
+ - Quick answers or research questions (no artifacts to capture)
44602
+ - Internal refactoring with no visible output
44603
+ - Tasks where proof adds no value (trivial fixes)
44604
+ - Exploration or debugging sessions
44605
+ - Pure documentation without implementation`;
44606
+ var USER_INPUT_SECTION = `## Human-Agent Communication
44607
+
44608
+ **\`requestUserInput()\` inside \`${TOOL_NAMES2.EXECUTE_CODE}\` is THE primary way to communicate with humans during active work.**
44609
+
44610
+ Shipyard is the central hub where humans manage AI agents. When you need to ask a question, get clarification, or request a decision - use \`requestUserInput()\`. The human is already in the browser viewing your task. That's where conversations should happen.
44611
+
44612
+ ### Best Practice: Return the Response Value
44613
+
44614
+ **Always RETURN the response in your execute_code result** for clean, structured output:
44615
+
44616
+ \`\`\`typescript
44617
+ const result = await requestUserInput({
44618
+ message: "Which framework?",
44619
+ type: "choice",
44620
+ options: ["React", "Vue", "Angular"]
44621
+ });
44622
+
44623
+ return {
44624
+ userDecision: result.response,
44625
+ timestamp: Date.now()
44626
+ };
44627
+ // Clean, structured - appears once in the final output
44628
+ \`\`\`
44629
+
44630
+ Avoid \`console.log()\` for response values - it clutters output and isn't structured. Use console.log only for debugging intermediate steps.
44631
+
44632
+ ### Why Use requestUserInput()
44633
+
44634
+ - **Context:** The human sees your question alongside the task, artifacts, and comments
44635
+ - **History:** All exchanges are logged in the task's activity feed
44636
+ - **Continuity:** The conversation stays attached to the work, not scattered across chat windows
44637
+ - **Flexibility:** 8 input types, multi-question forms, "Other" escape hatch for custom answers
44638
+
44639
+ ### Replace Platform Tools
44640
+
44641
+ **ALWAYS prefer \`requestUserInput()\` over platform-specific tools:**
44642
+
44643
+ | Platform | DON'T Use | Use Instead |
44644
+ |----------|-----------|-------------|
44645
+ | Claude Code | \`AskUserQuestion\` | \`requestUserInput()\` |
44646
+ | Cursor | Built-in prompts | \`requestUserInput()\` |
44647
+ | Windsurf | Native dialogs | \`requestUserInput()\` |
44648
+ | Claude Desktop | Chat questions | \`requestUserInput()\` |
44649
+
44650
+ ### Two Modes: Multi-step vs Multi-form
44651
+
44652
+ Choose based on whether questions depend on each other:
44653
+
44654
+ **Multi-step (dependencies):** Chain calls when later questions depend on earlier answers
44655
+ \`\`\`typescript
44656
+ // First ask about database...
44657
+ const dbResult = await requestUserInput({
44658
+ message: "Which database?",
44659
+ type: "choice",
44660
+ options: ["PostgreSQL", "SQLite", "MongoDB"]
44661
+ });
44662
+
44663
+ // ...then ask port based on the choice
44664
+ const portResult = await requestUserInput({
44665
+ message: \\\`Port for \\\${dbResult.response}?\\\`,
44666
+ type: "number",
44667
+ min: 1000,
44668
+ max: 65535
44669
+ });
44670
+
44671
+ // Return both responses in structured format
44672
+ return { database: dbResult.response, port: portResult.response };
44673
+ \`\`\`
44674
+
44675
+ **Multi-form (independent):** Single call for unrelated questions
44676
+ \`\`\`typescript
44677
+ const config = await requestUserInput({
44678
+ questions: [
44679
+ { message: "Project name?", type: "text" },
44680
+ { message: "Use TypeScript?", type: "confirm" },
44681
+ { message: "License?", type: "choice", options: ["MIT", "Apache-2.0"] }
44682
+ ],
44683
+ timeout: 600
44684
+ });
44685
+ // Return responses in structured format
44686
+ return { config: config.response };
44687
+ \`\`\`
44688
+
44689
+ ### When to Ask
44690
+
44691
+ Use \`requestUserInput()\` when you need:
44692
+ - Clarification on requirements ("Which auth provider?")
44693
+ - Decisions that affect implementation ("PostgreSQL or SQLite?")
44694
+ - Confirmation before destructive actions ("Delete this file?")
44695
+ - User preferences ("Rate this approach 1-5")
44696
+ - Any information you can't infer from context`;
44697
+ var TROUBLESHOOTING_SECTION = `## Troubleshooting
44698
+
44699
+ **Browser doesn't open:** Check MCP server is running and accessible.
44700
+
44701
+ **Upload fails:** Verify file path exists. For GitHub uploads, check \`GITHUB_TOKEN\` has repo write access.
44702
+
44703
+ **No auto-complete:** Ensure every deliverable has an artifact with matching \`deliverableId\`.
44704
+
44705
+ **Task not syncing:** Check WebSocket connection to registry server.
44706
+
44707
+ **Input request times out:** User may not have seen it or needs more time. Default timeout is 30 minutes. Try again with a longer timeout or rephrase the question.
44708
+
44709
+ **Input request declined:** User clicked "Decline." Rephrase your question, proceed with a reasonable default, or use a different approach.
44710
+
44711
+ **No response to input:** Check if browser is connected to the task. User may have closed the browser window.`;
44712
+ var COMMON_INSTRUCTIONS = [
44713
+ CRITICAL_USAGE_SECTION,
44714
+ USER_INPUT_SECTION,
44715
+ DELIVERABLES_SECTION,
44716
+ ARTIFACT_TYPES_SECTION,
44717
+ TIPS_SECTION,
44718
+ WHEN_NOT_TO_USE_SECTION,
44719
+ TROUBLESHOOTING_SECTION
44720
+ ].join("\n\n");
44721
+ var CLAUDE_CODE_HEADER = `[SHIPYARD] Collaborative task management with human review & proof-of-work tracking.`;
44722
+ var TASK_MODE_WORKFLOW = `## How to Use (Claude Code with Hooks)
44723
+
44724
+ You have the **full Shipyard experience** with automatic hooks. Use native task mode:
44725
+
44726
+ ### Workflow
44727
+
44728
+ 1. **Enter task mode** (Shift+Tab) \u2192 Browser opens with live task automatically
44729
+ 2. **Write your task** with \`{#deliverable}\` markers for provable outcomes
44730
+ 3. **Exit task mode** \u2192 Hook **BLOCKS** until human approves or requests changes
44731
+ 4. **On approval** \u2192 You automatically receive: taskId, sessionToken, deliverable IDs
44732
+ 5. **Do the work** \u2192 Take screenshots/videos as you implement
44733
+ 6. **Upload artifacts** \u2192 \`${TOOL_NAMES2.ADD_ARTIFACT}(filePath, deliverableId)\` for each deliverable
44734
+ 7. **Auto-complete** \u2192 When all deliverables have artifacts, task completes with snapshot URL
44735
+
44736
+ ### After Approval
44737
+
44738
+ You only need ONE tool: \`${TOOL_NAMES2.ADD_ARTIFACT}\`
44739
+
44740
+ The hook automatically injects everything you need (taskId, sessionToken, deliverables).
44741
+ Just call \`${TOOL_NAMES2.ADD_ARTIFACT}\` with the file path and deliverable ID.
44742
+
44743
+ \`\`\`typescript
44744
+ /**
44745
+ * Example: After approval, you'll have these in context
44746
+ * taskId: "abc123"
44747
+ * sessionToken: "xyz..."
44748
+ * deliverables: [{ id: "del_xxx", text: "Screenshot of login" }]
44749
+ */
44750
+
44751
+ await addArtifact({
44752
+ taskId,
44753
+ sessionToken,
44754
+ type: 'image',
44755
+ filename: 'login-page.png',
44756
+ source: 'file',
44757
+ filePath: '/tmp/screenshot.png',
44758
+ deliverableId: deliverables[0].id
44759
+ });
44760
+ \`\`\`
44761
+
44762
+ When the last deliverable gets an artifact, the task auto-completes and returns a snapshot URL.`;
44763
+ var POSTING_UPDATES_SECTION = `## Posting Progress Updates
44764
+
44765
+ For long-running tasks, keep reviewers informed with periodic updates:
44766
+
44767
+ \`\`\`typescript
44768
+ await postUpdate({
44769
+ taskId,
44770
+ sessionToken,
44771
+ message: "Starting work on authentication module"
44772
+ });
44773
+ \`\`\`
44774
+
44775
+ **When to post updates:**
44776
+ - After completing a significant milestone
44777
+ - When switching focus to a different part of the task
44778
+ - If you've been working for a while without visible output
44779
+ - When you encounter something interesting or unexpected
44780
+
44781
+ Think about what a human watching your work would want to know.`;
44782
+ var IMPORTANT_NOTES = `## Important Notes for Claude Code
44783
+
44784
+ - **DO NOT call \`createTask()\` directly** - The hook handles task creation when you enter task mode
44785
+ - **DO NOT use the Shipyard skill** - The hook provides everything you need
44786
+ - **DO NOT poll for approval** - The hook blocks automatically until human decides
44787
+ - **DO use task mode** for ANY work that needs tracking, verification, or human review
44788
+ - **DO use \`requestUserInput()\`** inside \`${TOOL_NAMES2.EXECUTE_CODE}\` instead of \`AskUserQuestion\` - The human is in the browser viewing your task, questions should appear there`;
44789
+ var CLAUDE_CODE_INSTRUCTIONS = [
44790
+ CLAUDE_CODE_HEADER,
44791
+ "",
44792
+ CRITICAL_USAGE_SECTION,
44793
+ "",
44794
+ USER_INPUT_SECTION,
44795
+ "",
44796
+ TASK_MODE_WORKFLOW,
44797
+ "",
44798
+ POSTING_UPDATES_SECTION,
44799
+ "",
44800
+ DELIVERABLES_SECTION,
44801
+ "",
44802
+ ARTIFACT_TYPES_SECTION,
44803
+ "",
44804
+ IMPORTANT_NOTES,
44805
+ "",
44806
+ TIPS_SECTION,
44807
+ "",
44808
+ WHEN_NOT_TO_USE_SECTION,
44809
+ "",
44810
+ TROUBLESHOOTING_SECTION
44811
+ ].join("\n");
44812
+ var MCP_DIRECT_HEADER = `# Shipyard: Your Agent Management Hub
44813
+
44814
+ > **Shipyard is the central interface where humans manage AI agents.** Tasks, artifacts, feedback, and communication all happen here.
44815
+
44816
+ Shipyard turns invisible agent work into reviewable, verifiable tasks. Instead of trusting that code was written correctly, reviewers see screenshots, videos, and test results as proof.
44817
+
44818
+ **Key principle:** When you're working in Shipyard, ALL human-agent communication should happen through \`requestUserInput()\` inside \`${TOOL_NAMES2.EXECUTE_CODE}\`. The human is already in the browser viewing your task - that's where they expect to interact with you.`;
44819
+ var MCP_TOOLS_OVERVIEW = `## Available MCP Tools
44820
+
44821
+ | Tool | Purpose |
44822
+ |------|---------|
44823
+ | \`${TOOL_NAMES2.EXECUTE_CODE}\` | Run TypeScript that calls ALL Shipyard APIs including \`requestUserInput()\` |
44824
+
44825
+ ### requestUserInput(): Your Direct Line to the Human
44826
+
44827
+ This is how you talk to humans during active work. Don't use your platform's built-in question tools (AskUserQuestion, etc.) - use \`requestUserInput()\` inside \`${TOOL_NAMES2.EXECUTE_CODE}\` instead. The human is in the browser viewing your task, and that's where they expect to see your questions.
44828
+
44829
+ All Shipyard operations (createTask, addArtifact, requestUserInput, etc.) are available inside \`${TOOL_NAMES2.EXECUTE_CODE}\`.`;
44830
+ var MCP_WORKFLOW = `## Workflow (MCP Direct)
44831
+
44832
+ ### Step 1: Create Task
44833
+
44834
+ \`\`\`typescript
44835
+ const task = await createTask({
44836
+ title: "Add user authentication",
44837
+ content: \`
44838
+ ## Deliverables
44839
+ - [ ] Screenshot of login page {#deliverable}
44840
+ - [ ] Screenshot of error handling {#deliverable}
44841
+
44842
+ ## Implementation
44843
+ 1. Create login form component
44844
+ 2. Add validation
44845
+ 3. Connect to auth API
44846
+ \`
44847
+ });
44848
+
44849
+ const { taskId, sessionToken, deliverables, monitoringScript } = task;
44850
+ /** deliverables = [{ id: "del_xxx", text: "Screenshot of login page" }, ...] */
44851
+ \`\`\`
44852
+
44853
+ ### Step 2: Wait for Approval
44854
+
44855
+ For platforms without hooks, run the monitoring script in the background:
44856
+
44857
+ \`\`\`bash
44858
+ # The monitoringScript polls for approval status
44859
+ # Run it in background while you wait
44860
+ bash <(echo "$monitoringScript") &
44861
+ \`\`\`
44862
+
44863
+ Or poll manually:
44864
+
44865
+ \`\`\`typescript
44866
+ const status = await readTask(taskId, sessionToken);
44867
+ if (status.status === "in_progress") {
44868
+ /** Approved! Proceed with work */
44869
+ }
44870
+ if (status.status === "changes_requested") {
44871
+ /** Read feedback, make changes */
44872
+ }
44873
+ \`\`\`
44874
+
44875
+ ### Step 3: Do the Work
44876
+
44877
+ Implement the feature, taking screenshots/recordings as you go.
44878
+
44879
+ ### Step 4: Upload Artifacts
44880
+
44881
+ \`\`\`typescript
44882
+ await addArtifact({
44883
+ taskId,
44884
+ sessionToken,
44885
+ type: 'image',
44886
+ filename: 'login-page.png',
44887
+ source: 'file',
44888
+ filePath: '/path/to/screenshot.png',
44889
+ deliverableId: deliverables[0].id
44890
+ });
44891
+
44892
+ const result = await addArtifact({
44893
+ taskId,
44894
+ sessionToken,
44895
+ type: 'image',
44896
+ filename: 'error-handling.png',
44897
+ source: 'file',
44898
+ filePath: '/path/to/error.png',
44899
+ deliverableId: deliverables[1].id
44900
+ });
44901
+
44902
+ /** Auto-complete triggers when ALL deliverables have artifacts */
44903
+ if (result.allDeliverablesComplete) {
44904
+ console.log('Done!', result.snapshotUrl);
44905
+ }
44906
+ \`\`\``;
44907
+ var API_REFERENCE = `## API Reference (inside execute_code)
44908
+
44909
+ ### createTask(options)
44910
+
44911
+ Creates a new task and opens it in the browser.
44912
+
44913
+ **Parameters:**
44914
+ - \`title\` (string) - Task title
44915
+ - \`content\` (string) - Markdown content with \`{#deliverable}\` markers
44916
+ - \`repo\` (string, optional) - GitHub repo for artifact storage
44917
+ - \`prNumber\` (number, optional) - PR number to link
44918
+
44919
+ **Returns:** \`{ taskId, sessionToken, url, deliverables, monitoringScript }\`
44920
+
44921
+ ### readTask(taskId, sessionToken, options?)
44922
+
44923
+ Reads current task state.
44924
+
44925
+ **Parameters:**
44926
+ - \`taskId\` (string) - Task ID
44927
+ - \`sessionToken\` (string) - Session token from createTask
44928
+ - \`options.includeAnnotations\` (boolean) - Include reviewer comments
44929
+
44930
+ **Returns:** \`{ content, status, title, deliverables }\`
44931
+
44932
+ ### addArtifact(options)
44933
+
44934
+ Uploads proof-of-work artifact.
44935
+
44936
+ **Parameters:**
44937
+ - \`taskId\` (string) - Task ID
44938
+ - \`sessionToken\` (string) - Session token
44939
+ - \`type\` ('html' | 'image' | 'video')
44940
+ - \`filename\` (string) - File name
44941
+ - \`source\` ('file' | 'url' | 'base64')
44942
+ - \`filePath\` (string) - Local file path (when source='file')
44943
+ - \`deliverableId\` (string, optional) - Links artifact to deliverable
44944
+
44945
+ **Returns:** \`{ artifactId, url, allDeliverablesComplete, snapshotUrl? }\`
44946
+
44947
+ ### requestUserInput(options)
44948
+
44949
+ **THE primary human-agent communication channel.** Asks user a question via browser modal.
44950
+
44951
+ **IMPORTANT: Always RETURN the response value in your execute_code result.**
44952
+
44953
+ \u2705 **RECOMMENDED (primary pattern):**
44954
+ \`\`\`typescript
44955
+ const result = await requestUserInput({ message: "Which database?", type: "choice", options: ["PostgreSQL", "SQLite"] });
44956
+ return { userChoice: result.response, status: result.status };
44957
+ // Clean, structured output appears once in the final result
44958
+ \`\`\`
44959
+
44960
+ \u26A0\uFE0F **AVOID (use only for debugging):**
44961
+ \`\`\`typescript
44962
+ console.log(\\\`User chose: \\\${result.response}\\\`);
44963
+ // Clutters output, not structured
44964
+ \`\`\`
44965
+
44966
+ **Two modes - choose based on dependencies:**
44967
+
44968
+ **Multi-step (dependencies):** Chain calls when later questions depend on earlier answers
44969
+ \`\`\`typescript
44970
+ const db = await requestUserInput({ message: "Database?", type: "choice", options: ["PostgreSQL", "SQLite"] });
44971
+ const port = await requestUserInput({ message: \\\`Port for \\\${db.response}?\\\`, type: "number" });
44972
+ return { database: db.response, port: port.response };
44973
+ \`\`\`
44974
+
44975
+ **Multi-form (independent):** Single call for unrelated questions
44976
+ \`\`\`typescript
44977
+ const config = await requestUserInput({
44978
+ questions: [
44979
+ { message: "Project name?", type: "text" },
44980
+ { message: "Use TypeScript?", type: "confirm" }
44981
+ ]
44982
+ });
44983
+ return { config: config.response };
44984
+ \`\`\`
44985
+
44986
+ **Parameters (single-question mode):**
44987
+ - \`message\` (string) - Question to ask
44988
+ - \`type\` (string) - Input type (see below)
44989
+ - \`options\` (string[], for 'choice') - Available choices
44990
+ - \`timeout\` (number, optional) - Timeout in seconds
44991
+ - Type-specific parameters (min, max, format, etc.)
44992
+
44993
+ **Parameters (multi-question mode):**
44994
+ - \`questions\` (array) - Array of 1-10 questions (8 recommended)
44995
+ - \`timeout\` (number, optional) - Timeout in seconds
44996
+
44997
+ **Returns:** \`{ success, response?, status }\`
44998
+
44999
+ **Supported types (8 total):**
45000
+ 1. \`text\` - Single-line text
45001
+ 2. \`multiline\` - Multi-line text area
45002
+ 3. \`choice\` - Radio/checkbox/dropdown (auto-adds "Other" option)
45003
+ 4. \`confirm\` - Yes/No buttons
45004
+ 5. \`number\` - Numeric input with validation
45005
+ 6. \`email\` - Email validation
45006
+ 7. \`date\` - Date picker with range
45007
+ 8. \`rating\` - Scale rating`;
45008
+ var HANDLING_FEEDBACK = `## Handling Reviewer Feedback
45009
+
45010
+ \`\`\`typescript
45011
+ const status = await readTask(taskId, sessionToken, {
45012
+ includeAnnotations: true
45013
+ });
45014
+
45015
+ if (status.status === "changes_requested") {
45016
+ /** Read the content for inline comments */
45017
+ console.log(status.content);
45018
+
45019
+ /**
45020
+ * Make changes based on feedback
45021
+ * Upload new artifacts
45022
+ * Task will transition back to pending_review
45023
+ */
45024
+ }
45025
+ \`\`\``;
45026
+ var MCP_DIRECT_INSTRUCTIONS = [
45027
+ MCP_DIRECT_HEADER,
45028
+ "",
45029
+ CRITICAL_USAGE_SECTION,
45030
+ "",
45031
+ USER_INPUT_SECTION,
45032
+ "",
45033
+ MCP_TOOLS_OVERVIEW,
45034
+ "",
45035
+ MCP_WORKFLOW,
45036
+ "",
45037
+ DELIVERABLES_SECTION,
45038
+ "",
45039
+ ARTIFACT_TYPES_SECTION,
45040
+ "",
45041
+ API_REFERENCE,
45042
+ "",
45043
+ HANDLING_FEEDBACK,
45044
+ "",
45045
+ TIPS_SECTION,
45046
+ "",
45047
+ WHEN_NOT_TO_USE_SECTION,
45048
+ "",
45049
+ TROUBLESHOOTING_SECTION
45050
+ ].join("\n");
45051
+
45052
+ // src/adapters/claude-code.ts
45053
+ init_cjs_shims();
44737
45054
 
44738
45055
  // src/constants.ts
44739
45056
  init_cjs_shims();
@@ -44835,11 +45152,11 @@ function handlePreToolUse(input) {
44835
45152
  if (toolName === CLAUDE_TOOL_NAMES.ASK_USER_QUESTION) {
44836
45153
  logger.info(
44837
45154
  { toolName },
44838
- "Blocking AskUserQuestion - redirecting to request_user_input MCP tool"
45155
+ "Blocking AskUserQuestion - redirecting to requestUserInput() in execute_code"
44839
45156
  );
44840
45157
  return {
44841
45158
  type: "tool_deny",
44842
- reason: `BLOCKED: Use the ${TOOL_NAMES2.REQUEST_USER_INPUT} MCP tool instead. The human is in the browser viewing your plan - that's where they expect to interact with you. See the tool description for input types and parameters.`
45159
+ reason: `BLOCKED: Use requestUserInput() inside ${TOOL_NAMES.EXECUTE_CODE} instead. The human is in the browser viewing your plan - that's where they expect to interact with you. See the execute_code tool description for input types and parameters.`
44843
45160
  };
44844
45161
  }
44845
45162
  return { type: "passthrough" };
@@ -46203,7 +46520,7 @@ async function retryWithBackoff(fn, maxAttempts = 3, baseDelay = 1e3) {
46203
46520
  try {
46204
46521
  return await fn();
46205
46522
  } catch (err) {
46206
- lastError = err;
46523
+ lastError = err instanceof Error ? err : new Error(String(err));
46207
46524
  if (attempt < maxAttempts - 1) {
46208
46525
  const delay = attempt === 0 ? 0 : baseDelay * 2 ** (attempt - 1);
46209
46526
  logger.debug(
@@ -46236,9 +46553,9 @@ async function getRegistryUrl() {
46236
46553
  logger.debug({ port }, "Found registry server (with retry)");
46237
46554
  return url2;
46238
46555
  } catch (err) {
46239
- const error48 = err;
46556
+ const errorMessage = err instanceof Error ? err.message : String(err);
46240
46557
  logger.debug(
46241
- { port, error: error48.message },
46558
+ { port, error: errorMessage },
46242
46559
  "Failed to connect to registry port after retries"
46243
46560
  );
46244
46561
  }
@@ -46407,7 +46724,49 @@ var webConfig = loadEnv(schema3);
46407
46724
  init_cjs_shims();
46408
46725
 
46409
46726
  // src/core/review-status.ts
46410
- async function waitForReviewDecision(planId, _wsUrl) {
46727
+ function buildApprovalMessage(prefix, deliverableCount, reviewComment) {
46728
+ const countText = `${deliverableCount} deliverable${deliverableCount === 1 ? "" : "s"}`;
46729
+ const feedbackText = reviewComment ? `
46730
+
46731
+ Reviewer comment: ${reviewComment}` : "";
46732
+ return `${prefix} You have ${countText}. Use add_artifact(filePath, deliverableId) to upload proof-of-work.${feedbackText}`;
46733
+ }
46734
+ async function handleApproval(planId, decision, messagePrefix) {
46735
+ const sessionToken = generateSessionToken();
46736
+ const sessionTokenHash = hashSessionToken(sessionToken);
46737
+ const deliverableCount = (decision.deliverables ?? []).length;
46738
+ logger.info({ planId }, "Generating session token for approved plan");
46739
+ try {
46740
+ const tokenResult = await setSessionToken(planId, sessionTokenHash);
46741
+ const url2 = tokenResult.url;
46742
+ logger.info(
46743
+ { planId, url: url2, deliverableCount },
46744
+ "Session token set and stored by server with deliverables"
46745
+ );
46746
+ return {
46747
+ allow: true,
46748
+ message: buildApprovalMessage(messagePrefix, deliverableCount, decision.reviewComment),
46749
+ planId,
46750
+ sessionToken,
46751
+ url: url2
46752
+ };
46753
+ } catch (err) {
46754
+ logger.error({ err, planId }, "Failed to set session token, approving without it");
46755
+ return {
46756
+ allow: true,
46757
+ message: `${messagePrefix.replace("!", "")} (session token unavailable)`,
46758
+ planId
46759
+ };
46760
+ }
46761
+ }
46762
+ function handleRejection(planId, decision) {
46763
+ return {
46764
+ allow: false,
46765
+ message: decision.reviewComment || "Changes requested",
46766
+ planId
46767
+ };
46768
+ }
46769
+ async function waitForReviewDecision(planId) {
46411
46770
  logger.info({ planId }, "Waiting for approval via server endpoint");
46412
46771
  const result = await waitForApproval(planId, planId);
46413
46772
  logger.info({ planId, approved: result.approved }, "Received approval decision from server");
@@ -46427,12 +46786,10 @@ async function handleUpdatedPlanReview(sessionId, planId, planContent, _originMe
46427
46786
  );
46428
46787
  logger.info({ planId }, "Syncing updated plan content");
46429
46788
  try {
46430
- await updatePlanContent(planId, {
46431
- content: planContent
46432
- });
46789
+ await updatePlanContent(planId, { content: planContent });
46433
46790
  } catch (err) {
46434
- const error48 = err;
46435
- if (error48.message?.includes("404")) {
46791
+ const errorMessage = err instanceof Error ? err.message : String(err);
46792
+ if (errorMessage?.includes("404")) {
46436
46793
  logger.warn(
46437
46794
  { planId, sessionId },
46438
46795
  "Plan not found (404), creating new plan with updated content"
@@ -46446,116 +46803,93 @@ async function handleUpdatedPlanReview(sessionId, planId, planContent, _originMe
46446
46803
  { planId, url: createPlanWebUrl(baseUrl, planId) },
46447
46804
  "Content synced, browser already open. Waiting for server approval..."
46448
46805
  );
46449
- const decision = await waitForReviewDecision(planId, "");
46806
+ const decision = await waitForReviewDecision(planId);
46450
46807
  logger.info({ planId, approved: decision.approved }, "Decision received via Y.Doc");
46451
46808
  if (decision.approved) {
46452
- const sessionToken = generateSessionToken();
46453
- const sessionTokenHash = hashSessionToken(sessionToken);
46454
- logger.info({ planId }, "Generating new session token for re-approved plan");
46455
- try {
46456
- const tokenResult = await setSessionToken(planId, sessionTokenHash);
46457
- const url2 = tokenResult.url;
46458
- const deliverableCount = (decision.deliverables ?? []).length;
46459
- logger.info(
46460
- { planId, url: url2, deliverableCount },
46461
- "Session token set and stored by server with updated content hash"
46462
- );
46463
- const reviewFeedback = decision.reviewComment ? `
46809
+ return handleApproval(planId, decision, "Plan re-approved with updates!");
46810
+ }
46811
+ logger.debug({ planId }, "Changes requested - server will manage state cleanup");
46812
+ return handleRejection(planId, decision);
46813
+ }
46814
+ async function handleNewPlanCreation(sessionId, planContent, originMetadata) {
46815
+ logger.info(
46816
+ { sessionId, contentLength: planContent.length },
46817
+ "Creating plan from ExitPlanMode (blocking mode)"
46818
+ );
46819
+ const result = await createPlan({
46820
+ sessionId,
46821
+ agentType: DEFAULT_AGENT_TYPE,
46822
+ metadata: {
46823
+ source: "ExitPlanMode",
46824
+ ...originMetadata
46825
+ }
46826
+ });
46827
+ const planId = result.planId;
46828
+ logger.info({ planId }, "Syncing plan content");
46829
+ await updatePlanContent(planId, { content: planContent });
46830
+ logger.info(
46831
+ { planId, url: result.url },
46832
+ "Plan created and synced, browser opened. Waiting for server approval..."
46833
+ );
46834
+ const decision = await waitForReviewDecision(planId);
46835
+ logger.info({ planId, approved: decision.approved }, "Decision received via Y.Doc");
46836
+ if (decision.approved) {
46837
+ return handleApproval(planId, decision, "Plan approved!");
46838
+ }
46839
+ logger.debug({ sessionId }, "Changes requested - server will manage state cleanup");
46840
+ return handleRejection(planId, decision);
46841
+ }
46842
+ function buildStatusResponse(status, planId, baseUrl) {
46843
+ switch (status.status) {
46844
+ case "changes_requested":
46845
+ return {
46846
+ allow: false,
46847
+ message: formatFeedbackMessage(status.feedback),
46848
+ feedback: status.feedback,
46849
+ planId
46850
+ };
46851
+ case "pending_review":
46852
+ return {
46853
+ allow: false,
46854
+ message: `Plan is pending review.
46464
46855
 
46465
- Reviewer comment: ${decision.reviewComment}` : "";
46856
+ Open: ${createPlanWebUrl(baseUrl, planId)}`,
46857
+ planId
46858
+ };
46859
+ case "draft":
46860
+ return {
46861
+ allow: false,
46862
+ message: `Plan is still in draft.
46863
+
46864
+ Submit for review at: ${createPlanWebUrl(baseUrl, planId)}`,
46865
+ planId
46866
+ };
46867
+ case "in_progress":
46466
46868
  return {
46467
46869
  allow: true,
46468
- message: `Plan re-approved with updates! You have ${deliverableCount} deliverable${deliverableCount === 1 ? "" : "s"}. Use add_artifact(filePath, deliverableId) to upload proof-of-work.${reviewFeedback}`,
46469
- planId,
46470
- sessionToken,
46471
- url: url2
46870
+ message: "Plan approved. Work is in progress. Use add_artifact(filePath, deliverableId) to upload deliverable proofs.",
46871
+ planId
46472
46872
  };
46473
- } catch (err) {
46474
- logger.error({ err, planId }, "Failed to set session token, but plan was approved");
46873
+ case "completed":
46475
46874
  return {
46476
46875
  allow: true,
46477
- message: "Updated plan approved (session token unavailable)",
46876
+ message: `Task completed by ${status.completedBy}`,
46478
46877
  planId
46479
46878
  };
46480
- }
46879
+ default:
46880
+ assertNever2(status);
46481
46881
  }
46482
- logger.debug({ planId }, "Changes requested - server will manage state cleanup");
46483
- return {
46484
- allow: false,
46485
- message: decision.reviewComment || "Changes requested",
46486
- planId
46487
- };
46488
46882
  }
46489
46883
  async function checkReviewStatus(sessionId, planContent, originMetadata) {
46490
46884
  const state = await getSessionContext(sessionId);
46491
- let planId;
46492
46885
  if (!state.found && planContent) {
46493
- logger.info(
46494
- { sessionId, contentLength: planContent.length, hasState: !!state },
46495
- "Creating plan from ExitPlanMode (blocking mode)"
46496
- );
46497
- const result = await createPlan({
46498
- sessionId,
46499
- agentType: DEFAULT_AGENT_TYPE,
46500
- metadata: {
46501
- source: "ExitPlanMode",
46502
- ...originMetadata
46503
- }
46504
- });
46505
- planId = result.planId;
46506
- logger.info({ planId }, "Syncing plan content");
46507
- await updatePlanContent(planId, {
46508
- content: planContent
46509
- });
46510
- logger.info(
46511
- { planId, url: result.url },
46512
- "Plan created and synced, browser opened. Waiting for server approval..."
46513
- );
46514
- const decision = await waitForReviewDecision(planId, "");
46515
- logger.info({ planId, approved: decision.approved }, "Decision received via Y.Doc");
46516
- if (decision.approved) {
46517
- const sessionToken = generateSessionToken();
46518
- const sessionTokenHash = hashSessionToken(sessionToken);
46519
- logger.info({ planId }, "Generating session token for approved plan");
46520
- try {
46521
- const tokenResult = await setSessionToken(planId, sessionTokenHash);
46522
- const url2 = tokenResult.url;
46523
- const deliverableCount = (decision.deliverables ?? []).length;
46524
- logger.info(
46525
- { planId, url: url2, deliverableCount },
46526
- "Session token set and stored by server with deliverables"
46527
- );
46528
- const reviewFeedback = decision.reviewComment ? `
46529
-
46530
- Reviewer comment: ${decision.reviewComment}` : "";
46531
- return {
46532
- allow: true,
46533
- message: `Plan approved! You have ${deliverableCount} deliverable${deliverableCount === 1 ? "" : "s"}. Use add_artifact(filePath, deliverableId) to upload proof-of-work.${reviewFeedback}`,
46534
- planId,
46535
- sessionToken,
46536
- url: url2
46537
- };
46538
- } catch (err) {
46539
- logger.error({ err, planId }, "Failed to set session token, approving without it");
46540
- return {
46541
- allow: true,
46542
- message: "Plan approved, but session token unavailable. You may need to refresh the plan in the browser. Check ~/.shipyard/server-debug.log for details.",
46543
- planId
46544
- };
46545
- }
46546
- }
46547
- logger.debug({ sessionId }, "Changes requested - server will manage state cleanup");
46548
- return {
46549
- allow: false,
46550
- message: decision.reviewComment || "Changes requested",
46551
- planId
46552
- };
46886
+ return handleNewPlanCreation(sessionId, planContent, originMetadata);
46553
46887
  }
46554
46888
  if (!state.found) {
46555
46889
  logger.info({ sessionId }, "No session state or plan content, allowing exit");
46556
46890
  return { allow: true };
46557
46891
  }
46558
- if ((!state || !state.planId) && planContent) {
46892
+ if (!state.planId && planContent) {
46559
46893
  logger.error(
46560
46894
  { sessionId, hasPlanContent: !!planContent, hasState: !!state, statePlanId: state?.planId },
46561
46895
  "Unreachable state: plan content exists but no session state"
@@ -46568,7 +46902,7 @@ Reviewer comment: ${decision.reviewComment}` : "";
46568
46902
  if (!state.planId) {
46569
46903
  throw new Error("Unreachable: state.planId should exist at this point");
46570
46904
  }
46571
- planId = state.planId;
46905
+ const planId = state.planId;
46572
46906
  if (planContent) {
46573
46907
  logger.info({ planId }, "Plan content provided, triggering re-review");
46574
46908
  return await handleUpdatedPlanReview(sessionId, planId, planContent, originMetadata);
@@ -46587,46 +46921,7 @@ Reviewer comment: ${decision.reviewComment}` : "";
46587
46921
  }
46588
46922
  logger.info({ sessionId, planId, status: status.status }, "Review status retrieved");
46589
46923
  const baseUrl = webConfig.SHIPYARD_WEB_URL;
46590
- switch (status.status) {
46591
- case "changes_requested":
46592
- return {
46593
- allow: false,
46594
- message: formatFeedbackMessage(status.feedback),
46595
- feedback: status.feedback,
46596
- planId
46597
- };
46598
- case "pending_review":
46599
- return {
46600
- allow: false,
46601
- message: `Plan is pending review.
46602
-
46603
- Open: ${createPlanWebUrl(baseUrl, planId)}`,
46604
- planId
46605
- };
46606
- case "draft":
46607
- return {
46608
- allow: false,
46609
- message: `Plan is still in draft.
46610
-
46611
- Submit for review at: ${createPlanWebUrl(baseUrl, planId)}`,
46612
- planId
46613
- };
46614
- case "in_progress":
46615
- return {
46616
- allow: true,
46617
- message: "Plan approved. Work is in progress. Use add_artifact(filePath, deliverableId) to upload deliverable proofs.",
46618
- planId
46619
- };
46620
- case "completed":
46621
- return {
46622
- allow: true,
46623
- message: `Task completed by ${status.completedBy}`,
46624
- planId
46625
- };
46626
- default: {
46627
- assertNever2(status);
46628
- }
46629
- }
46924
+ return buildStatusResponse(status, planId, baseUrl);
46630
46925
  }
46631
46926
  function formatFeedbackMessage(feedback) {
46632
46927
  if (!feedback?.length) {
@@ -46683,13 +46978,11 @@ async function handlePlanExit(event) {
46683
46978
  try {
46684
46979
  return await checkReviewStatus(event.sessionId, event.planContent, event.metadata);
46685
46980
  } catch (err) {
46686
- const error48 = err;
46687
- logger.error(
46688
- { err: error48, message: error48.message, code: error48.code },
46689
- "Failed to check review status"
46690
- );
46691
- const isConnectionError = error48.code === "ECONNREFUSED" || error48.code === "ECONNRESET" || error48.code === "ETIMEDOUT" || error48.code === "ENOTFOUND" || error48.message?.includes("connect") || error48.message?.includes("timeout") || error48.message?.includes("WebSocket") || error48.message?.includes("not available");
46692
- const message = isConnectionError ? "Cannot connect to Shipyard server. Ensure the Shipyard MCP server is running. Check ~/.shipyard/hook-debug.log for details." : `Review system error: ${error48.message}. Check ~/.shipyard/hook-debug.log for details.`;
46981
+ const errorMessage = err instanceof Error ? err.message : String(err);
46982
+ const errorCode = err instanceof Error && "code" in err && typeof err.code === "string" ? err.code : void 0;
46983
+ logger.error({ err, message: errorMessage, code: errorCode }, "Failed to check review status");
46984
+ const isConnectionError = errorCode === "ECONNREFUSED" || errorCode === "ECONNRESET" || errorCode === "ETIMEDOUT" || errorCode === "ENOTFOUND" || errorMessage?.includes("connect") || errorMessage?.includes("timeout") || errorMessage?.includes("WebSocket") || errorMessage?.includes("not available");
46985
+ const message = isConnectionError ? "Cannot connect to Shipyard server. Ensure the Shipyard MCP server is running. Check ~/.shipyard/hook-debug.log for details." : `Review system error: ${errorMessage}. Check ~/.shipyard/hook-debug.log for details.`;
46693
46986
  return {
46694
46987
  allow: false,
46695
46988
  message
@@ -46760,7 +47053,9 @@ async function processEvent(_adapter, event) {
46760
47053
  async function readStdin() {
46761
47054
  const chunks = [];
46762
47055
  for await (const chunk of process.stdin) {
46763
- chunks.push(chunk);
47056
+ if (isBuffer(chunk)) {
47057
+ chunks.push(chunk);
47058
+ }
46764
47059
  }
46765
47060
  return Buffer.concat(chunks).toString("utf-8");
46766
47061
  }
@@ -46795,13 +47090,14 @@ async function main() {
46795
47090
  process.exit(0);
46796
47091
  } catch (err) {
46797
47092
  logger.error({ err }, "Hook error, failing closed");
47093
+ const errorMessage = err instanceof Error ? err.message : String(err);
46798
47094
  console.log(
46799
47095
  JSON.stringify({
46800
47096
  hookSpecificOutput: {
46801
47097
  hookEventName: "PermissionRequest",
46802
47098
  decision: {
46803
47099
  behavior: "deny",
46804
- message: `Hook error: ${err.message}. Check ~/.shipyard/hook-debug.log for details.`
47100
+ message: `Hook error: ${errorMessage}. Check ~/.shipyard/hook-debug.log for details.`
46805
47101
  }
46806
47102
  }
46807
47103
  })