@bytesbrains/pi-contrib-gate 1.7.0 → 1.8.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bytesbrains/pi-contrib-gate",
3
- "version": "1.7.0",
4
- "description": "Contribution gateway for AI agents \u2014 enforce branch naming, conventional commits, pre-commit quality gates, and PR automation.",
3
+ "version": "1.8.0",
4
+ "description": "Contribution gateway for AI agents enforce branch naming, conventional commits, pre-commit quality gates, and PR automation.",
5
5
  "keywords": [
6
6
  "pi-package",
7
7
  "pi-extension",
@@ -48,4 +48,4 @@
48
48
  "devDependencies": {
49
49
  "vitest": "^2.1.9"
50
50
  }
51
- }
51
+ }
package/src/config.ts CHANGED
@@ -71,6 +71,9 @@ export function loadConfig(cwd: string): ContribConfig {
71
71
  doctorAudit: result["quality.doctorAudit"] !== "false",
72
72
  maxFilesChanged: parseInt(result["quality.maxFilesChanged"] as string) || DEFAULT_CONFIG.quality.maxFilesChanged,
73
73
  maxLinesAdded: parseInt(result["quality.maxLinesAdded"] as string) || DEFAULT_CONFIG.quality.maxLinesAdded,
74
+ lensErrors: parseBool(result["quality.lensErrors"] as string, DEFAULT_CONFIG.quality.lensErrors),
75
+ maxLensErrors: parseInt(result["quality.maxLensErrors"] as string) || DEFAULT_CONFIG.quality.maxLensErrors,
76
+ secretScan: parseBool(result["quality.secretScan"] as string, DEFAULT_CONFIG.quality.secretScan),
74
77
  },
75
78
  requireIssueValidation: parseBool(result["requireIssueValidation"] as string, DEFAULT_CONFIG.requireIssueValidation),
76
79
  };
package/src/intercepts.ts CHANGED
@@ -90,6 +90,21 @@ export async function interceptToolCall(event: any, ctx: ExtensionContext) {
90
90
  };
91
91
  }
92
92
 
93
+ // Run secret scan on all direct commits (including main/dev)
94
+ if (config.quality.secretScan) {
95
+ const leak = exec("gitleaks protect --staged --no-banner 2>&1", ctx.cwd);
96
+ if (!leak.ok) {
97
+ const leakLines = leak.stdout
98
+ .split("\n")
99
+ .filter((l: string) => l.includes("Finding:") || l.includes("RuleID:") || l.includes("File:"))
100
+ .join("\n ");
101
+ return {
102
+ block: true,
103
+ reason: `Secrets/credentials detected in staged changes!\n ${leakLines}\n\n Remove secrets before committing. Add a .gitleaks.toml allowlist or gitleaks:allow comment for false positives.`,
104
+ };
105
+ }
106
+ }
107
+
93
108
  if (config.commits.convention === "conventional") {
94
109
  const msgMatch = cmd.match(/-m\s+"([^"]+)"/);
95
110
  if (msgMatch) {
package/src/types.ts CHANGED
@@ -28,6 +28,8 @@ export interface ContribConfig {
28
28
  lensErrors: boolean;
29
29
  /** Max allowed LSP errors in staged files (default: 0) */
30
30
  maxLensErrors: number;
31
+ /** Scan staged changes for secrets/credentials with gitleaks (default: true) */
32
+ secretScan: boolean;
31
33
  };
32
34
  /** Validate that the linked Gitea issue actually exists before starting work (default: true) */
33
35
  requireIssueValidation: boolean;
@@ -65,6 +67,7 @@ export const DEFAULT_CONFIG: ContribConfig = {
65
67
  maxLinesAdded: 500,
66
68
  lensErrors: true,
67
69
  maxLensErrors: 0,
70
+ secretScan: true,
68
71
  },
69
72
  requireIssueValidation: true,
70
73
  };
package/src/validate.ts CHANGED
@@ -48,6 +48,25 @@ export function runQualityGate(
48
48
  );
49
49
  }
50
50
 
51
+
52
+ // ── Secret / credential scan with gitleaks ─────────────────
53
+ if (config.quality.secretScan) {
54
+ const leak = exec("gitleaks protect --staged --no-banner 2>&1", cwd);
55
+ if (!leak.ok) {
56
+ // gitleaks exit code 1 = leaks found
57
+ const leakSummary = leak.stdout
58
+ .split("\n")
59
+ .filter((l) => l.includes("Finding:") || l.includes("RuleID:") || l.includes("File:"))
60
+ .join("\n ");
61
+ errors.push(
62
+ `Secrets/credentials detected in staged changes!\n ${leakSummary}\n\n Remove secrets from files before committing. If this is a false positive, add a .gitleaks.toml allowlist or use gitleaks:allow comment.`,
63
+ );
64
+ } else if (leak.stderr && !leak.stderr.includes("no leaks found")) {
65
+ // gitleaks might output to stderr even on success (warnings, etc.)
66
+ // Only flag as error if the exit code was non-zero, already handled above.
67
+ }
68
+ }
69
+
51
70
  const diff = exec("git diff --cached --name-only", cwd);
52
71
  if (diff.ok) {
53
72
  const files = diff.stdout.split("\n").filter(Boolean);