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

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.
@@ -28488,6 +28488,342 @@ 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 CRITICAL_USAGE_SECTION = `## CRITICAL: When to Use Shipyard
28494
+
28495
+ \u26A0\uFE0F **MANDATORY USAGE RULE:**
28496
+
28497
+ If Shipyard is available, you MUST use it for:
28498
+ - Creating implementation plans
28499
+ - Tracking work that needs human review
28500
+ - Documenting proof of work (screenshots, videos, test results)
28501
+ - ANY user request involving "plan", "track", "verify", or "prove"
28502
+
28503
+ **DO NOT:**
28504
+ - Create plans manually in chat or as markdown files
28505
+ - Write implementation docs yourself when Shipyard is available
28506
+ - Suggest alternatives to Shipyard for trackable work
28507
+ - Overthink whether to use it - WHEN IN DOUBT, USE SHIPYARD
28508
+
28509
+ **Decision Tree:**
28510
+ \`\`\`
28511
+ Need to create/track/verify work?
28512
+ \u2502
28513
+ \u25BC
28514
+ Shipyard available?
28515
+ \u2502 \u2502
28516
+ YES NO
28517
+ \u2502 \u2502
28518
+ \u25BC \u25BC
28519
+ USE IT Manual approach
28520
+ NOW (tell user why)
28521
+ \`\`\``;
28522
+ var DELIVERABLES_SECTION = `## What are Deliverables?
28523
+
28524
+ Deliverables are measurable outcomes you can **prove** with artifacts (screenshots, videos, test results).
28525
+
28526
+ **Good deliverables (provable):**
28527
+ \`\`\`
28528
+ - [ ] Screenshot of working login page {#deliverable}
28529
+ - [ ] Video showing drag-and-drop feature {#deliverable}
28530
+ - [ ] Test results showing all tests pass {#deliverable}
28531
+ \`\`\`
28532
+
28533
+ **Bad deliverables (not provable - these are tasks, not deliverables):**
28534
+ \`\`\`
28535
+ - [ ] Implement getUserMedia API \u2190 Implementation detail, not provable
28536
+ - [ ] Add error handling \u2190 Can't capture this with an artifact
28537
+ - [ ] Refactor authentication \u2190 Too vague, no visual proof
28538
+ \`\`\`
28539
+
28540
+ **Rule:** If you can't screenshot/record/export it, it's not a deliverable.`;
28541
+ var ARTIFACT_TYPES_SECTION = `## Artifact Types
28542
+
28543
+ | Type | Use For | File Formats |
28544
+ |------|---------|--------------|
28545
+ | \`screenshot\` | UI changes, visual proof, error states | .png, .jpg, .webp |
28546
+ | \`video\` | Complex flows, interactions, animations | .mp4, .webm |
28547
+ | \`test_results\` | Test output, coverage reports | .json, .txt, .xml |
28548
+ | \`diff\` | Code changes, before/after comparisons | .diff, .patch |`;
28549
+ var TIPS_SECTION = `## Tips for Effective Use
28550
+
28551
+ 1. **Plan deliverables first** - Decide what proves success before coding
28552
+ 2. **Capture during work** - Take screenshots as you implement, not after
28553
+ 3. **Be specific** - "Login page with error state" beats "Screenshot"
28554
+ 4. **Link every artifact** - Always set \`deliverableId\` for auto-completion
28555
+ 5. **Check feedback** - Read reviewer comments and iterate`;
28556
+ var WHEN_NOT_TO_USE_SECTION = `## When NOT to Use Shipyard
28557
+
28558
+ Skip Shipyard for:
28559
+ - Quick answers or research questions (no artifacts to capture)
28560
+ - Internal refactoring with no visible output
28561
+ - Tasks where proof adds no value (trivial fixes)
28562
+ - Exploration or debugging sessions
28563
+ - Pure documentation without implementation`;
28564
+ var TROUBLESHOOTING_SECTION = `## Troubleshooting
28565
+
28566
+ **Browser doesn't open:** Check MCP server is running and accessible.
28567
+
28568
+ **Upload fails:** Verify file path exists. For GitHub uploads, check \`GITHUB_TOKEN\` has repo write access.
28569
+
28570
+ **No auto-complete:** Ensure every deliverable has an artifact with matching \`deliverableId\`.
28571
+
28572
+ **Plan not syncing:** Check WebSocket connection to registry server.`;
28573
+ var COMMON_INSTRUCTIONS = [
28574
+ CRITICAL_USAGE_SECTION,
28575
+ DELIVERABLES_SECTION,
28576
+ ARTIFACT_TYPES_SECTION,
28577
+ TIPS_SECTION,
28578
+ WHEN_NOT_TO_USE_SECTION,
28579
+ TROUBLESHOOTING_SECTION
28580
+ ].join("\n\n");
28581
+ var CLAUDE_CODE_HEADER = `[SHIPYARD] Collaborative planning with human review & proof-of-work tracking.`;
28582
+ var PLAN_MODE_WORKFLOW = `## How to Use (Claude Code with Hooks)
28583
+
28584
+ You have the **full Shipyard experience** with automatic hooks. Use native plan mode:
28585
+
28586
+ ### Workflow
28587
+
28588
+ 1. **Enter plan mode** (Shift+Tab) \u2192 Browser opens with live plan automatically
28589
+ 2. **Write your plan** with \`{#deliverable}\` markers for provable outcomes
28590
+ 3. **Exit plan mode** \u2192 Hook **BLOCKS** until human approves or requests changes
28591
+ 4. **On approval** \u2192 You automatically receive: planId, sessionToken, deliverable IDs
28592
+ 5. **Do the work** \u2192 Take screenshots/videos as you implement
28593
+ 6. **Upload artifacts** \u2192 \`add_artifact(filePath, deliverableId)\` for each deliverable
28594
+ 7. **Auto-complete** \u2192 When all deliverables have artifacts, task completes with snapshot URL
28595
+
28596
+ ### After Approval
28597
+
28598
+ You only need ONE tool: \`add_artifact\`
28599
+
28600
+ The hook automatically injects everything you need (planId, sessionToken, deliverables).
28601
+ Just call \`add_artifact\` with the file path and deliverable ID.
28602
+
28603
+ \`\`\`typescript
28604
+ // Example: After approval, you'll have these in context
28605
+ // planId: "abc123"
28606
+ // sessionToken: "xyz..."
28607
+ // deliverables: [{ id: "del_xxx", text: "Screenshot of login" }]
28608
+
28609
+ await addArtifact({
28610
+ planId,
28611
+ sessionToken,
28612
+ type: 'screenshot',
28613
+ filename: 'login-page.png',
28614
+ source: 'file',
28615
+ filePath: '/tmp/screenshot.png',
28616
+ deliverableId: deliverables[0].id
28617
+ });
28618
+ \`\`\`
28619
+
28620
+ When the last deliverable gets an artifact, the task auto-completes and returns a snapshot URL.`;
28621
+ var IMPORTANT_NOTES = `## Important Notes for Claude Code
28622
+
28623
+ - **DO NOT call \`createPlan()\` directly** - The hook handles plan creation when you enter plan mode
28624
+ - **DO NOT use the Shipyard skill** - The hook provides everything you need
28625
+ - **DO NOT poll for approval** - The hook blocks automatically until human decides
28626
+ - **DO use plan mode** for ANY work that needs tracking, verification, or human review`;
28627
+ var CLAUDE_CODE_INSTRUCTIONS = [
28628
+ CLAUDE_CODE_HEADER,
28629
+ "",
28630
+ CRITICAL_USAGE_SECTION,
28631
+ "",
28632
+ PLAN_MODE_WORKFLOW,
28633
+ "",
28634
+ DELIVERABLES_SECTION,
28635
+ "",
28636
+ ARTIFACT_TYPES_SECTION,
28637
+ "",
28638
+ IMPORTANT_NOTES,
28639
+ "",
28640
+ TIPS_SECTION,
28641
+ "",
28642
+ WHEN_NOT_TO_USE_SECTION,
28643
+ "",
28644
+ TROUBLESHOOTING_SECTION
28645
+ ].join("\n");
28646
+ var MCP_DIRECT_HEADER = `# Shipyard: Verified Work Tasks
28647
+
28648
+ > **MCP Integration:** Use \`execute_code\` to call Shipyard APIs. This skill teaches the workflow.
28649
+
28650
+ 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.`;
28651
+ var MCP_TOOLS_OVERVIEW = `## Available MCP Tools
28652
+
28653
+ | Tool | Purpose |
28654
+ |------|---------|
28655
+ | \`execute_code\` | Run TypeScript that calls Shipyard APIs (recommended) |
28656
+ | \`request_user_input\` | Ask user questions via browser modal |
28657
+
28658
+ **Preferred approach:** Use \`execute_code\` to chain multiple API calls in one step.`;
28659
+ var MCP_WORKFLOW = `## Workflow (MCP Direct)
28660
+
28661
+ ### Step 1: Create Plan
28662
+
28663
+ \`\`\`typescript
28664
+ const plan = await createPlan({
28665
+ title: "Add user authentication",
28666
+ content: \`
28667
+ ## Deliverables
28668
+ - [ ] Screenshot of login page {#deliverable}
28669
+ - [ ] Screenshot of error handling {#deliverable}
28670
+
28671
+ ## Implementation
28672
+ 1. Create login form component
28673
+ 2. Add validation
28674
+ 3. Connect to auth API
28675
+ \`
28676
+ });
28677
+
28678
+ const { planId, sessionToken, deliverables, monitoringScript } = plan;
28679
+ // deliverables = [{ id: "del_xxx", text: "Screenshot of login page" }, ...]
28680
+ \`\`\`
28681
+
28682
+ ### Step 2: Wait for Approval
28683
+
28684
+ For platforms without hooks, run the monitoring script in the background:
28685
+
28686
+ \`\`\`bash
28687
+ # The monitoringScript polls for approval status
28688
+ # Run it in background while you wait
28689
+ bash <(echo "$monitoringScript") &
28690
+ \`\`\`
28691
+
28692
+ Or poll manually:
28693
+
28694
+ \`\`\`typescript
28695
+ const status = await readPlan(planId, sessionToken);
28696
+ if (status.status === "in_progress") {
28697
+ // Approved! Proceed with work
28698
+ }
28699
+ if (status.status === "changes_requested") {
28700
+ // Read feedback, make changes
28701
+ }
28702
+ \`\`\`
28703
+
28704
+ ### Step 3: Do the Work
28705
+
28706
+ Implement the feature, taking screenshots/recordings as you go.
28707
+
28708
+ ### Step 4: Upload Artifacts
28709
+
28710
+ \`\`\`typescript
28711
+ await addArtifact({
28712
+ planId,
28713
+ sessionToken,
28714
+ type: 'screenshot',
28715
+ filename: 'login-page.png',
28716
+ source: 'file',
28717
+ filePath: '/path/to/screenshot.png',
28718
+ deliverableId: deliverables[0].id // Links to specific deliverable
28719
+ });
28720
+
28721
+ const result = await addArtifact({
28722
+ planId,
28723
+ sessionToken,
28724
+ type: 'screenshot',
28725
+ filename: 'error-handling.png',
28726
+ source: 'file',
28727
+ filePath: '/path/to/error.png',
28728
+ deliverableId: deliverables[1].id
28729
+ });
28730
+
28731
+ // Auto-complete triggers when ALL deliverables have artifacts
28732
+ if (result.allDeliverablesComplete) {
28733
+ console.log('Done!', result.snapshotUrl);
28734
+ }
28735
+ \`\`\``;
28736
+ var API_REFERENCE = `## API Reference
28737
+
28738
+ ### createPlan(options)
28739
+
28740
+ Creates a new plan and opens it in the browser.
28741
+
28742
+ **Parameters:**
28743
+ - \`title\` (string) - Plan title
28744
+ - \`content\` (string) - Markdown content with \`{#deliverable}\` markers
28745
+ - \`repo\` (string, optional) - GitHub repo for artifact storage
28746
+ - \`prNumber\` (number, optional) - PR number to link
28747
+
28748
+ **Returns:** \`{ planId, sessionToken, url, deliverables, monitoringScript }\`
28749
+
28750
+ ### readPlan(planId, sessionToken, options?)
28751
+
28752
+ Reads current plan state.
28753
+
28754
+ **Parameters:**
28755
+ - \`planId\` (string) - Plan ID
28756
+ - \`sessionToken\` (string) - Session token from createPlan
28757
+ - \`options.includeAnnotations\` (boolean) - Include reviewer comments
28758
+
28759
+ **Returns:** \`{ content, status, title, deliverables }\`
28760
+
28761
+ ### addArtifact(options)
28762
+
28763
+ Uploads proof-of-work artifact.
28764
+
28765
+ **Parameters:**
28766
+ - \`planId\` (string) - Plan ID
28767
+ - \`sessionToken\` (string) - Session token
28768
+ - \`type\` ('screenshot' | 'video' | 'test_results' | 'diff')
28769
+ - \`filename\` (string) - File name
28770
+ - \`source\` ('file' | 'url' | 'base64')
28771
+ - \`filePath\` (string) - Local file path (when source='file')
28772
+ - \`deliverableId\` (string, optional) - Links artifact to deliverable
28773
+
28774
+ **Returns:** \`{ artifactId, url, allDeliverablesComplete, snapshotUrl? }\`
28775
+
28776
+ ### requestUserInput(options)
28777
+
28778
+ Asks user a question via browser modal.
28779
+
28780
+ **Parameters:**
28781
+ - \`message\` (string) - Question to ask
28782
+ - \`type\` ('text' | 'choice' | 'confirm' | 'multiline')
28783
+ - \`options\` (string[], for 'choice') - Available choices
28784
+ - \`timeout\` (number, optional) - Timeout in seconds
28785
+
28786
+ **Returns:** \`{ success, response?, status }\``;
28787
+ var HANDLING_FEEDBACK = `## Handling Reviewer Feedback
28788
+
28789
+ \`\`\`typescript
28790
+ const status = await readPlan(planId, sessionToken, {
28791
+ includeAnnotations: true
28792
+ });
28793
+
28794
+ if (status.status === "changes_requested") {
28795
+ // Read the content for inline comments
28796
+ console.log(status.content);
28797
+
28798
+ // Make changes based on feedback
28799
+ // Upload new artifacts
28800
+ // Plan will transition back to pending_review
28801
+ }
28802
+ \`\`\``;
28803
+ var MCP_DIRECT_INSTRUCTIONS = [
28804
+ MCP_DIRECT_HEADER,
28805
+ "",
28806
+ CRITICAL_USAGE_SECTION,
28807
+ "",
28808
+ MCP_TOOLS_OVERVIEW,
28809
+ "",
28810
+ MCP_WORKFLOW,
28811
+ "",
28812
+ DELIVERABLES_SECTION,
28813
+ "",
28814
+ ARTIFACT_TYPES_SECTION,
28815
+ "",
28816
+ API_REFERENCE,
28817
+ "",
28818
+ HANDLING_FEEDBACK,
28819
+ "",
28820
+ TIPS_SECTION,
28821
+ "",
28822
+ WHEN_NOT_TO_USE_SECTION,
28823
+ "",
28824
+ TROUBLESHOOTING_SECTION
28825
+ ].join("\n");
28826
+
28491
28827
  // src/adapters/claude-code.ts
