@schoolai/shipyard-mcp 0.3.2-next.518 → 0.3.2-next.523

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.
@@ -499,7 +499,10 @@ var CursorOriginMetadataSchema = z.object({
499
499
  conversationId: z.string(),
500
500
  generationId: z.string().optional()
501
501
  });
502
- var UnknownOriginMetadataSchema = z.object({ platform: z.literal("unknown") });
502
+ var UnknownOriginMetadataSchema = z.object({
503
+ platform: z.literal("unknown"),
504
+ cwd: z.string()
505
+ });
503
506
  var OriginMetadataSchema = z.discriminatedUnion("platform", [
504
507
  ClaudeCodeOriginMetadataSchema,
505
508
  DevinOriginMetadataSchema,
@@ -700,7 +703,19 @@ var PlanEventSchema = z.discriminatedUnion("type", [
700
703
  data: z.object({
701
704
  requestId: z.string(),
702
705
  response: z.unknown(),
703
- answeredBy: z.string()
706
+ answeredBy: z.string(),
707
+ requestMessage: z.string().optional(),
708
+ requestType: z.enum([
709
+ "text",
710
+ "multiline",
711
+ "choice",
712
+ "confirm",
713
+ "number",
714
+ "email",
715
+ "date",
716
+ "rating",
717
+ "multi"
718
+ ]).optional()
704
719
  })
705
720
  }),
706
721
  PlanEventBaseSchema.extend({
@@ -832,6 +847,18 @@ var PRReviewCommentSchema = z.object({
832
847
  createdAt: z.number(),
833
848
  resolved: z.boolean().optional()
834
849
  });
850
+ var LocalDiffCommentSchema = z.object({
851
+ id: z.string(),
852
+ type: z.literal("local"),
853
+ path: z.string(),
854
+ line: z.number(),
855
+ body: z.string(),
856
+ author: z.string(),
857
+ createdAt: z.number(),
858
+ baseRef: z.string(),
859
+ resolved: z.boolean().optional(),
860
+ lineContentHash: z.string().optional()
861
+ });
835
862
  function createLinkedPR(params) {
836
863
  const linkedPR = {
837
864
  ...params,
@@ -898,7 +925,7 @@ function createHandedOffConversationVersion(params) {
898
925
  return ConversationVersionSchema.parse(version);
899
926
  }
900
927
 
901
- // ../../packages/schema/dist/yjs-helpers-3DNyE4lF.mjs
928
+ // ../../packages/schema/dist/yjs-helpers-DzEyLz-f.mjs
902
929
  import { z as z2 } from "zod";
903
930
  import { nanoid as nanoid2 } from "nanoid";
904
931
  import * as Y from "yjs";
@@ -1209,7 +1236,7 @@ var ChoiceQuestionSchema = QuestionBaseSchema.extend({
1209
1236
  placeholder: z2.string().optional()
1210
1237
  });
1211
1238
  var ConfirmQuestionSchema = QuestionBaseSchema.extend({ type: z2.literal("confirm") });
1212
- var NumberQuestionSchema = QuestionBaseSchema.extend({
1239
+ var NumberQuestionBaseSchema = QuestionBaseSchema.extend({
1213
1240
  type: z2.literal("number"),
1214
1241
  min: z2.number().optional(),
1215
1242
  max: z2.number().optional(),
@@ -1219,17 +1246,17 @@ var NumberQuestionSchema = QuestionBaseSchema.extend({
1219
1246
  "currency",
1220
1247
  "percentage"
1221
1248
  ]).optional()
1222
- }).refine((data) => data.min === void 0 || data.max === void 0 || data.min <= data.max, { message: "min must be <= max" });
1249
+ });
1223
1250
  var EmailQuestionSchema = QuestionBaseSchema.extend({
1224
1251
  type: z2.literal("email"),
1225
1252
  domain: z2.string().optional()
1226
1253
  });
1227
- var DateQuestionSchema = QuestionBaseSchema.extend({
1254
+ var DateQuestionBaseSchema = QuestionBaseSchema.extend({
1228
1255
  type: z2.literal("date"),
1229
1256
  min: z2.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").optional(),
1230
1257
  max: z2.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").optional()
1231
- }).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" });
1232
- var RatingQuestionSchema = QuestionBaseSchema.extend({
1258
+ });
1259
+ var RatingQuestionBaseSchema = QuestionBaseSchema.extend({
1233
1260
  type: z2.literal("rating"),
1234
1261
  min: z2.number().int().optional(),
1235
1262
  max: z2.number().int().optional(),
@@ -1242,20 +1269,58 @@ var RatingQuestionSchema = QuestionBaseSchema.extend({
1242
1269
  low: z2.string().optional(),
1243
1270
  high: z2.string().optional()
1244
1271
  }).optional()
1245
- }).refine((data) => {
1246
- if (data.min === void 0 || data.max === void 0) return true;
1247
- return data.min <= data.max && data.max - data.min <= 20;
1248
- }, { message: "Rating scale must have min <= max and at most 20 items" });
1272
+ });
1249
1273
  var QuestionSchema = z2.discriminatedUnion("type", [
1250
1274
  TextQuestionSchema,
1251
1275
  MultilineQuestionSchema,
1252
1276
  ChoiceQuestionSchema,
1253
1277
  ConfirmQuestionSchema,
1254
- NumberQuestionSchema,
1278
+ NumberQuestionBaseSchema,
1255
1279
  EmailQuestionSchema,
1256
- DateQuestionSchema,
1257
- RatingQuestionSchema
1280
+ DateQuestionBaseSchema,
1281
+ RatingQuestionBaseSchema
1258
1282
  ]);
1283
+ function validateNumberQuestion(q, index, ctx) {
1284
+ if (q.min !== void 0 && q.max !== void 0 && q.min > q.max) ctx.addIssue({
1285
+ code: z2.ZodIssueCode.custom,
1286
+ message: "min must be <= max",
1287
+ path: ["questions", index]
1288
+ });
1289
+ }
1290
+ function validateDateQuestion(q, index, ctx) {
1291
+ if (q.min !== void 0 && q.max !== void 0 && new Date(q.min) > new Date(q.max)) ctx.addIssue({
1292
+ code: z2.ZodIssueCode.custom,
1293
+ message: "min date must be before or equal to max date",
1294
+ path: ["questions", index]
1295
+ });
1296
+ }
1297
+ function validateRatingQuestion(q, index, ctx) {
1298
+ if (q.min === void 0 || q.max === void 0) return;
1299
+ if (q.min > q.max || q.max - q.min > 20) ctx.addIssue({
1300
+ code: z2.ZodIssueCode.custom,
1301
+ message: "Rating scale must have min <= max and at most 20 items",
1302
+ path: ["questions", index]
1303
+ });
1304
+ }
1305
+ function validateQuestionConstraints(questions, ctx) {
1306
+ for (let i = 0; i < questions.length; i++) {
1307
+ const q = questions[i];
1308
+ if (!q) continue;
1309
+ switch (q.type) {
1310
+ case "number":
1311
+ validateNumberQuestion(q, i, ctx);
1312
+ break;
1313
+ case "date":
1314
+ validateDateQuestion(q, i, ctx);
1315
+ break;
1316
+ case "rating":
1317
+ validateRatingQuestion(q, i, ctx);
1318
+ break;
1319
+ default:
1320
+ break;
1321
+ }
1322
+ }
1323
+ }
1259
1324
  var MultiQuestionInputRequestSchema = z2.object({
1260
1325
  id: z2.string(),
1261
1326
  createdAt: z2.number(),
@@ -1268,6 +1333,8 @@ var MultiQuestionInputRequestSchema = z2.object({
1268
1333
  answeredAt: z2.number().optional(),
1269
1334
  answeredBy: z2.string().optional(),
1270
1335
  isBlocker: z2.boolean().optional()
1336
+ }).superRefine((data, ctx) => {
1337
+ validateQuestionConstraints(data.questions, ctx);
1271
1338
  });
1272
1339
  var AnyInputRequestSchema = z2.union([InputRequestSchema, MultiQuestionInputRequestSchema]);
1273
1340
  function createMultiQuestionInputRequest(params) {
@@ -1298,7 +1365,8 @@ var YDOC_KEYS = {
1298
1365
  PR_REVIEW_COMMENTS: "prReviewComments",
1299
1366
  EVENTS: "events",
1300
1367
  SNAPSHOTS: "snapshots",
1301
- INPUT_REQUESTS: "inputRequests"
1368
+ INPUT_REQUESTS: "inputRequests",
1369
+ LOCAL_DIFF_COMMENTS: "localDiffComments"
1302
1370
  };
1303
1371
  var validKeys = new Set(Object.values(YDOC_KEYS));
1304
1372
  function isValidYDocKey(key) {
@@ -1353,6 +1421,20 @@ function extractMentions(body) {
1353
1421
  function toUnknownArray(array) {
1354
1422
  return array.toJSON();
1355
1423
  }
1424
+ function findInputRequestById(data, requestId) {
1425
+ for (let i = 0; i < data.length; i++) {
1426
+ const item = data[i];
1427
+ if (item && typeof item === "object" && "id" in item && item.id === requestId) {
1428
+ const parsed = AnyInputRequestSchema.safeParse(item);
1429
+ if (parsed.success) return {
1430
+ rawIndex: i,
1431
+ request: parsed.data
1432
+ };
1433
+ return null;
1434
+ }
1435
+ }
1436
+ return null;
1437
+ }
1356
1438
  var VALID_STATUS_TRANSITIONS = {
1357
1439
  draft: [
1358
1440
  "pending_review",
@@ -1430,8 +1512,8 @@ function applyChangesRequestedTransition(map, transition) {
1430
1512
  if (transition.reviewComment !== void 0) map.set("reviewComment", transition.reviewComment);
1431
1513
  }
1432
1514
  function applyInProgressTransition(map, transition) {
1433
- if (transition.reviewedAt !== void 0) map.set("reviewedAt", transition.reviewedAt);
1434
- if (transition.reviewedBy !== void 0) map.set("reviewedBy", transition.reviewedBy);
1515
+ map.set("reviewedAt", transition.reviewedAt);
1516
+ map.set("reviewedBy", transition.reviewedBy);
1435
1517
  if (transition.reviewComment !== void 0) map.set("reviewComment", transition.reviewComment);
1436
1518
  }
1437
1519
  function applyCompletedTransition(map, transition) {
@@ -1757,6 +1839,39 @@ function removePRReviewComment(ydoc, commentId) {
1757
1839
  array.delete(index, 1);
1758
1840
  return true;
1759
1841
  }
1842
+ function getLocalDiffComments(ydoc) {
1843
+ return toUnknownArray(ydoc.getArray(YDOC_KEYS.LOCAL_DIFF_COMMENTS)).map((item) => LocalDiffCommentSchema.safeParse(item)).filter((result) => result.success).map((result) => result.data);
1844
+ }
1845
+ function getLocalDiffCommentsForFile(ydoc, path) {
1846
+ return getLocalDiffComments(ydoc).filter((c) => c.path === path);
1847
+ }
1848
+ function addLocalDiffComment(ydoc, comment, actor) {
1849
+ const validated = LocalDiffCommentSchema.parse(comment);
1850
+ ydoc.transact(() => {
1851
+ ydoc.getArray(YDOC_KEYS.LOCAL_DIFF_COMMENTS).push([validated]);
1852
+ }, actor ? { actor } : void 0);
1853
+ }
1854
+ function resolveLocalDiffComment(ydoc, commentId, resolved) {
1855
+ const array = ydoc.getArray(YDOC_KEYS.LOCAL_DIFF_COMMENTS);
1856
+ const existing = toUnknownArray(array).map((item) => LocalDiffCommentSchema.safeParse(item)).filter((r) => r.success).map((r) => r.data);
1857
+ const index = existing.findIndex((c) => c.id === commentId);
1858
+ if (index === -1) return false;
1859
+ const comment = existing[index];
1860
+ if (!comment) return false;
1861
+ array.delete(index, 1);
1862
+ array.insert(index, [{
1863
+ ...comment,
1864
+ resolved
1865
+ }]);
1866
+ return true;
1867
+ }
1868
+ function removeLocalDiffComment(ydoc, commentId) {
1869
+ const array = ydoc.getArray(YDOC_KEYS.LOCAL_DIFF_COMMENTS);
1870
+ const index = toUnknownArray(array).map((item) => LocalDiffCommentSchema.safeParse(item)).filter((r) => r.success).map((r) => r.data).findIndex((c) => c.id === commentId);
1871
+ if (index === -1) return false;
1872
+ array.delete(index, 1);
1873
+ return true;
1874
+ }
1760
1875
  function extractViewedByFromCrdt(existingViewedBy) {
1761
1876
  const viewedBy = {};
1762
1877
  if (existingViewedBy instanceof Y.Map) {
@@ -1937,17 +2052,12 @@ function unarchivePlan(ydoc, actorId) {
1937
2052
  }
1938
2053
  function answerInputRequest(ydoc, requestId, response, answeredBy) {
1939
2054
  const requestsArray = ydoc.getArray(YDOC_KEYS.INPUT_REQUESTS);
1940
- const requests = toUnknownArray(requestsArray).map((item) => AnyInputRequestSchema.safeParse(item)).filter((r) => r.success).map((r) => r.data);
1941
- const index = requests.findIndex((r) => r.id === requestId);
1942
- if (index === -1) return {
1943
- success: false,
1944
- error: "Request not found"
1945
- };
1946
- const request = requests[index];
1947
- if (!request) return {
2055
+ const found = findInputRequestById(toUnknownArray(requestsArray), requestId);
2056
+ if (!found) return {
1948
2057
  success: false,
1949
2058
  error: "Request not found"
1950
2059
  };
2060
+ const { rawIndex: index, request } = found;
1951
2061
  if (request.status !== "pending") switch (request.status) {
1952
2062
  case "answered":
1953
2063
  return {
@@ -1985,24 +2095,21 @@ function answerInputRequest(ydoc, requestId, response, answeredBy) {
1985
2095
  logPlanEvent(ydoc, "input_request_answered", answeredBy, {
1986
2096
  requestId,
1987
2097
  response,
1988
- answeredBy
2098
+ answeredBy,
2099
+ requestMessage: "message" in request ? request.message : void 0,
2100
+ requestType: request.type
1989
2101
  });
1990
2102
  });
1991
2103
  return { success: true };
1992
2104
  }
1993
2105
  function answerMultiQuestionInputRequest(ydoc, requestId, responses, answeredBy) {
1994
2106
  const requestsArray = ydoc.getArray(YDOC_KEYS.INPUT_REQUESTS);
1995
- const requests = toUnknownArray(requestsArray).map((item) => AnyInputRequestSchema.safeParse(item)).filter((r) => r.success).map((r) => r.data);
1996
- const index = requests.findIndex((r) => r.id === requestId);
1997
- if (index === -1) return {
1998
- success: false,
1999
- error: "Request not found"
2000
- };
2001
- const request = requests[index];
2002
- if (!request) return {
2107
+ const found = findInputRequestById(toUnknownArray(requestsArray), requestId);
2108
+ if (!found) return {
2003
2109
  success: false,
2004
2110
  error: "Request not found"
2005
2111
  };
2112
+ const { rawIndex: index, request } = found;
2006
2113
  if (request.type !== "multi") return {
2007
2114
  success: false,
2008
2115
  error: "Request is not pending"
@@ -2044,24 +2151,20 @@ function answerMultiQuestionInputRequest(ydoc, requestId, responses, answeredBy)
2044
2151
  logPlanEvent(ydoc, "input_request_answered", answeredBy, {
2045
2152
  requestId,
2046
2153
  response: responses,
2047
- answeredBy
2154
+ answeredBy,
2155
+ requestType: "multi"
2048
2156
  });
2049
2157
  });
2050
2158
  return { success: true };
2051
2159
  }
2052
2160
  function cancelInputRequest(ydoc, requestId) {
2053
2161
  const requestsArray = ydoc.getArray(YDOC_KEYS.INPUT_REQUESTS);
2054
- const requests = toUnknownArray(requestsArray).map((item) => AnyInputRequestSchema.safeParse(item)).filter((r) => r.success).map((r) => r.data);
2055
- const index = requests.findIndex((r) => r.id === requestId);
2056
- if (index === -1) return {
2057
- success: false,
2058
- error: "Request not found"
2059
- };
2060
- const request = requests[index];
2061
- if (!request) return {
2162
+ const found = findInputRequestById(toUnknownArray(requestsArray), requestId);
2163
+ if (!found) return {
2062
2164
  success: false,
2063
2165
  error: "Request not found"
2064
2166
  };
2167
+ const { rawIndex: index, request } = found;
2065
2168
  if (request.status !== "pending") return {
2066
2169
  success: false,
2067
2170
  error: `Request is not pending`
@@ -2079,17 +2182,12 @@ function cancelInputRequest(ydoc, requestId) {
2079
2182
  }
2080
2183
  function declineInputRequest(ydoc, requestId) {
2081
2184
  const requestsArray = ydoc.getArray(YDOC_KEYS.INPUT_REQUESTS);
2082
- const requests = toUnknownArray(requestsArray).map((item) => AnyInputRequestSchema.safeParse(item)).filter((r) => r.success).map((r) => r.data);
2083
- const index = requests.findIndex((r) => r.id === requestId);
2084
- if (index === -1) return {
2085
- success: false,
2086
- error: "Request not found"
2087
- };
2088
- const request = requests[index];
2089
- if (!request) return {
2185
+ const found = findInputRequestById(toUnknownArray(requestsArray), requestId);
2186
+ if (!found) return {
2090
2187
  success: false,
2091
2188
  error: "Request not found"
2092
2189
  };
2190
+ const { rawIndex: index, request } = found;
2093
2191
  if (request.status !== "pending") return {
2094
2192
  success: false,
2095
2193
  error: `Request is not pending`
@@ -2739,6 +2837,185 @@ function extractTextFromBlock(block) {
2739
2837
  if (!block.content || !Array.isArray(block.content) || block.content.length === 0) return "";
2740
2838
  return block.content.map((item) => item.text || "").join("").trim();
2741
2839
  }
2840
+ function hashLineContent(content) {
2841
+ let hash = 0;
2842
+ for (let i = 0; i < content.length; i++) {
2843
+ const char = content.charCodeAt(i);
2844
+ hash = (hash << 5) - hash + char;
2845
+ hash = hash & hash;
2846
+ }
2847
+ return hash.toString(16);
2848
+ }
2849
+ function computeCommentStaleness(comment, currentHeadSha, currentLineContent) {
2850
+ if (currentHeadSha && comment.baseRef !== currentHeadSha) return {
2851
+ type: "head_changed",
2852
+ isStale: true
2853
+ };
2854
+ if (currentLineContent !== void 0 && comment.lineContentHash) {
2855
+ if (hashLineContent(currentLineContent) !== comment.lineContentHash) return {
2856
+ type: "content_changed",
2857
+ isStale: true
2858
+ };
2859
+ }
2860
+ return {
2861
+ type: "none",
2862
+ isStale: false
2863
+ };
2864
+ }
2865
+ function isLineContentStale(comment, currentLineContent) {
2866
+ if (!comment.lineContentHash || currentLineContent === void 0) return false;
2867
+ const currentHash = hashLineContent(currentLineContent);
2868
+ return comment.lineContentHash !== currentHash;
2869
+ }
2870
+ function withStalenessInfo(comment, currentHeadSha, currentLineContent) {
2871
+ const staleness = computeCommentStaleness(comment, currentHeadSha, currentLineContent);
2872
+ return {
2873
+ ...comment,
2874
+ isStale: staleness.isStale,
2875
+ stalenessType: staleness.type
2876
+ };
2877
+ }
2878
+ function withStalenessInfoBatch(comments, currentHeadSha, lineContentMap) {
2879
+ return comments.map((comment) => {
2880
+ const key = `${comment.path}:${comment.line}`;
2881
+ const currentLineContent = lineContentMap?.get(key);
2882
+ return withStalenessInfo(comment, currentHeadSha, currentLineContent);
2883
+ });
2884
+ }
2885
+ function isDiffHeader(line) {
2886
+ return line.startsWith("diff --git") || line.startsWith("index ") || line.startsWith("---") || line.startsWith("+++");
2887
+ }
2888
+ function processLineDiff(line, currentLineNumber, filePath, lineContentMap) {
2889
+ const hunkMatch = line.match(/^@@.*\+(\d+)/);
2890
+ if (hunkMatch?.[1]) return Number.parseInt(hunkMatch[1], 10);
2891
+ if (isDiffHeader(line)) return currentLineNumber;
2892
+ if (line.startsWith("-")) return currentLineNumber;
2893
+ if (line.startsWith("+")) {
2894
+ const content = line.slice(1);
2895
+ lineContentMap.set(`${filePath}:${currentLineNumber}`, content);
2896
+ return currentLineNumber + 1;
2897
+ }
2898
+ if (line.startsWith(" ") || line === "") {
2899
+ const content = line.startsWith(" ") ? line.slice(1) : "";
2900
+ lineContentMap.set(`${filePath}:${currentLineNumber}`, content);
2901
+ return currentLineNumber + 1;
2902
+ }
2903
+ return currentLineNumber;
2904
+ }
2905
+ function processFilePatch(file, lineContentMap) {
2906
+ if (!file.patch) return;
2907
+ const lines = file.patch.split("\n");
2908
+ let currentLine = 0;
2909
+ for (const line of lines) currentLine = processLineDiff(line, currentLine, file.path, lineContentMap);
2910
+ }
2911
+ function buildLineContentMap(files) {
2912
+ const lineContentMap = /* @__PURE__ */ new Map();
2913
+ for (const file of files) processFilePatch(file, lineContentMap);
2914
+ return lineContentMap;
2915
+ }
2916
+ function formatStalenessMarker(staleness) {
2917
+ if (!staleness.isStale) return "";
2918
+ switch (staleness.type) {
2919
+ case "head_changed":
2920
+ return "[STALE: HEAD changed]";
2921
+ case "content_changed":
2922
+ return "[STALE: Line content changed]";
2923
+ default:
2924
+ return "";
2925
+ }
2926
+ }
2927
+ function groupBy(items, getKey, sortKeys) {
2928
+ const grouped = /* @__PURE__ */ new Map();
2929
+ for (const item of items) {
2930
+ const key = getKey(item);
2931
+ const existing = grouped.get(key);
2932
+ if (existing) existing.push(item);
2933
+ else grouped.set(key, [item]);
2934
+ }
2935
+ return Array.from(grouped.entries()).sort(([a], [b]) => sortKeys(a, b));
2936
+ }
2937
+ function formatCommentLine(comment) {
2938
+ const markers = [];
2939
+ if (comment.resolved) markers.push("[RESOLVED]");
2940
+ if (comment.staleness) {
2941
+ const stalenessMarker = formatStalenessMarker(comment.staleness);
2942
+ if (stalenessMarker) markers.push(stalenessMarker);
2943
+ }
2944
+ const markerStr = markers.length > 0 ? ` ${markers.join(" ")}` : "";
2945
+ const body = comment.body.replace(/\n/g, " ").trim();
2946
+ return `- Line ${comment.line} (${comment.author})${markerStr}: ${body}`;
2947
+ }
2948
+ function formatFileSection(path, comments) {
2949
+ return `### ${path}
2950
+ ${[...comments].sort((a, b) => a.line - b.line).map(formatCommentLine).join("\n")}`;
2951
+ }
2952
+ function formatLocalCommentsSection(comments) {
2953
+ return `## Local Changes Comments
2954
+
2955
+ ${groupBy(comments, (c) => c.path, (a, b) => a.localeCompare(b)).map(([path, fileComments]) => formatFileSection(path, fileComments)).join("\n\n")}`;
2956
+ }
2957
+ function formatPRCommentsSections(comments) {
2958
+ return groupBy(comments, (c) => c.prNumber, (a, b) => a - b).map(([prNumber, prComments]) => {
2959
+ return `## PR Review Comments (PR #${prNumber})
2960
+
2961
+ ${groupBy(prComments, (c) => c.path, (a, b) => a.localeCompare(b)).map(([path, fileComments]) => formatFileSection(path, fileComments)).join("\n\n")}`;
2962
+ });
2963
+ }
2964
+ function separateCommentTypes(comments) {
2965
+ return {
2966
+ local: comments.filter((c) => "type" in c && c.type === "local"),
2967
+ pr: comments.filter((c) => !("type" in c))
2968
+ };
2969
+ }
2970
+ function formatDiffCommentsForLLM(comments, options = {}) {
2971
+ const { includeResolved = false, currentHeadSha, files } = options;
2972
+ const lineContentMap = options.lineContentMap ?? (files ? buildLineContentMap(files) : void 0);
2973
+ const unresolvedComments = comments.filter((c) => !c.resolved);
2974
+ const resolvedCount = comments.length - unresolvedComments.length;
2975
+ const commentsToShow = includeResolved ? comments : unresolvedComments;
2976
+ if (commentsToShow.length === 0) return resolvedCount > 0 ? `All ${resolvedCount} diff comment(s) have been resolved.` : "";
2977
+ const { local, pr } = separateCommentTypes(commentsToShow);
2978
+ const sections = [];
2979
+ if (local.length > 0) {
2980
+ const localWithStaleness = local.map((comment) => {
2981
+ const key = `${comment.path}:${comment.line}`;
2982
+ const currentLineContent = lineContentMap?.get(key);
2983
+ const staleness = computeCommentStaleness(comment, currentHeadSha, currentLineContent);
2984
+ return {
2985
+ ...comment,
2986
+ staleness
2987
+ };
2988
+ });
2989
+ sections.push(formatLocalCommentsSection(localWithStaleness));
2990
+ }
2991
+ if (pr.length > 0) sections.push(...formatPRCommentsSections(pr));
2992
+ let output = sections.join("\n\n");
2993
+ if (!includeResolved && resolvedCount > 0) output += `
2994
+
2995
+ ---
2996
+ (${resolvedCount} resolved comment(s) not shown)`;
2997
+ return output;
2998
+ }
2999
+ function formatPRCommentsForLLM(comments, options = {}) {
3000
+ return formatDiffCommentsForLLM(comments, options);
3001
+ }
3002
+ function getPRCommentsSummary(comments) {
3003
+ const byFile = /* @__PURE__ */ new Map();
3004
+ let unresolved = 0;
3005
+ let resolved = 0;
3006
+ for (const comment of comments) {
3007
+ if (comment.resolved) resolved++;
3008
+ else unresolved++;
3009
+ const count = byFile.get(comment.path) ?? 0;
3010
+ byFile.set(comment.path, count + 1);
3011
+ }
3012
+ return {
3013
+ total: comments.length,
3014
+ unresolved,
3015
+ resolved,
3016
+ byFile
3017
+ };
3018
+ }
2742
3019
  var EnvironmentContextSchema = z4.object({
2743
3020
  projectName: z4.string().optional(),
2744
3021
  branch: z4.string().optional(),
@@ -2868,6 +3145,7 @@ var LocalChangesResponseSchema = z4.object({
2868
3145
  available: z4.literal(true),
2869
3146
  branch: z4.string(),
2870
3147
  baseBranch: z4.string(),
3148
+ headSha: z4.string().optional(),
2871
3149
  staged: z4.array(LocalFileChangeSchema),
2872
3150
  unstaged: z4.array(LocalFileChangeSchema),
2873
3151
  untracked: z4.array(z4.string()),
@@ -3184,6 +3462,7 @@ var TOOL_NAMES = {
3184
3462
  CREATE_PLAN: "create_plan",
3185
3463
  EXECUTE_CODE: "execute_code",
3186
3464
  LINK_PR: "link_pr",
3465
+ READ_DIFF_COMMENTS: "read_diff_comments",
3187
3466
  READ_PLAN: "read_plan",
3188
3467
  REGENERATE_SESSION_TOKEN: "regenerate_session_token",
3189
3468
  REQUEST_USER_INPUT: "request_user_input",
@@ -3314,7 +3593,7 @@ var planRouter = router({
3314
3593
  message: "Plan not found"
3315
3594
  });
3316
3595
  const origin = metadata.origin;
3317
- const cwd = origin?.platform === "claude-code" ? origin.cwd : void 0;
3596
+ const cwd = origin?.platform === "claude-code" || origin?.platform === "unknown" ? origin.cwd : void 0;
3318
3597
  if (!cwd) return {
3319
3598
  available: false,
3320
3599
  reason: "no_cwd",
@@ -3335,7 +3614,7 @@ var planRouter = router({
3335
3614
  message: "Plan not found"
3336
3615
  });
3337
3616
  const origin = metadata.origin;
3338
- const cwd = origin?.platform === "claude-code" ? origin.cwd : void 0;
3617
+ const cwd = origin?.platform === "claude-code" || origin?.platform === "unknown" ? origin.cwd : void 0;
3339
3618
  if (!cwd) return {
3340
3619
  content: null,
3341
3620
  error: "No working directory available"
@@ -3432,6 +3711,7 @@ export {
3432
3711
  LinkedPRStatusValues,
3433
3712
  LinkedPRSchema,
3434
3713
  PRReviewCommentSchema,
3714
+ LocalDiffCommentSchema,
3435
3715
  createLinkedPR,
3436
3716
  createGitHubArtifact,
3437
3717
  createLocalArtifact,
@@ -3517,6 +3797,11 @@ export {
3517
3797
  addPRReviewComment,
3518
3798
  resolvePRReviewComment,
3519
3799
  removePRReviewComment,
3800
+ getLocalDiffComments,
3801
+ getLocalDiffCommentsForFile,
3802
+ addLocalDiffComment,
3803
+ resolveLocalDiffComment,
3804
+ removeLocalDiffComment,
3520
3805
  markPlanAsViewed,
3521
3806
  getViewedBy,
3522
3807
  isPlanUnread,
@@ -3561,6 +3846,16 @@ export {
3561
3846
  formatAsClaudeCodeJSONL,
3562
3847
  formatDeliverablesForLLM,
3563
3848
  extractDeliverables,
3849
+ hashLineContent,
3850
+ computeCommentStaleness,
3851
+ isLineContentStale,
3852
+ withStalenessInfo,
3853
+ withStalenessInfoBatch,
3854
+ buildLineContentMap,
3855
+ formatStalenessMarker,
3856
+ formatDiffCommentsForLLM,
3857
+ formatPRCommentsForLLM,
3858
+ getPRCommentsSummary,
3564
3859
  EnvironmentContextSchema,
3565
3860
  GitHubPRResponseSchema,
3566
3861
  asPlanId,