@staff0rd/assist 0.548.0 → 0.549.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.
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command } from "commander";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@staff0rd/assist",
9
- version: "0.548.0",
9
+ version: "0.549.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -3421,6 +3421,312 @@ function isBicepFile(filePath) {
3421
3421
  return BICEP_EXTENSIONS.some((ext) => filePath.endsWith(ext));
3422
3422
  }
3423
3423
 
3424
+ // src/shared/isCsharpFile.ts
3425
+ var CSHARP_EXTENSIONS = [".cs", ".csx"];
3426
+ function isCsharpFile(filePath) {
3427
+ if (!filePath) return false;
3428
+ return CSHARP_EXTENSIONS.some((ext) => filePath.endsWith(ext));
3429
+ }
3430
+
3431
+ // src/shared/isGeneratedCsharpFile.ts
3432
+ var GENERATED_SUFFIXES = [".g.cs", ".designer.cs"];
3433
+ var BUILD_OUTPUT_DIR = /(^|[/\\])obj[/\\]/;
3434
+ var AUTO_GENERATED_MARKER = "<auto-generated";
3435
+ var HEADER_LINES = 10;
3436
+ function hasAutoGeneratedHeader(content) {
3437
+ return content.split("\n", HEADER_LINES).some((line) => line.toLowerCase().includes(AUTO_GENERATED_MARKER));
3438
+ }
3439
+ function isGeneratedCsharpFile(filePath, content) {
3440
+ if (!isCsharpFile(filePath) || !filePath) return false;
3441
+ const lower = filePath.toLowerCase();
3442
+ if (GENERATED_SUFFIXES.some((suffix) => lower.endsWith(suffix))) return true;
3443
+ if (BUILD_OUTPUT_DIR.test(lower)) return true;
3444
+ return content !== void 0 && hasAutoGeneratedHeader(content);
3445
+ }
3446
+
3447
+ // src/shared/isRazorFile.ts
3448
+ var RAZOR_EXTENSIONS = [".razor", ".cshtml"];
3449
+ function isRazorFile(filePath) {
3450
+ if (!filePath) return false;
3451
+ return RAZOR_EXTENSIONS.some((ext) => filePath.endsWith(ext));
3452
+ }
3453
+
3454
+ // src/shared/lineCounter.ts
3455
+ function lineCounter(content) {
3456
+ let scanned = 0;
3457
+ let line = 1;
3458
+ return (offset) => {
3459
+ for (; scanned < offset; scanned++) {
3460
+ if (content[scanned] === "\n") line++;
3461
+ }
3462
+ return line;
3463
+ };
3464
+ }
3465
+
3466
+ // src/shared/readRazorComment.ts
3467
+ var RAZOR_OPEN = "@*";
3468
+ var RAZOR_CLOSE = "*@";
3469
+ var HTML_OPEN = "<!--";
3470
+ var HTML_CLOSE = "-->";
3471
+ function read(content, index3, open, close) {
3472
+ const found = content.indexOf(close, index3 + open.length);
3473
+ const end = found === -1 ? content.length : found + close.length;
3474
+ return { end, text: content.slice(index3, end) };
3475
+ }
3476
+ function readRazorComment(content, index3) {
3477
+ if (content.startsWith(RAZOR_OPEN, index3))
3478
+ return read(content, index3, RAZOR_OPEN, RAZOR_CLOSE);
3479
+ if (content.startsWith(HTML_OPEN, index3))
3480
+ return read(content, index3, HTML_OPEN, HTML_CLOSE);
3481
+ return void 0;
3482
+ }
3483
+
3484
+ // src/shared/skipCsharpCharLiteral.ts
3485
+ var CHAR_LITERAL_MAX_LENGTH = 12;
3486
+ function skipCsharpCharLiteral(content, index3) {
3487
+ const limit = Math.min(content.length, index3 + CHAR_LITERAL_MAX_LENGTH);
3488
+ let cursor = index3 + 1;
3489
+ while (cursor < limit) {
3490
+ const char = content[cursor];
3491
+ if (char === "\n") break;
3492
+ if (char === "\\") {
3493
+ cursor += 2;
3494
+ continue;
3495
+ }
3496
+ if (char === "'") return cursor + 1;
3497
+ cursor++;
3498
+ }
3499
+ return index3 + 1;
3500
+ }
3501
+
3502
+ // src/shared/readCsharpStringStart.ts
3503
+ function readCsharpStringStart(content, index3) {
3504
+ let cursor = index3;
3505
+ let verbatim = false;
3506
+ let interpolated = false;
3507
+ while (content[cursor] === "@" || content[cursor] === "$") {
3508
+ if (content[cursor] === "@") verbatim = true;
3509
+ else interpolated = true;
3510
+ cursor++;
3511
+ }
3512
+ if (content[cursor] !== '"') return void 0;
3513
+ let quoteRun = 0;
3514
+ while (content[cursor + quoteRun] === '"') quoteRun++;
3515
+ return { quote: cursor, verbatim, interpolated, quoteRun };
3516
+ }
3517
+
3518
+ // src/shared/skipCsharpRawString.ts
3519
+ function skipCsharpRawString(content, quote, quoteRun) {
3520
+ let cursor = quote + quoteRun;
3521
+ while (cursor < content.length) {
3522
+ if (content[cursor] !== '"') {
3523
+ cursor++;
3524
+ continue;
3525
+ }
3526
+ let run4 = 0;
3527
+ while (content[cursor + run4] === '"') run4++;
3528
+ if (run4 >= quoteRun) return cursor + run4;
3529
+ cursor += run4;
3530
+ }
3531
+ return content.length;
3532
+ }
3533
+
3534
+ // src/shared/skipCsharpString.ts
3535
+ function skipInterpolationHole(content, index3) {
3536
+ let cursor = index3;
3537
+ let depth = 1;
3538
+ while (cursor < content.length) {
3539
+ const char = content[cursor];
3540
+ if (char === "{") depth++;
3541
+ else if (char === "}" && --depth === 0) return cursor + 1;
3542
+ else if (char === "'") {
3543
+ cursor = skipCsharpCharLiteral(content, cursor);
3544
+ continue;
3545
+ } else {
3546
+ const nested = skipCsharpString(content, cursor);
3547
+ if (nested !== void 0) {
3548
+ cursor = nested;
3549
+ continue;
3550
+ }
3551
+ }
3552
+ cursor++;
3553
+ }
3554
+ return content.length;
3555
+ }
3556
+ function skipDelimitedString(content, start3) {
3557
+ const { quote, verbatim, interpolated } = start3;
3558
+ let cursor = quote + 1;
3559
+ while (cursor < content.length) {
3560
+ const char = content[cursor];
3561
+ if (char === "\\" && !verbatim) cursor += 2;
3562
+ else if (char === "\n" && !verbatim) return cursor;
3563
+ else if (char === '"') {
3564
+ if (!verbatim || content[cursor + 1] !== '"') return cursor + 1;
3565
+ cursor += 2;
3566
+ } else if (interpolated && char === "{")
3567
+ cursor = content[cursor + 1] === "{" ? cursor + 2 : skipInterpolationHole(content, cursor + 1);
3568
+ else cursor++;
3569
+ }
3570
+ return content.length;
3571
+ }
3572
+ function skipCsharpString(content, index3) {
3573
+ const start3 = readCsharpStringStart(content, index3);
3574
+ if (!start3) return void 0;
3575
+ if (!start3.verbatim && start3.quoteRun >= 3)
3576
+ return skipCsharpRawString(content, start3.quote, start3.quoteRun);
3577
+ return skipDelimitedString(content, start3);
3578
+ }
3579
+
3580
+ // src/shared/stepRazorCode.ts
3581
+ function stepRazorCode(content, index3) {
3582
+ const char = content[index3];
3583
+ if (char === "{") return { next: index3 + 1, depthChange: 1 };
3584
+ if (char === "}") return { next: index3 + 1, depthChange: -1 };
3585
+ if (char === "'")
3586
+ return { next: skipCsharpCharLiteral(content, index3), depthChange: 0 };
3587
+ const afterString = skipCsharpString(content, index3);
3588
+ return { next: afterString ?? index3 + 1, depthChange: 0 };
3589
+ }
3590
+
3591
+ // src/shared/readRazorCodeBlockStart.ts
3592
+ var CODE_KEYWORDS = ["code", "functions"];
3593
+ var WHITESPACE = /* @__PURE__ */ new Set([" ", " ", "\r", "\n"]);
3594
+ function readRazorCodeBlockStart(content, index3) {
3595
+ if (content[index3] !== "@") return void 0;
3596
+ let cursor = index3 + 1;
3597
+ const keyword = CODE_KEYWORDS.find(
3598
+ (word) => content.startsWith(word, cursor)
3599
+ );
3600
+ if (keyword) {
3601
+ cursor += keyword.length;
3602
+ while (WHITESPACE.has(content[cursor] ?? "")) cursor++;
3603
+ }
3604
+ return content[cursor] === "{" ? cursor + 1 : void 0;
3605
+ }
3606
+
3607
+ // src/shared/skipHtmlTag.ts
3608
+ var TAG_NAME_START = /[A-Za-z/]/;
3609
+ function skipHtmlTag(content, index3) {
3610
+ if (content[index3] !== "<") return void 0;
3611
+ if (!TAG_NAME_START.test(content[index3 + 1] ?? "")) return void 0;
3612
+ let cursor = index3 + 1;
3613
+ while (cursor < content.length) {
3614
+ const char = content[cursor];
3615
+ if (char === '"' || char === "'") {
3616
+ const close = content.indexOf(char, cursor + 1);
3617
+ if (close === -1) return void 0;
3618
+ cursor = close + 1;
3619
+ } else if (char === ">") return cursor + 1;
3620
+ else cursor++;
3621
+ }
3622
+ return void 0;
3623
+ }
3624
+
3625
+ // src/shared/skipRawTextElement.ts
3626
+ var RAW_TEXT_TAGS = ["script", "style"];
3627
+ var TAG_NAME_END = /[^A-Za-z0-9-]/;
3628
+ function openedTag(content, tagStart, tagEnd) {
3629
+ if (content.slice(tagStart, tagEnd).endsWith("/>")) return "";
3630
+ const name = content.slice(tagStart + 1, tagEnd).toLowerCase();
3631
+ return RAW_TEXT_TAGS.find(
3632
+ (tag) => name.startsWith(tag) && TAG_NAME_END.test(name[tag.length] ?? ">")
3633
+ ) ?? "";
3634
+ }
3635
+ function skipRawTextElement(content, tagStart, tagEnd) {
3636
+ const tag = openedTag(content, tagStart, tagEnd);
3637
+ if (!tag) return tagEnd;
3638
+ const close = new RegExp(`</${tag}`, "i").exec(content.slice(tagEnd));
3639
+ return close ? tagEnd + close.index : tagEnd;
3640
+ }
3641
+
3642
+ // src/shared/skipRazorExpression.ts
3643
+ var IDENT_START = /[A-Za-z_]/;
3644
+ var IDENT_PART = /[A-Za-z0-9_]/;
3645
+ var GROUP_CLOSE = { "(": ")", "[": "]" };
3646
+ function skipGroup(content, index3) {
3647
+ const open = content[index3];
3648
+ const close = GROUP_CLOSE[open];
3649
+ let cursor = index3 + 1;
3650
+ let depth = 1;
3651
+ while (cursor < content.length) {
3652
+ const char = content[cursor];
3653
+ if (char === open) depth++;
3654
+ else if (char === close && --depth === 0) return cursor + 1;
3655
+ else if (char === "'") {
3656
+ cursor = skipCsharpCharLiteral(content, cursor);
3657
+ continue;
3658
+ } else {
3659
+ const afterString = skipCsharpString(content, cursor);
3660
+ if (afterString !== void 0) {
3661
+ cursor = afterString;
3662
+ continue;
3663
+ }
3664
+ }
3665
+ cursor++;
3666
+ }
3667
+ return void 0;
3668
+ }
3669
+ function skipRazorExpression(content, index3) {
3670
+ if (content[index3] !== "@") return void 0;
3671
+ const first = content[index3 + 1] ?? "";
3672
+ if (!IDENT_START.test(first) && !GROUP_CLOSE[first]) return void 0;
3673
+ let cursor = index3 + 1;
3674
+ while (cursor < content.length) {
3675
+ const char = content[cursor];
3676
+ if (IDENT_PART.test(char)) cursor++;
3677
+ else if (char === "." && IDENT_START.test(content[cursor + 1] ?? ""))
3678
+ cursor++;
3679
+ else if (GROUP_CLOSE[char]) {
3680
+ const afterGroup = skipGroup(content, cursor);
3681
+ if (afterGroup === void 0) return cursor;
3682
+ cursor = afterGroup;
3683
+ } else break;
3684
+ }
3685
+ return cursor;
3686
+ }
3687
+
3688
+ // src/shared/stepRazorMarkup.ts
3689
+ function stepRazorMarkup(content, index3) {
3690
+ const blockBody = readRazorCodeBlockStart(content, index3);
3691
+ if (blockBody !== void 0) return { next: blockBody, enteredCode: true };
3692
+ const expressionEnd = skipRazorExpression(content, index3);
3693
+ if (expressionEnd !== void 0)
3694
+ return { next: expressionEnd, enteredCode: false };
3695
+ const tagEnd = skipHtmlTag(content, index3);
3696
+ const next3 = tagEnd === void 0 ? index3 + 1 : skipRawTextElement(content, index3, tagEnd);
3697
+ return { next: next3, enteredCode: false };
3698
+ }
3699
+
3700
+ // src/shared/extractRazorComments.ts
3701
+ function extractRazorComments(content) {
3702
+ const comments3 = [];
3703
+ const lineOf = lineCounter(content);
3704
+ let index3 = 0;
3705
+ let codeDepth = 0;
3706
+ while (index3 < content.length) {
3707
+ if (content[index3] === "@" && content[index3 + 1] === "@") {
3708
+ index3 += 2;
3709
+ continue;
3710
+ }
3711
+ const comment3 = readRazorComment(content, index3);
3712
+ if (comment3) {
3713
+ comments3.push({ line: lineOf(index3), text: comment3.text });
3714
+ index3 = comment3.end;
3715
+ continue;
3716
+ }
3717
+ if (codeDepth > 0) {
3718
+ const step3 = stepRazorCode(content, index3);
3719
+ codeDepth += step3.depthChange;
3720
+ index3 = step3.next;
3721
+ continue;
3722
+ }
3723
+ const step2 = stepRazorMarkup(content, index3);
3724
+ if (step2.enteredCode) codeDepth = 1;
3725
+ index3 = step2.next;
3726
+ }
3727
+ return comments3;
3728
+ }
3729
+
3424
3730
  // src/commands/verify/blockCodeComments/collectBicepComments.ts