28492
28828
  init_cjs_shims();
28493
28829
 
@@ -43832,6 +44168,42 @@ var InviteRedemptionSchema = external_exports.object({
43832
44168
  redeemedAt: external_exports.number(),
43833
44169
  tokenId: external_exports.string()
43834
44170
  });
44171
+ var GitFileStatusSchema = external_exports.enum([
44172
+ "added",
44173
+ "modified",
44174
+ "deleted",
44175
+ "renamed",
44176
+ "copied",
44177
+ "untracked"
44178
+ ]);
44179
+ var LocalFileChangeSchema = external_exports.object({
44180
+ path: external_exports.string(),
44181
+ status: GitFileStatusSchema,
44182
+ additions: external_exports.number(),
44183
+ deletions: external_exports.number(),
44184
+ patch: external_exports.string().optional()
44185
+ });
44186
+ var LocalChangesResponseSchema = external_exports.object({
44187
+ available: external_exports.literal(true),
44188
+ branch: external_exports.string(),
44189
+ baseBranch: external_exports.string(),
44190
+ staged: external_exports.array(LocalFileChangeSchema),
44191
+ unstaged: external_exports.array(LocalFileChangeSchema),
44192
+ untracked: external_exports.array(external_exports.string()),
44193
+ files: external_exports.array(LocalFileChangeSchema)
44194
+ });
44195
+ var LocalChangesUnavailableReasonSchema = external_exports.enum([
44196
+ "no_cwd",
44197
+ "not_git_repo",
44198
+ "mcp_not_connected",
44199
+ "git_error"
44200
+ ]);
44201
+ var LocalChangesUnavailableSchema = external_exports.object({
44202
+ available: external_exports.literal(false),
44203
+ reason: LocalChangesUnavailableReasonSchema,
44204
+ message: external_exports.string()
44205
+ });
44206
+ var LocalChangesResultSchema = external_exports.discriminatedUnion("available", [LocalChangesResponseSchema, LocalChangesUnavailableSchema]);
43835
44207
  var ConversationExportStartMetaSchema = external_exports.object({
43836
44208
  exportId: external_exports.string(),
43837
44209
  totalChunks: external_exports.number().int().positive(),
@@ -44027,6 +44399,21 @@ var planRouter = router({
44027
44399
  }),
44028
44400
  hasConnections: publicProcedure.input(PlanIdSchema).output(HasConnectionsResponseSchema).query(async ({ input, ctx }) => {
44029
44401
  return { hasConnections: await ctx.getPlanStore().hasActiveConnections(input.planId) };
44402
+ }),
44403
+ getLocalChanges: publicProcedure.input(PlanIdSchema).output(LocalChangesResultSchema).query(async ({ input, ctx }) => {
44404
+ const metadata = getPlanMetadata(await ctx.getOrCreateDoc(input.planId));
44405
+ if (!metadata) throw new TRPCError({
44406
+ code: "NOT_FOUND",
44407
+ message: "Plan not found"
44408
+ });
44409
+ const origin = metadata.origin;
44410
+ const cwd = origin?.platform === "claude-code" ? origin.cwd : void 0;
44411
+ if (!cwd) return {
44412
+ available: false,
44413
+ reason: "no_cwd",
44414
+ message: "Plan has no associated working directory. Only Claude Code plans support local changes."
44415
+ };
44416
+ return ctx.getLocalChanges(cwd);
44030
44417
  })
44031
44418
  });
