@gulu9527/code-trust 0.2.0 → 0.2.1

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.d.ts CHANGED
@@ -105,6 +105,12 @@ declare class ScanEngine {
105
105
  declare function loadConfig(searchFrom?: string): Promise<CodeTrustConfig>;
106
106
  declare function generateDefaultConfig(): string;
107
107
 
108
+ interface Fix {
109
+ /** Byte range in the original file content [startOffset, endOffset) */
110
+ range: [number, number];
111
+ /** Replacement text (empty string = delete) */
112
+ text: string;
113
+ }
108
114
  interface Rule {
109
115
  id: string;
110
116
  category: RuleCategory;
@@ -112,6 +118,10 @@ interface Rule {
112
118
  title: string;
113
119
  description: string;
114
120
  check: (context: RuleContext) => Issue[];
121
+ /** Whether this rule supports auto-fix */
122
+ fixable?: boolean;
123
+ /** Generate a fix for a given issue. Returns null if unfixable. */
124
+ fix?: (context: RuleContext, issue: Issue) => Fix | null;
115
125
  }
116
126
  interface RuleContext {
117
127
  filePath: string;
package/dist/index.js CHANGED
@@ -524,6 +524,24 @@ function detectImmediateReassign(context, lines, issues) {
524
524
  }
525
525
  }
526
526
 
527
+ // src/rules/fix-utils.ts
528
+ function lineStartOffset(content, lineNumber) {
529
+ let offset = 0;
530
+ const lines = content.split("\n");
531
+ for (let i = 0; i < lineNumber - 1 && i < lines.length; i++) {
532
+ offset += lines[i].length + 1;
533
+ }
534
+ return offset;
535
+ }
536
+ function lineRange(content, lineNumber) {
537
+ const lines = content.split("\n");
538
+ const lineIndex = lineNumber - 1;
539
+ if (lineIndex < 0 || lineIndex >= lines.length) return [0, 0];
540
+ const start = lineStartOffset(content, lineNumber);
541
+ const end = start + lines[lineIndex].length + (lineIndex < lines.length - 1 ? 1 : 0);
542
+ return [start, end];
543
+ }
544
+
527
545
  // src/parsers/ast.ts
528
546
  import { parse, AST_NODE_TYPES } from "@typescript-eslint/typescript-estree";
529
547
 
@@ -706,6 +724,19 @@ var unusedVariablesRule = {
706
724
  severity: "low",
707
725
  title: "Unused variable detected",
708
726
  description: "AI-generated code sometimes declares variables that are never used, indicating incomplete or hallucinated logic.",
727
+ fixable: true,
728
+ fix(context, issue) {
729
+ const lines = context.fileContent.split("\n");
730
+ const lineIndex = issue.startLine - 1;
731
+ if (lineIndex < 0 || lineIndex >= lines.length) return null;
732
+ const line = lines[lineIndex].trim();
733
+ if (/^(const|let|var)\s+\w+\s*[=:;]/.test(line) && !line.includes(",")) {
734
+ const [start, end] = lineRange(context.fileContent, issue.startLine);
735
+ if (start === end) return null;
736
+ return { range: [start, end], text: "" };
737
+ }
738
+ return null;
739
+ },
709
740
  check(context) {
710
741
  const issues = [];
711
742
  let ast;
@@ -1429,6 +1460,23 @@ var unusedImportRule = {
1429
1460
  severity: "low",
1430
1461
  title: "Unused import",
1431
1462
  description: "AI-generated code often imports modules or identifiers that are never used in the file.",
1463
+ fixable: true,
1464
+ fix(context, issue) {
1465
+ const lines = context.fileContent.split("\n");
1466
+ const lineIndex = issue.startLine - 1;
1467
+ if (lineIndex < 0 || lineIndex >= lines.length) return null;
1468
+ const line = lines[lineIndex].trim();
1469
+ const isSingleDefault = /^import\s+\w+\s+from\s+/.test(line);
1470
+ const isSingleNamed = /^import\s*\{\s*\w+\s*\}\s*from\s+/.test(line);
1471
+ const isSingleTypeNamed = /^import\s+type\s*\{\s*\w+\s*\}\s*from\s+/.test(line);
1472
+ const isSingleTypeDefault = /^import\s+type\s+\w+\s+from\s+/.test(line);
1473
+ if (!isSingleDefault && !isSingleNamed && !isSingleTypeNamed && !isSingleTypeDefault) {
1474
+ return null;
1475
+ }
1476
+ const [start, end] = lineRange(context.fileContent, issue.startLine);
1477
+ if (start === end) return null;
1478
+ return { range: [start, end], text: "" };
1479
+ },
1432
1480
  check(context) {
1433
1481
  const issues = [];
1434
1482
  let ast;
@@ -1666,6 +1714,33 @@ var typeCoercionRule = {
1666
1714
  severity: "medium",
1667
1715
  title: "Loose equality with type coercion",
1668
1716
  description: "AI-generated code often uses == instead of ===, leading to implicit type coercion bugs.",
1717
+ fixable: true,
1718
+ fix(context, issue) {
1719
+ const lines = context.fileContent.split("\n");
1720
+ const lineIndex = issue.startLine - 1;
1721
+ if (lineIndex < 0 || lineIndex >= lines.length) return null;
1722
+ const line = lines[lineIndex];
1723
+ const base = lineStartOffset(context.fileContent, issue.startLine);
1724
+ const isNotEqual = issue.message.includes("!=");
1725
+ const searchOp = isNotEqual ? "!=" : "==";
1726
+ const replaceOp = isNotEqual ? "!==" : "===";
1727
+ let pos = -1;
1728
+ for (let j = 0; j < line.length - 1; j++) {
1729
+ if (line[j] === searchOp[0] && line[j + 1] === "=") {
1730
+ if (line[j + 2] === "=") {
1731
+ j += 2;
1732
+ continue;
1733
+ }
1734
+ if (!isNotEqual && j > 0 && (line[j - 1] === "!" || line[j - 1] === "<" || line[j - 1] === ">")) {
1735
+ continue;
1736
+ }
1737
+ pos = j;
1738
+ break;
1739
+ }
1740
+ }
1741
+ if (pos === -1) return null;
1742
+ return { range: [base + pos, base + pos + searchOp.length], text: replaceOp };
1743
+ },
1669
1744
  check(context) {
1670
1745
  const issues = [];
1671
1746
  const lines = context.fileContent.split("\n");
@@ -1920,6 +1995,12 @@ var noDebuggerRule = {
1920
1995
  severity: "high",
1921
1996
  title: "Debugger statement",
1922
1997
  description: "Debugger statements should never be committed to production code.",
1998
+ fixable: true,
1999
+ fix(context, issue) {
2000
+ const [start, end] = lineRange(context.fileContent, issue.startLine);
2001
+ if (start === end) return null;
2002
+ return { range: [start, end], text: "" };
2003
+ },
1923
2004
  check(context) {
1924
2005
  const issues = [];
1925
2006
  const lines = context.fileContent.split("\n");