claude-teammate 0.1.97 → 0.1.99

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-teammate",
3
- "version": "0.1.97",
3
+ "version": "0.1.99",
4
4
  "description": "CLI bootstrapper for Claude Teammate.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1162,7 +1162,7 @@ a:hover { text-decoration:underline; }
1162
1162
  .drawer-log-panel {
1163
1163
  display:flex;
1164
1164
  flex-direction:column;
1165
- overflow:hidden;
1165
+ overflow-x:hidden;
1166
1166
  background:#111110;
1167
1167
  }
1168
1168
 
@@ -335,13 +335,64 @@ export function createGitLabClient(config) {
335
335
 
336
336
  async createPullRequestReview(repoUrl, prNumber, body, suggestions) {
337
337
  const repo = parseGitLabRepoUrl(repoUrl);
338
- const reviewBody = buildGitLabReviewBody(body, suggestions);
339
- return requestGitLabProject(config, repo, `/merge_requests/${prNumber}/notes`, {
340
- method: "POST",
341
- body: {
342
- body: reviewBody
338
+ const normalizedSuggestions = Array.isArray(suggestions) ? suggestions.filter(Boolean) : [];
339
+ const fallbackSuggestions = [];
340
+ const discussionResults = [];
341
+
342
+ if (normalizedSuggestions.length > 0) {
343
+ const [mergeRequest, versions, changesPayload] = await Promise.all([
344
+ requestGitLabProject(config, repo, `/merge_requests/${prNumber}`),
345
+ requestGitLabProject(config, repo, `/merge_requests/${prNumber}/versions`),
346
+ requestGitLabProject(config, repo, `/merge_requests/${prNumber}/changes`)
347
+ ]);
348
+ const diffRefs = selectLatestGitLabDiffRefs(mergeRequest, versions);
349
+ const positionIndex = buildGitLabDiffPositionIndex(changesPayload?.changes || []);
350
+
351
+ for (const suggestion of normalizedSuggestions) {
352
+ const position = diffRefs
353
+ ? findGitLabSuggestionPosition(positionIndex, suggestion)
354
+ : null;
355
+
356
+ if (!position) {
357
+ fallbackSuggestions.push(suggestion);
358
+ continue;
359
+ }
360
+
361
+ try {
362
+ const payload = await requestGitLabProject(config, repo, `/merge_requests/${prNumber}/discussions`, {
363
+ method: "POST",
364
+ body: {
365
+ body: buildGitLabSuggestionDiscussionBody(suggestion),
366
+ position: {
367
+ position_type: "text",
368
+ base_sha: diffRefs.baseSha,
369
+ start_sha: diffRefs.startSha,
370
+ head_sha: diffRefs.headSha,
371
+ ...position
372
+ }
373
+ }
374
+ });
375
+ discussionResults.push(payload);
376
+ } catch {
377
+ fallbackSuggestions.push(suggestion);
378
+ }
343
379
  }
344
- });
380
+ }
381
+
382
+ const reviewBody = buildGitLabReviewBody(body, fallbackSuggestions);
383
+ const overviewNote = reviewBody
384
+ ? await requestGitLabProject(config, repo, `/merge_requests/${prNumber}/notes`, {
385
+ method: "POST",
386
+ body: {
387
+ body: reviewBody
388
+ }
389
+ })
390
+ : null;
391
+
392
+ return {
393
+ overviewNote,
394
+ discussions: discussionResults
395
+ };
345
396
  },
346
397
 
347
398
  async fetchPullRequestDiff(repoUrl, prNumber) {
@@ -401,17 +452,148 @@ function buildGitLabReviewBody(body, suggestions = []) {
401
452
  if (Array.isArray(suggestions) && suggestions.length > 0) {
402
453
  base.push("Suggestions:");
403
454
  for (const suggestion of suggestions) {
404
- const location = [suggestion.file, suggestion.line].filter(Boolean).join(":");
405
- const note = String(suggestion.body || "").trim();
406
- const block = suggestion.committable && suggestion.suggestion
407
- ? `\n\n\`\`\`suggestion\n${suggestion.suggestion}\n\`\`\``
408
- : "";
409
- base.push(`- ${location || "general"}: ${note}${block}`);
455
+ base.push(buildGitLabFallbackSuggestionEntry(suggestion));
410
456
  }
411
457
  }
412
458
  return base.filter(Boolean).join("\n\n").trim();
413
459
  }
414
460
 
461
+ function buildGitLabFallbackSuggestionEntry(suggestion) {
462
+ const location = [suggestion?.file, suggestion?.line].filter(Boolean).join(":");
463
+ const note = String(suggestion?.body || "").trim();
464
+ const block = canCreateGitLabSuggestionBlock(suggestion)
465
+ ? `\n\n\`\`\`\`suggestion:-0+0\n${String(suggestion.suggestion || "").trim()}\n\`\`\`\``
466
+ : "";
467
+ return `- ${location || "general"}: ${note}${block}`;
468
+ }
469
+
470
+ function buildGitLabSuggestionDiscussionBody(suggestion) {
471
+ const note = String(suggestion?.body || "").trim();
472
+ const block = canCreateGitLabSuggestionBlock(suggestion)
473
+ ? `\n\n\`\`\`\`suggestion:-0+0\n${String(suggestion.suggestion || "").trim()}\n\`\`\`\``
474
+ : "";
475
+ return `${note}${block}`.trim();
476
+ }
477
+
478
+ function canCreateGitLabSuggestionBlock(suggestion) {
479
+ return Boolean(
480
+ suggestion?.committable &&
481
+ String(suggestion?.suggestion || "").trim() &&
482
+ String(suggestion?.side || "RIGHT").trim().toUpperCase() === "RIGHT"
483
+ );
484
+ }
485
+
486
+ function selectLatestGitLabDiffRefs(mergeRequest, versions) {
487
+ const candidates = Array.isArray(versions) ? versions : [];
488
+ const latestVersion = candidates
489
+ .filter((version) => version && (version.created_at || version.id != null))
490
+ .sort((left, right) => {
491
+ const leftDate = left.created_at ? Date.parse(left.created_at) : Number(left.id) || 0;
492
+ const rightDate = right.created_at ? Date.parse(right.created_at) : Number(right.id) || 0;
493
+ return rightDate - leftDate;
494
+ })[0];
495
+
496
+ const baseSha = latestVersion?.base_commit_sha || mergeRequest?.diff_refs?.base_sha;
497
+ const startSha = latestVersion?.start_commit_sha || mergeRequest?.diff_refs?.start_sha || baseSha;
498
+ const headSha = latestVersion?.head_commit_sha || mergeRequest?.diff_refs?.head_sha;
499
+
500
+ if (!baseSha || !startSha || !headSha) {
501
+ return null;
502
+ }
503
+
504
+ return { baseSha, startSha, headSha };
505
+ }
506
+
507
+ function buildGitLabDiffPositionIndex(changes) {
508
+ return (Array.isArray(changes) ? changes : []).map((change) => ({
509
+ oldPath: String(change?.old_path || change?.new_path || ""),
510
+ newPath: String(change?.new_path || change?.old_path || ""),
511
+ positions: parseGitLabDiffPositions(String(change?.diff || ""))
512
+ }));
513
+ }
514
+
515
+ function findGitLabSuggestionPosition(positionIndex, suggestion) {
516
+ const file = String(suggestion?.file || "").trim();
517
+ const line = Number(suggestion?.line) || 0;
518
+ const side = String(suggestion?.side || "RIGHT").trim().toUpperCase();
519
+ if (!file || !line) {
520
+ return null;
521
+ }
522
+
523
+ const preferredChange = positionIndex.find((change) =>
524
+ side === "LEFT" ? change.oldPath === file : change.newPath === file
525
+ ) || positionIndex.find((change) => change.oldPath === file || change.newPath === file);
526
+
527
+ if (!preferredChange) {
528
+ return null;
529
+ }
530
+
531
+ const match = preferredChange.positions.find((position) => (
532
+ side === "LEFT" ? position.oldLine === line : position.newLine === line
533
+ ));
534
+
535
+ if (!match) {
536
+ return null;
537
+ }
538
+
539
+ const payload = {
540
+ old_path: preferredChange.oldPath,
541
+ new_path: preferredChange.newPath
542
+ };
543
+
544
+ if (match.oldLine != null) {
545
+ payload.old_line = match.oldLine;
546
+ }
547
+ if (match.newLine != null) {
548
+ payload.new_line = match.newLine;
549
+ }
550
+
551
+ return payload;
552
+ }
553
+
554
+ function parseGitLabDiffPositions(diff) {
555
+ const positions = [];
556
+ let oldLine = 0;
557
+ let newLine = 0;
558
+
559
+ for (const rawLine of String(diff || "").split("\n")) {
560
+ const hunkMatch = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/u.exec(rawLine);
561
+ if (hunkMatch) {
562
+ oldLine = Number(hunkMatch[1]);
563
+ newLine = Number(hunkMatch[2]);
564
+ continue;
565
+ }
566
+
567
+ if (!rawLine || rawLine.startsWith("diff --git") || rawLine.startsWith("index ")) {
568
+ continue;
569
+ }
570
+ if (rawLine.startsWith("\")) {
571
+ continue;
572
+ }
573
+ if (rawLine.startsWith("--- ") || rawLine.startsWith("+++ ")) {
574
+ continue;
575
+ }
576
+
577
+ const prefix = rawLine[0];
578
+ if (prefix === "+") {
579
+ positions.push({ oldLine: null, newLine });
580
+ newLine += 1;
581
+ continue;
582
+ }
583
+ if (prefix === "-") {
584
+ positions.push({ oldLine, newLine: null });
585
+ oldLine += 1;
586
+ continue;
587
+ }
588
+
589
+ positions.push({ oldLine, newLine });
590
+ oldLine += 1;
591
+ newLine += 1;
592
+ }
593
+
594
+ return positions;
595
+ }
596
+
415
597
  function buildGitLabDiff(changes) {
416
598
  return (Array.isArray(changes) ? changes : []).map((change) => {
417
599
  const oldPath = change.old_path || change.new_path || "unknown";