44032
44419
  var subscriptionRouter = router({
@@ -46094,46 +46481,10 @@ async function readStdin() {
46094
46481
  return Buffer.concat(chunks).toString("utf-8");
46095
46482
  }
46096
46483
  function outputSessionStartContext() {
46097
- const context = `[SHIPYARD] Collaborative planning with human review & proof-of-work tracking.
46098
-
46099
- IMPORTANT: Use native plan mode (Shift+Tab) to create plans. The hook handles everything automatically.
46100
-
46101
- ## What are Deliverables?
46102
-
46103
- Deliverables are measurable outcomes you can prove with artifacts (screenshots, videos, test results).
46104
-
46105
- Good deliverables (provable):
46106
- \`\`\`
46107
- - [ ] Screenshot of working login page {#deliverable}
46108
- - [ ] Video showing feature in action {#deliverable}
46109
- - [ ] Test results showing all tests pass {#deliverable}
46110
- \`\`\`
46111
-
46112
- Bad deliverables (implementation details, not provable):
46113
- \`\`\`
46114
- - [ ] Implement getUserMedia API \u2190 This is a task, not a deliverable
46115
- - [ ] Add error handling \u2190 Can't prove this with an artifact
46116
- \`\`\`
46117
-
46118
- ## Workflow
46119
-
46120
- 1. Enter plan mode (Shift+Tab) \u2192 Browser opens with live plan
46121
- 2. Write plan with {#deliverable} markers for provable outcomes
46122
- 3. Exit plan mode \u2192 Hook BLOCKS until human approves
46123
- 4. On approval \u2192 You receive planId, sessionToken, and deliverable IDs
46124
- 5. Do work \u2192 Take screenshots/videos as you go
46125
- 6. \`add_artifact(filePath, deliverableId)\` for each deliverable
46126
- 7. When all deliverables fulfilled \u2192 Auto-completes with snapshot URL
46127
-
46128
- ## After Approval
46129
-
46130
- You only need ONE tool: \`add_artifact\`
46131
-
46132
- When the last deliverable gets an artifact, the task auto-completes and returns a snapshot URL for your PR.`;
46133
46484
  const hookOutput = {
46134
46485
  hookSpecificOutput: {
46135
46486
  hookEventName: "SessionStart",
46136
- additionalContext: context
46487
+ additionalContext: CLAUDE_CODE_INSTRUCTIONS
46137
46488
  }
46138
46489
  };
46139
46490
  console.log(JSON.stringify(hookOutput));
@@ -2356,6 +2356,42 @@ function getTokenTimeRemaining(expiresAt) {
2356
2356
  formatted: `${minutes}m`
2357
2357
  };
2358
2358
  }
2359
+ var GitFileStatusSchema = z3.enum([
2360
+ "added",
2361
+ "modified",
2362
+ "deleted",
2363
+ "renamed",
2364
+ "copied",
2365
+ "untracked"
2366
+ ]);
2367
+ var LocalFileChangeSchema = z3.object({
2368
+ path: z3.string(),
2369
+ status: GitFileStatusSchema,
2370
+ additions: z3.number(),
2371
+ deletions: z3.number(),
2372
+ patch: z3.string().optional()
2373
+ });
2374
+ var LocalChangesResponseSchema = z3.object({
2375
+ available: z3.literal(true),
2376
+ branch: z3.string(),
2377
+ baseBranch: z3.string(),
2378
+ staged: z3.array(LocalFileChangeSchema),
2379
+ unstaged: z3.array(LocalFileChangeSchema),
2380
+ untracked: z3.array(z3.string()),
2381
+ files: z3.array(LocalFileChangeSchema)
2382
+ });
2383
+ var LocalChangesUnavailableReasonSchema = z3.enum([
2384
+ "no_cwd",
2385
+ "not_git_repo",
2386
+ "mcp_not_connected",
2387
+ "git_error"
2388
+ ]);
2389
+ var LocalChangesUnavailableSchema = z3.object({
2390
+ available: z3.literal(false),
2391
+ reason: LocalChangesUnavailableReasonSchema,
2392
+ message: z3.string()
2393
+ });
2394
+ var LocalChangesResultSchema = z3.discriminatedUnion("available", [LocalChangesResponseSchema, LocalChangesUnavailableSchema]);
2359
2395
  var P2PMessageType = {
2360
2396
  CONVERSATION_EXPORT_START: 240,
2361
2397
  CONVERSATION_CHUNK: 241,
@@ -2763,6 +2799,21 @@ var planRouter = router({
2763
2799
  }),
2764
2800
  hasConnections: publicProcedure.input(PlanIdSchema).output(HasConnectionsResponseSchema).query(async ({ input, ctx }) => {
2765
2801
  return { hasConnections: await ctx.getPlanStore().hasActiveConnections(input.planId) };
2802
+ }),
2803
+ getLocalChanges: publicProcedure.input(PlanIdSchema).output(LocalChangesResultSchema).query(async ({ input, ctx }) => {
2804
+ const metadata = getPlanMetadata(await ctx.getOrCreateDoc(input.planId));
2805
+ if (!metadata) throw new TRPCError({
2806
+ code: "NOT_FOUND",
2807
+ message: "Plan not found"
2808
+ });
2809
+ const origin = metadata.origin;
2810
+ const cwd = origin?.platform === "claude-code" ? origin.cwd : void 0;
2811
+ if (!cwd) return {
2812
+ available: false,
2813
+ reason: "no_cwd",
2814
+ message: "Plan has no associated working directory. Only Claude Code plans support local changes."
2815
+ };
2816
+ return ctx.getLocalChanges(cwd);
2766
2817
  })
2767
2818
  });
2768
2819
  var subscriptionRouter = router({
@@ -2957,6 +3008,12 @@ export {
2957
3008
  parseInviteFromUrl,
2958
3009
  buildInviteUrl,
2959
3010
  getTokenTimeRemaining,
3011
+ GitFileStatusSchema,
3012
+ LocalFileChangeSchema,
3013
+ LocalChangesResponseSchema,
3014
+ LocalChangesUnavailableReasonSchema,
3015
+ LocalChangesUnavailableSchema,
3016
+ LocalChangesResultSchema,
2960
3017
  P2PMessageType,
2961
3018
  ConversationExportStartMetaSchema,
2962
3019
  ChunkMessageSchema,
@@ -3,7 +3,7 @@ import {
3
3
  YDOC_KEYS,
4
4
  createInputRequest,
5
5
  logPlanEvent
6
- } from "./chunk-CYHEHTLS.js";
6
+ } from "./chunk-5V7HUMDU.js";
7
7
  import {
8
8
  logger
9
9
  } from "./chunk-GSGLHRWX.js";
@@ -28,6 +28,7 @@ import {
28
28
  DeliverableSchema,
29
29
  DevinOriginMetadataSchema,
30
30
  GetReviewStatusResponseSchema,
31
+ GitFileStatusSchema,
31
32
  GitHubPRResponseSchema,
32
33
  HasConnectionsResponseSchema,
33
34
  HookApiErrorSchema,
@@ -40,6 +41,11 @@ import {
40
41
  InviteTokenSchema,
41
42
  LinkedPRSchema,
42
43
  LinkedPRStatusValues,
44
+ LocalChangesResponseSchema,
45
+ LocalChangesResultSchema,
46
+ LocalChangesUnavailableReasonSchema,
47
+ LocalChangesUnavailableSchema,
48
+ LocalFileChangeSchema,
43
49
  NON_PLAN_DB_NAMES,
44
50
  OriginMetadataSchema,
45
51
  OriginPlatformValues,
@@ -206,7 +212,7 @@ import {
206
212
  updateLinkedPRStatus,
207
213
  updatePlanIndexViewedBy,
208
214
  validateA2AMessages
209
- } from "./chunk-CYHEHTLS.js";
215
+ } from "./chunk-5V7HUMDU.js";
210
216
  import "./chunk-JSBRDJBE.js";
211
217
  export {
212
218
  A2ADataPartSchema,
@@ -238,6 +244,7 @@ export {
238
244
  DeliverableSchema,
239
245
  DevinOriginMetadataSchema,
240
246
  GetReviewStatusResponseSchema,
247
+ GitFileStatusSchema,
241
248
  GitHubPRResponseSchema,
242
249
  HasConnectionsResponseSchema,
243
250
  HookApiErrorSchema,
@@ -250,6 +257,11 @@ export {
250
257
  InviteTokenSchema,
251
258
  LinkedPRSchema,
252
259
  LinkedPRStatusValues,
260
+ LocalChangesResponseSchema,
261
+ LocalChangesResultSchema,
262
+ LocalChangesUnavailableReasonSchema,
263
+ LocalChangesUnavailableSchema,
264
+ LocalFileChangeSchema,
253
265
  NON_PLAN_DB_NAMES,
254
266
  OriginMetadataSchema,
255
267
  OriginPlatformValues,
@@ -20,7 +20,7 @@ import {
20
20
  } from "./chunk-EBNL5ZX7.js";
21
21
  import {
22
22
  InputRequestManager
23
- } from "./chunk-7ISZ4RKB.js";
23
+ } from "./chunk-ZD2SHV5V.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-CYHEHTLS.js";
75
+ } from "./chunk-5V7HUMDU.js";
76
76
  import {
77
77
  loadEnv,
78
78
  logger
@@ -383,6 +383,247 @@ function attachCRDTValidation(planId, doc) {
383
383
  logger.debug({ planId }, "CRDT validation observers attached");
384
384
  }
385
385
 
386
+ // src/git-local-changes.ts
387
+ import { execSync } from "child_process";
388
+ function getLocalChanges(cwd) {
389
+ try {
390
+ try {
391
+ execSync("git rev-parse --is-inside-work-tree", {
392
+ cwd,
393
+ encoding: "utf-8",
394
+ timeout: 5e3,
395
+ stdio: ["pipe", "pipe", "pipe"]
396
+ });
397
+ } catch {
398
+ return {
399
+ available: false,
400
+ reason: "not_git_repo",
401
+ message: `Directory is not a git repository: ${cwd}`
402
+ };
403
+ }
404
+ let branch;
405
+ try {
406
+ branch = execSync("git rev-parse --abbrev-ref HEAD", {
407
+ cwd,
408
+ encoding: "utf-8",
409
+ timeout: 5e3,
410
+ stdio: ["pipe", "pipe", "pipe"]
411
+ }).trim();
412
+ if (branch === "HEAD") {
413
+ branch = execSync("git rev-parse --short HEAD", {
414
+ cwd,
415
+ encoding: "utf-8",
416
+ timeout: 5e3,
417
+ stdio: ["pipe", "pipe", "pipe"]
418
+ }).trim();
419
+ }
420
+ } catch (error) {
421
+ logger.warn({ error, cwd }, "Could not get current branch");
422
+ branch = "unknown";
423
+ }
424
+ const statusOutput = execSync("git status --porcelain", {
425
+ cwd,
426
+ encoding: "utf-8",
427
+ timeout: 1e4,
428
+ stdio: ["pipe", "pipe", "pipe"]
429
+ });
430
+ const { staged, unstaged, untracked } = parseGitStatus(statusOutput);
431
+ let diffOutput = "";
432
+ try {
433
+ diffOutput = execSync("git diff HEAD", {
434
+ cwd,
435
+ encoding: "utf-8",
436
+ timeout: 3e4,
437
+ maxBuffer: 10 * 1024 * 1024,
438
+ // 10MB
439
+ stdio: ["pipe", "pipe", "pipe"]
440
+ });
441
+ } catch (error) {
442
+ logger.debug({ error, cwd }, "git diff HEAD failed, trying alternatives");
443
+ try {
444
+ diffOutput = execSync("git diff --cached", {
445
+ cwd,
446
+ encoding: "utf-8",
447
+ timeout: 3e4,
448
+ maxBuffer: 10 * 1024 * 1024,
449
+ stdio: ["pipe", "pipe", "pipe"]
450
+ });
451
+ } catch {
452
+ diffOutput = "";
453
+ }
454
+ }
455
+ const files = parseDiffOutput(diffOutput);
456
+ const allPaths = /* @__PURE__ */ new Set([
457
+ ...staged.map((f) => f.path),
458
+ ...unstaged.map((f) => f.path),
459
+ ...files.map((f) => f.path)
460
+ ]);
461
+ const mergedFiles = [];
462
+ for (const path2 of allPaths) {
463
+ const diffFile = files.find((f) => f.path === path2);
464
+ const stagedFile = staged.find((f) => f.path === path2);
465
+ const unstagedFile = unstaged.find((f) => f.path === path2);
466
+ if (diffFile) {
467
+ mergedFiles.push(diffFile);
468
+ } else {
469
+ const status = stagedFile?.status ?? unstagedFile?.status ?? "modified";
470
+ mergedFiles.push({
471
+ path: path2,
472
+ status,
473
+ additions: 0,
474
+ deletions: 0,
475
+ patch: void 0
476
+ });
477
+ }
478
+ }
479
+ mergedFiles.sort((a, b) => a.path.localeCompare(b.path));
480
+ logger.debug(
481
+ {
482
+ cwd,
483
+ branch,
484
+ stagedCount: staged.length,
485
+ unstagedCount: unstaged.length,
486
+ untrackedCount: untracked.length,
487
+ filesCount: mergedFiles.length
488
+ },
489
+ "Got local changes"
490
+ );
491
+ return {
492
+ available: true,
493
+ branch,
494
+ baseBranch: "HEAD",
495
+ staged,
496
+ unstaged,
497
+ untracked,
498
+ files: mergedFiles
499
+ };
500
+ } catch (error) {
501
+ const message = error instanceof Error ? error.message : "Unknown error";
502
+ logger.error({ error, cwd }, "Failed to get local changes");
503
+ return {
504
+ available: false,
505
+ reason: "git_error",
506
+ message: `Git error: ${message}`
507
+ };
508
+ }
509
+ }
510
+ function parseGitStatus(output) {
511
+ const staged = [];
512
+ const unstaged = [];
513
+ const untracked = [];
514
+ for (const line of output.split("\n")) {
515
+ if (!line || line.length < 3) continue;
516
+ const x = line[0];
517
+ const y = line[1];
518
+ let path2 = line.slice(3);
519
+ if (path2.includes(" -> ")) {
520
+ path2 = path2.split(" -> ")[1] ?? path2;
521
+ }
522
+ if (x === "?" && y === "?") {
523
+ untracked.push(path2);
524
+ continue;
525
+ }
526
+ if (x === "!" && y === "!") {
527
+ continue;
528
+ }
529
+ if (x && x !== " " && x !== "?") {
530
+ staged.push({
531
+ path: path2,
532
+ status: parseStatusChar(x),
533
+ additions: 0,
534
+ // We get this from diff output
535
+ deletions: 0
536
+ });
537
+ }
538
+ if (y && y !== " " && y !== "?") {
539
+ unstaged.push({
540
+ path: path2,
541
+ status: parseStatusChar(y),
542
+ additions: 0,
543
+ deletions: 0
544
+ });
545
+ }
546
+ }
547
+ return { staged, unstaged, untracked };
548
+ }
549
+ function parseStatusChar(char) {
550
+ switch (char) {
551
+ case "A":
552
+ return "added";
553
+ case "M":
554
+ return "modified";
555
+ case "D":
556
+ return "deleted";
557
+ case "R":
558
+ return "renamed";
559
+ case "C":
560
+ return "copied";
561
+ case "U":
562
+ return "modified";
563
+ // Unmerged, treat as modified
564
+ default:
565
+ return "modified";
566
+ }
567
+ }
568
+ function parseDiffOutput(diff) {
569
+ const files = [];
570
+ if (!diff.trim()) {
571
+ return files;
572
+ }
573
+ const fileDiffs = diff.split(/(?=diff --git )/);
574
+ for (const fileDiff of fileDiffs) {
575
+ if (!fileDiff.trim()) continue;
576
+ const headerMatch = fileDiff.match(/^diff --git a\/(.+?) b\/(.+)/m);
577
+ if (!headerMatch) continue;
578
+ const path2 = headerMatch[2] ?? headerMatch[1];
579
+ if (!path2) continue;
580
+ if (fileDiff.includes("Binary files")) {
581
+ files.push({
582
+ path: path2,
583
+ status: detectStatus(fileDiff),
584
+ additions: 0,
585
+ deletions: 0,
586
+ patch: void 0
587
+ });
588
+ continue;
589
+ }
590
+ let additions = 0;
591
+ let deletions = 0;
592
+ for (const line of fileDiff.split("\n")) {
593
+ if (line.startsWith("+") && !line.startsWith("+++")) {
594
+ additions++;
595
+ } else if (line.startsWith("-") && !line.startsWith("---")) {
596
+ deletions++;
597
+ }
598
+ }
599
+ const patchStart = fileDiff.indexOf("@@");
600
+ const patch = patchStart >= 0 ? fileDiff.slice(patchStart) : void 0;
601
+ files.push({
602
+ path: path2,
603
+ status: detectStatus(fileDiff),
604
+ additions,
605
+ deletions,
606
+ patch
607
+ });
608
+ }
609
+ return files;
610
+ }
611
+ function detectStatus(fileDiff) {
612
+ if (fileDiff.includes("new file mode")) {
613
+ return "added";
614
+ }
615
+ if (fileDiff.includes("deleted file mode")) {
616
+ return "deleted";
617
+ }
618
+ if (fileDiff.includes("rename from")) {
619
+ return "renamed";
620
+ }
621
+ if (fileDiff.includes("copy from")) {
622
+ return "copied";
623
+ }
624
+ return "modified";
625
+ }
626
+
386
627
  // src/github-artifacts.ts
387
628
  import { Octokit } from "@octokit/rest";
388
629
  var ARTIFACTS_BRANCH = "plan-artifacts";
@@ -2123,7 +2364,8 @@ function createContext() {
2123
2364
  getPlanStore: createPlanStore,
2124
2365
  logger,
2125
2366
  hookHandlers: createHookHandlers(),
2126
- conversationHandlers: createConversationHandlers()
2367
+ conversationHandlers: createConversationHandlers(),
2368
+ getLocalChanges
2127
2369
  };
2128
2370
  }
2129
2371
  function createApp() {
@@ -2474,7 +2716,7 @@ import ffmpegInstaller from "@ffmpeg-installer/ffmpeg";
2474
2716
  import { z as z13 } from "zod";
2475
2717
 
2476
2718
  // src/tools/add-artifact.ts
2477
- import { execSync } from "child_process";
2719
+ import { execSync as execSync2 } from "child_process";
2478
2720
  import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
2479
2721
  import { ServerBlockNoteEditor as ServerBlockNoteEditor2 } from "@blocknote/server-util";
2480
2722
  import { nanoid as nanoid4 } from "nanoid";
@@ -3028,7 +3270,7 @@ ${error.message}`
3028
3270
  async function tryAutoLinkPR(ydoc, repo) {
3029
3271
  let branch;
3030
3272
  try {
3031
- branch = execSync("git branch --show-current", {
3273
+ branch = execSync2("git branch --show-current", {
3032
3274
  encoding: "utf-8",
3033
3275
  timeout: 5e3,
3034
3276
  stdio: ["pipe", "pipe", "pipe"]
@@ -3204,7 +3446,7 @@ The comment will appear in the Changes tab when viewing this PR.`
3204
3446
  };
3205
3447
 
3206
3448
  // src/tools/complete-task.ts
3207
- import { execSync as execSync2 } from "child_process";
3449
+ import { execSync as execSync3 } from "child_process";
3208
3450
  import { ServerBlockNoteEditor as ServerBlockNoteEditor3 } from "@blocknote/server-util";
3209
3451
  import { z as z5 } from "zod";
3210
3452
  var CompleteTaskInput = z5.object({
@@ -3404,7 +3646,7 @@ linkPR({ planId, sessionToken, prNumber: 42 })
3404
3646
  async function tryAutoLinkPR2(ydoc, repo) {
3405
3647
  let branch;
3406
3648
  try {
3407
- branch = execSync2("git branch --show-current", {
3649
+ branch = execSync3("git branch --show-current", {
3408
3650
  encoding: "utf-8",
3409
3651
  timeout: 5e3,
3410
3652
  stdio: ["pipe", "pipe", "pipe"]
@@ -5408,7 +5650,7 @@ async function setupReviewNotification(planId, pollIntervalSeconds) {
5408
5650
  return { script, fullResponse: text };
5409
5651
  }
5410
5652
  async function requestUserInput(opts) {
5411
- const { InputRequestManager: InputRequestManager2 } = await import("./input-request-manager-YOBSG7RN.js");
5653
+ const { InputRequestManager: InputRequestManager2 } = await import("./input-request-manager-QX7MYHH7.js");
5412
5654
  const ydoc = await getOrCreateDoc3(PLAN_INDEX_DOC_NAME);
5413
5655
  const manager = new InputRequestManager2();
5414
5656
  const params = opts.type === "choice" ? {
@@ -5452,7 +5694,7 @@ async function requestUserInput(opts) {
5452
5694
  };
5453
5695
  }
5454
5696
  async function postActivityUpdate(opts) {
5455
- const { logPlanEvent: logPlanEvent2 } = await import("./dist-HCVQNHBC.js");
5697
+ const { logPlanEvent: logPlanEvent2 } = await import("./dist-2W7US5HB.js");
5456
5698
  const { getGitHubUsername: getGitHubUsername2 } = await import("./server-identity-LSZ4CZRK.js");
5457
5699
  const { nanoid: nanoid8 } = await import("nanoid");
5458
5700
  const doc = await getOrCreateDoc3(opts.planId);
@@ -5475,7 +5717,7 @@ async function postActivityUpdate(opts) {
5475
5717
  return { success: true, eventId, requestId };
5476
5718
  }
5477
5719
  async function resolveActivityRequest(opts) {
5478
- const { logPlanEvent: logPlanEvent2, getPlanEvents } = await import("./dist-HCVQNHBC.js");
5720
+ const { logPlanEvent: logPlanEvent2, getPlanEvents } = await import("./dist-2W7US5HB.js");
5479
5721
  const { getGitHubUsername: getGitHubUsername2 } = await import("./server-identity-LSZ4CZRK.js");
5480
5722
  const doc = await getOrCreateDoc3(opts.planId);
5481
5723
  const actorName = await getGitHubUsername2();
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  InputRequestManager
3
- } from "./chunk-7ISZ4RKB.js";
4
- import "./chunk-CYHEHTLS.js";
3
+ } from "./chunk-ZD2SHV5V.js";
4
+ import "./chunk-5V7HUMDU.js";
5
5
  import "./chunk-GSGLHRWX.js";
6
6
  import "./chunk-JSBRDJBE.js";
7
7
  export {
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@schoolai/shipyard-mcp",
3
- "version": "0.2.2-next.489",
3
+ "version": "0.2.2-next.493",
4
4
  "description": "Shipyard MCP server and CLI tools for distributed planning with CRDTs",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "mcp-server-shipyard": "./apps/server/dist/index.js",
8
- "shipyard-hook": "./apps/hook/dist/index.js"
8
+ "shipyard-hook": "./apps/hook/dist/index.cjs"
9
9
  },
10
10
  "files": [
11
11
  "apps/server/dist",
@@ -19,7 +19,7 @@
19
19
  "import": "./apps/server/dist/index.js"
20
20
  },
21
21
  "./hook": {
22
- "import": "./apps/hook/dist/index.js"
22
+ "require": "./apps/hook/dist/index.cjs"
23
23
  }
24
24
  },
25
25
  "engines": {