3425
3731
  function blankNonNewline(text17) {
3426
3732
  return text17.replace(/[^\n]/g, " ");
@@ -3444,6 +3750,84 @@ function collectBicepComments(content) {
3444
3750
  return comments3;
3445
3751
  }
3446
3752
 
3753
+ // src/shared/csharpHeaderLineCount.ts
3754
+ function closesBlock(trimmed, from) {
3755
+ return trimmed.includes("*/", from);
3756
+ }
3757
+ function csharpHeaderLineCount(content) {
3758
+ const lines2 = content.split("\n");
3759
+ let count8 = 0;
3760
+ let inBlock = false;
3761
+ while (count8 < lines2.length) {
3762
+ const trimmed = lines2[count8].trim();
3763
+ if (inBlock) {
3764
+ inBlock = !closesBlock(trimmed, 0);
3765
+ } else if (trimmed.startsWith("/*")) {
3766
+ inBlock = !closesBlock(trimmed, 2);
3767
+ } else if (trimmed !== "" && !trimmed.startsWith("//")) {
3768
+ break;
3769
+ }
3770
+ count8++;
3771
+ }
3772
+ return count8;
3773
+ }
3774
+
3775
+ // src/shared/readCsharpComment.ts
3776
+ function readCsharpComment(content, index3) {
3777
+ if (content[index3] !== "/") return void 0;
3778
+ if (content[index3 + 1] === "/") {
3779
+ const lineEnd = content.indexOf("\n", index3);
3780
+ const end = lineEnd === -1 ? content.length : lineEnd;
3781
+ return { end, text: content.slice(index3, end) };
3782
+ }
3783
+ if (content[index3 + 1] === "*") {
3784
+ const close = content.indexOf("*/", index3 + 2);
3785
+ const end = close === -1 ? content.length : close + 2;
3786
+ return { end, text: content.slice(index3, end) };
3787
+ }
3788
+ return void 0;
3789
+ }
3790
+
3791
+ // src/shared/extractCsharpComments.ts
3792
+ var WHITESPACE2 = /* @__PURE__ */ new Set([" ", " ", "\r"]);
3793
+ function extractCsharpComments(content) {
3794
+ const comments3 = [];
3795
+ const lineOf = lineCounter(content);
3796
+ let index3 = 0;
3797
+ let atLineStart = true;
3798
+ while (index3 < content.length) {
3799
+ const char = content[index3];
3800
+ if (char === "\n") {
3801
+ atLineStart = true;
3802
+ index3++;
3803
+ } else if (WHITESPACE2.has(char)) {
3804
+ index3++;
3805
+ } else if (atLineStart && char === "#") {
3806
+ const lineEnd = content.indexOf("\n", index3);
3807
+ index3 = lineEnd === -1 ? content.length : lineEnd;
3808
+ } else {
3809
+ atLineStart = false;
3810
+ const comment3 = readCsharpComment(content, index3);
3811
+ const afterString = comment3 ? void 0 : skipCsharpString(content, index3);
3812
+ if (comment3) {
3813
+ comments3.push({ line: lineOf(index3), text: comment3.text });
3814
+ index3 = comment3.end;
3815
+ } else if (char === "'") index3 = skipCsharpCharLiteral(content, index3);
3816
+ else if (afterString !== void 0) index3 = afterString;
3817
+ else index3++;
3818
+ }
3819
+ }
3820
+ return comments3;
3821
+ }
3822
+
3823
+ // src/commands/verify/blockCodeComments/collectCsharpComments.ts
3824
+ function collectCsharpComments(content) {
3825
+ const headerLines = csharpHeaderLineCount(content);
3826
+ return extractCsharpComments(content).filter(
3827
+ (comment3) => comment3.line > headerLines
3828
+ );
3829
+ }
3830
+
3447
3831
  // src/commands/verify/blockCodeComments/collectHashComments.ts
3448
3832
  function blankNonNewline2(text17) {
3449
3833
  return text17.replace(/[^\n]/g, " ");
@@ -3521,6 +3905,10 @@ var MACHINE_DIRECTIVES = [
3521
3905
  "v8 ignore",
3522
3906
  "c8 ignore",
3523
3907
  "@vitest-environment",
3908
+ "resharper disable",
3909
+ "resharper restore",
3910
+ "noinspection",
3911
+ "<auto-generated",
3524
3912
  MAINTAINABILITY_OVERRIDE_MARKER
3525
3913
  ];
3526
3914
  function isCommentExempt(text17) {
@@ -3556,7 +3944,7 @@ function collectYamlComments(content) {
3556
3944
  return comments3;
3557
3945
  }
3558
3946
 
3559
- // src/commands/verify/blockCodeComments/collectFileComments.ts
3947
+ // src/commands/verify/blockCodeComments/toFindings.ts
3560
3948
  function toFindings(file, lines2, raw, exempt) {
3561
3949
  const findings = [];
3562
3950
  for (const { line, text: text17 } of raw) {
@@ -3566,19 +3954,28 @@ function toFindings(file, lines2, raw, exempt) {
3566
3954
  }
3567
3955
  return findings;
3568
3956
  }
3957
+
3958
+ // src/commands/verify/blockCodeComments/collectFileComments.ts
3569
3959
  function collectFileComments(file, lines2, project) {
3570
- const read = () => fs14.readFileSync(file, "utf8");
3960
+ const read2 = () => fs14.readFileSync(file, "utf8");
3571
3961
  if (isYamlFile(file))
3572
- return toFindings(file, lines2, collectYamlComments(read()), false);
3962
+ return toFindings(file, lines2, collectYamlComments(read2()), false);
3573
3963
  if (isDockerfile(file) || isEnvFile(file) || isShellFile(file))
3574
3964
  return toFindings(
3575
3965
  file,
3576
3966
  lines2,
3577
- collectHashComments(read(), { skipHeader: isShellFile(file) }),
3967
+ collectHashComments(read2(), { skipHeader: isShellFile(file) }),
3578
3968
  true
3579
3969
  );
3580
3970
  if (isBicepFile(file))
3581
- return toFindings(file, lines2, collectBicepComments(read()), true);
3971
+ return toFindings(file, lines2, collectBicepComments(read2()), true);
3972
+ if (isCsharpFile(file)) {
3973
+ const content = read2();
3974
+ if (isGeneratedCsharpFile(file, content)) return [];
3975
+ return toFindings(file, lines2, collectCsharpComments(content), true);
3976
+ }
3977
+ if (isRazorFile(file))
3978
+ return toFindings(file, lines2, extractRazorComments(read2()), true);
3582
3979
  return collectSourceFindings(file, lines2, project);
3583
3980
  }
3584
3981
 
@@ -3628,7 +4025,11 @@ var SCANNED_EXTENSIONS = [
3628
4025
  ".yml",
3629
4026
  ".yaml",
3630
4027
  ".bicep",
3631
- ".bicepparam"
4028
+ ".bicepparam",
4029
+ ".cs",
4030
+ ".csx",
4031
+ ".razor",
4032
+ ".cshtml"
3632
4033
  ];
3633
4034
  function shouldScan(file, ignoreGlobs) {
3634
4035
  if (!SCANNED_EXTENSIONS.some((ext) => file.endsWith(ext)) && !isHashCommentFile(file))
@@ -19177,6 +19578,58 @@ function registerDotnet(program2) {
19177
19578
  // src/commands/editHook/index.ts
19178
19579
  import fs23 from "fs";
19179
19580
 
19581
+ // src/commands/editHook/introducedComments.ts
19582
+ function introducedComments(added, removed) {
19583
+ const counts = /* @__PURE__ */ new Map();
19584
+ for (const comment3 of removed) {
19585
+ counts.set(comment3, (counts.get(comment3) ?? 0) + 1);
19586
+ }
19587
+ const candidates = [];
19588
+ for (const comment3 of added) {
19589
+ const remaining = counts.get(comment3) ?? 0;
19590
+ if (remaining > 0) counts.set(comment3, remaining - 1);
19591
+ else candidates.push(comment3);
19592
+ }
19593
+ const removedWords = new Set(removed.flatMap(commentWords));
19594
+ return candidates.filter((comment3) => {
19595
+ const words = commentWords(comment3);
19596
+ if (words.length === 0) return true;
19597
+ return !words.every((word) => removedWords.has(word));
19598
+ });
19599
+ }
19600
+ function commentWords(comment3) {
19601
+ return comment3.toLowerCase().split(/[^a-z0-9]+/).filter((word) => word.length > 0);
19602
+ }
19603
+
19604
+ // src/commands/editHook/partitionStrings.ts
19605
+ function defined(values) {
19606
+ return values.filter((value) => value != null);
19607
+ }
19608
+ function partitionStrings(input, existingContent) {
19609
+ const { tool_name, tool_input } = input;
19610
+ switch (tool_name) {
19611
+ case "Edit":
19612
+ return {
19613
+ added: defined([tool_input.new_string]),
19614
+ removed: defined([tool_input.old_string])
19615
+ };
19616
+ case "MultiEdit": {
19617
+ const edits = tool_input.edits ?? [];
19618
+ return {
19619
+ added: defined(edits.map((e) => e.new_string)),
19620
+ removed: defined(edits.map((e) => e.old_string))
19621
+ };
19622
+ }
19623
+ case "Write":
19624
+ return {
19625
+ added: defined([tool_input.content]),
19626
+ removed: defined([existingContent])
19627
+ };
19628
+ default:
19629
+ return { added: [], removed: [] };
19630
+ }
19631
+ }
19632
+
19180
19633
  // src/commands/editHook/extractComments.ts
19181
19634
  var SOURCE_EXTENSIONS = [
19182
19635
  ".ts",
@@ -19213,6 +19666,21 @@ function extractComments(text17) {
19213
19666
  return comments3.map((comment3) => comment3.replace(/\s+/g, " ").trim()).filter((comment3) => comment3.length > 0).filter((comment3) => !isCommentExempt(comment3));
19214
19667
  }
19215
19668
 
19669
+ // src/commands/editHook/commentTexts.ts
19670
+ function commentTexts(comments3) {
19671
+ return comments3.map((comment3) => comment3.text.replace(/\s+/g, " ").trim()).filter((comment3) => comment3.length > 0).filter((comment3) => !isCommentExempt(comment3));
19672
+ }
19673
+
19674
+ // src/commands/editHook/extractCsharpCommentTexts.ts
19675
+ function extractCsharpCommentTexts(text17) {
19676
+ return commentTexts(extractCsharpComments(text17));
19677
+ }
19678
+
19679
+ // src/commands/editHook/extractRazorCommentTexts.ts
19680
+ function extractRazorCommentTexts(text17) {
19681
+ return commentTexts(extractRazorComments(text17));
19682
+ }
19683
+
19216
19684
  // src/commands/editHook/extractYamlComments.ts
19217
19685
  function blankNonNewline4(text17) {
19218
19686
  return text17.replace(/[^\n]/g, " ");
@@ -19243,27 +19711,19 @@ function extractShellComments(text17) {
19243
19711
  return extractYamlComments(lines2.slice(firstCodeLine).join("\n"));
19244
19712
  }
19245
19713
 
19246
- // src/commands/editHook/introducedComments.ts
19247
- function introducedComments(added, removed) {
19248
- const counts = /* @__PURE__ */ new Map();
19249
- for (const comment3 of removed) {
19250
- counts.set(comment3, (counts.get(comment3) ?? 0) + 1);
19251
- }
19252
- const candidates = [];
19253
- for (const comment3 of added) {
19254
- const remaining = counts.get(comment3) ?? 0;
19255
- if (remaining > 0) counts.set(comment3, remaining - 1);
19256
- else candidates.push(comment3);
19257
- }
19258
- const removedWords = new Set(removed.flatMap(commentWords));
19259
- return candidates.filter((comment3) => {
19260
- const words = commentWords(comment3);
19261
- if (words.length === 0) return true;
19262
- return !words.every((word) => removedWords.has(word));
19263
- });
19264
- }
19265
- function commentWords(comment3) {
19266
- return comment3.toLowerCase().split(/[^a-z0-9]+/).filter((word) => word.length > 0);
19714
+ // src/commands/editHook/selectCommentExtractor.ts
19715
+ function selectCommentExtractor(filePath) {
19716
+ if (isHashCommentFile(filePath))
19717
+ return {
19718
+ extract: isShellFile(filePath) ? extractShellComments : extractYamlComments,
19719
+ marker: "#"
19720
+ };
19721
+ if (isCsharpFile(filePath))
19722
+ return { extract: extractCsharpCommentTexts, marker: "//" };
19723
+ if (isRazorFile(filePath))
19724
+ return { extract: extractRazorCommentTexts, marker: "//" };
19725
+ if (isSourceFile(filePath)) return { extract: extractComments, marker: "//" };
19726
+ return void 0;
19267
19727
  }
19268
19728
 
19269
19729
  // src/commands/editHook/decideCommentGuard.ts
@@ -19271,44 +19731,17 @@ function denyReason(marker) {
19271
19731
  const blockClause = marker === "//" ? ", no block comments" : "";
19272
19732
  return `This edit introduces a code comment (${marker}), which is blocked by the comment gate. Comments are a last resort \u2014 prefer a clearer name, a smaller function, or a test that makes the comment unnecessary. The comment must not appear in your edit itself. If this one line genuinely earns its keep, use the escape hatch: run \`assist code-comment set <file> <line> "<text>"\` (single line, max 50 chars${blockClause}) to get a pin, then \`assist code-comment confirm <pin>\` to insert it.`;
19273
19733
  }
19274
- function defined(values) {
19275
- return values.filter((value) => value != null);
19276
- }
19277
- function partitionStrings(input, existingContent) {
19278
- const { tool_name, tool_input } = input;
19279
- switch (tool_name) {
19280
- case "Edit":
19281
- return {
19282
- added: defined([tool_input.new_string]),
19283
- removed: defined([tool_input.old_string])
19284
- };
19285
- case "MultiEdit": {
19286
- const edits = tool_input.edits ?? [];
19287
- return {
19288
- added: defined(edits.map((e) => e.new_string)),
19289
- removed: defined(edits.map((e) => e.old_string))
19290
- };
19291
- }
19292
- case "Write":
19293
- return {
19294
- added: defined([tool_input.content]),
19295
- removed: defined([existingContent])
19296
- };
19297
- default:
19298
- return { added: [], removed: [] };
19299
- }
19300
- }
19301
19734
  function decideCommentGuard(input, existingContent) {
19302
- const filePath = input.tool_input.file_path;
19303
- const hash = isHashCommentFile(filePath);
19304
- if (!isSourceFile(filePath) && !hash) return void 0;
19305
- const extract2 = hash ? isShellFile(filePath) ? extractShellComments : extractYamlComments : extractComments;
19735
+ const { file_path, content } = input.tool_input;
19736
+ if (isGeneratedCsharpFile(file_path, content)) return void 0;
19737
+ const gate = selectCommentExtractor(file_path);
19738
+ if (!gate) return void 0;
19306
19739
  const { added, removed } = partitionStrings(input, existingContent);
19307
19740
  const introduced = introducedComments(
19308
- added.flatMap(extract2),
19309
- removed.flatMap(extract2)
19741
+ added.flatMap(gate.extract),
19742
+ removed.flatMap(gate.extract)
19310
19743
  );
19311
- return introduced.length > 0 ? denyReason(hash ? "#" : "//") : void 0;
19744
+ return introduced.length > 0 ? denyReason(gate.marker) : void 0;
19312
19745
  }
19313
19746
 
19314
19747
  // src/commands/dbMigration/consumeMigrationApproval.ts
@@ -31730,7 +32163,7 @@ function watchActivity(session, notify2, onClaudeSessionId) {
31730
32163
  }
31731
32164
  reconcileActivity(session.id, session.activity);
31732
32165
  let timer = null;
31733
- const read = () => {
32166
+ const read2 = () => {
31734
32167
  timer = null;
31735
32168
  const activity2 = readActivity(path73);
31736
32169
  if (!activity2) return;
@@ -31743,9 +32176,9 @@ function watchActivity(session, notify2, onClaudeSessionId) {
31743
32176
  session.activityWatcher = watch2(dir, (_event, filename) => {
31744
32177
  if (filename && !path73.endsWith(filename)) return;
31745
32178
  if (timer) clearTimeout(timer);
31746
- timer = setTimeout(read, DEBOUNCE_MS2);
32179
+ timer = setTimeout(read2, DEBOUNCE_MS2);
31747
32180
  });
31748
- if (existsSync75(path73)) read();
32181
+ if (existsSync75(path73)) read2();
31749
32182
  }
31750
32183
  function refreshActivity(session) {
31751
32184
  if (session.commandType !== "assist" || !session.cwd) return;
@@ -33573,7 +34006,7 @@ function allocateAndBind(ctx, cwd, create, options2 = {}, context) {
33573
34006
  }
33574
34007
 
33575
34008
  // src/commands/sessions/daemon/worktree/ensureWatcher.ts
33576
- function ensureWatcher(ctx, cwd, launchedFrom) {
34009
+ function ensureWatcher(ctx, cwd) {
33577
34010
  if (!cwd) return void 0;
33578
34011
  const repoRoot2 = findRepoRoot(cwd) ?? cwd;
33579
34012
  const cfg = worktreeConfigFor(repoRoot2);
@@ -33590,8 +34023,7 @@ function ensureWatcher(ctx, cwd, launchedFrom) {
33590
34023
  ctx,
33591
34024
  clone,
33592
34025
  (sid, resolvedCwd) => createWatcherSession(sid, resolvedCwd ?? clone),
33593
- { inPlace: true },
33594
- { launchedFrom }
34026
+ { inPlace: true }
33595
34027
  );
33596
34028
  daemonLog(
33597
34029
  `spawned watcher session ${id} running /watch in the clone ${clone}`
@@ -33689,7 +34121,7 @@ function reuseSessionForRun(session, itemId2, clients, onStatusChange, tree) {
33689
34121
  onStatusChange
33690
34122
  );
33691
34123
  if (!started) return;
33692
- if (tree) ensureWatcher(tree, originCwd, session.id);
34124
+ if (tree) ensureWatcher(tree, originCwd);
33693
34125
  attachReusedRun(session, alloc, clients, onStatusChange, tree);
33694
34126
  daemonLog(
33695
34127
  `session ${session.id} reused for backlog run ${itemId2}: ${session.name}`
@@ -34647,7 +35079,7 @@ function spawnAssistInTree(ctx, assistArgs, cwd, meta, context) {
34647
35079
  },
34648
35080
  context
34649
35081
  );
34650
- if (isBacklogRunArgs(assistArgs)) ensureWatcher(ctx, cwd, id);
35082
+ if (isBacklogRunArgs(assistArgs)) ensureWatcher(ctx, cwd);
34651
35083
  return id;
34652
35084
  }
34653
35085
  function resumeInTree(ctx, sessionId, cwd, name) {