Skip to content

Commit a347ec9

Browse files
committed
feat: Phase 6 - Community & Growth (v0.6.0)
- Interactive TUI: Rich-based CLI mode for browsing repos & approving PRs - Leaderboard: PR merge/close rate tracking with repo rankings - Multi-language Analyzers: 19 rules for JS/TS (7), Go (6), Rust (6) - Notification System: Slack/Discord/Telegram webhook support - 3 new CLI commands: interactive, leaderboard, notify-test - NotificationConfig with per-channel and event-type toggles - httpx dependency for notification HTTP clients - Tests: 30 new (total: 251)
1 parent 5f7c5b8 commit a347ec9

14 files changed

Lines changed: 1202 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
## [0.5.0] - 2026-03-18
10+
## [0.6.0] - 2026-03-18
11+
12+
### Added
13+
- **Interactive TUI**: Rich-based CLI interactive mode for browsing, selecting, and approving contributions
14+
- **Contribution Leaderboard**: PR merge/close rate tracking with repo rankings and type-based stats
15+
- **Multi-language Analyzers**: 19 analysis rules for JavaScript/TypeScript (7), Go (6), Rust (6)
16+
- **Notification System**: Slack webhook, Discord embeds, Telegram Bot API integration
17+
- 3 new CLI commands: `interactive`, `leaderboard`, `notify-test`
18+
- `NotificationConfig` in config with per-channel and event-type toggles
19+
- `httpx` dependency for notification HTTP clients
20+
- 30 new tests (total: 251 tests)
21+
22+
1123

1224
### Added
1325
- **Plugin System**: Entry-point based `AnalyzerPlugin` / `GeneratorPlugin` with auto-discovery

contribai/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"""ContribAI - AI Agent for Open Source Contributions."""
22

3-
__version__ = "0.5.0"
3+
__version__ = "0.6.0"
44
__app_name__ = "contribai"
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
"""Multi-language analysis rules and prompts.
2+
3+
Extends the core analysis system with language-specific
4+
security patterns, best practices, and code quality rules
5+
for JavaScript/TypeScript, Go, and Rust.
6+
"""
7+
8+
from __future__ import annotations
9+
10+
import logging
11+
from dataclasses import dataclass
12+
13+
logger = logging.getLogger(__name__)
14+
15+
16+
@dataclass
17+
class LanguageRule:
18+
"""A language-specific analysis rule."""
19+
20+
language: str
21+
category: str # security | code_quality | docs | performance
22+
name: str
23+
description: str
24+
pattern: str # what to look for
25+
severity: str = "medium"
26+
fix_hint: str = ""
27+
28+
29+
# ── JavaScript / TypeScript ──────────────────────────────
30+
31+
32+
JS_TS_RULES: list[LanguageRule] = [
33+
LanguageRule(
34+
language="javascript",
35+
category="security",
36+
name="eval-usage",
37+
description="Use of eval() is a security risk",
38+
pattern="eval(",
39+
severity="critical",
40+
fix_hint="Replace eval() with JSON.parse() or a safe alternative",
41+
),
42+
LanguageRule(
43+
language="javascript",
44+
category="security",
45+
name="innerHTML-xss",
46+
description="innerHTML can lead to XSS attacks",
47+
pattern="innerHTML",
48+
severity="high",
49+
fix_hint="Use textContent or a sanitization library like DOMPurify",
50+
),
51+
LanguageRule(
52+
language="javascript",
53+
category="security",
54+
name="no-prototype-pollution",
55+
description="Prototype pollution via __proto__",
56+
pattern="__proto__",
57+
severity="critical",
58+
fix_hint="Use Object.create(null) or validate input keys",
59+
),
60+
LanguageRule(
61+
language="typescript",
62+
category="code_quality",
63+
name="no-any-type",
64+
description="Avoid 'any' type - defeats TypeScript's purpose",
65+
pattern=": any",
66+
severity="medium",
67+
fix_hint="Use specific types or 'unknown' with type guards",
68+
),
69+
LanguageRule(
70+
language="javascript",
71+
category="code_quality",
72+
name="no-var",
73+
description="Use const/let instead of var",
74+
pattern="var ",
75+
severity="low",
76+
fix_hint="Replace 'var' with 'const' (immutable) or 'let' (mutable)",
77+
),
78+
LanguageRule(
79+
language="javascript",
80+
category="performance",
81+
name="no-sync-fs",
82+
description="Synchronous fs operations block the event loop",
83+
pattern="readFileSync",
84+
severity="medium",
85+
fix_hint="Use async fs.readFile() or fs.promises.readFile()",
86+
),
87+
LanguageRule(
88+
language="javascript",
89+
category="security",
90+
name="no-hardcoded-jwt-secret",
91+
description="Hardcoded JWT secret key",
92+
pattern="jwt.sign(",
93+
severity="high",
94+
fix_hint="Use environment variables for JWT secrets",
95+
),
96+
]
97+
98+
99+
# ── Go ───────────────────────────────────────────────
100+
101+
102+
GO_RULES: list[LanguageRule] = [
103+
LanguageRule(
104+
language="go",
105+
category="security",
106+
name="sql-injection",
107+
description="Potential SQL injection via string formatting",
108+
pattern='fmt.Sprintf("SELECT',
109+
severity="critical",
110+
fix_hint="Use parameterized queries with db.Query(sql, args...)",
111+
),
112+
LanguageRule(
113+
language="go",
114+
category="code_quality",
115+
name="unchecked-error",
116+
description="Ignoring error return values",
117+
pattern="_ = ",
118+
severity="medium",
119+
fix_hint="Handle errors explicitly: if err != nil { return err }",
120+
),
121+
LanguageRule(
122+
language="go",
123+
category="code_quality",
124+
name="defer-in-loop",
125+
description="Defer inside loop can cause resource leaks",
126+
pattern="defer ",
127+
severity="medium",
128+
fix_hint="Move defer outside the loop or use a wrapper function",
129+
),
130+
LanguageRule(
131+
language="go",
132+
category="security",
133+
name="tls-insecure-skip",
134+
description="TLS verification disabled",
135+
pattern="InsecureSkipVerify: true",
136+
severity="critical",
137+
fix_hint="Remove InsecureSkipVerify or use proper CA certificates",
138+
),
139+
LanguageRule(
140+
language="go",
141+
category="performance",
142+
name="goroutine-leak",
143+
description="Goroutine without context cancellation",
144+
pattern="go func()",
145+
severity="medium",
146+
fix_hint="Use context.WithCancel and select for graceful shutdown",
147+
),
148+
LanguageRule(
149+
language="go",
150+
category="docs",
151+
name="missing-package-doc",
152+
description="Package missing documentation comment",
153+
pattern="package ",
154+
severity="low",
155+
fix_hint="Add // Package <name> ... comment above package declaration",
156+
),
157+
]
158+
159+
160+
# ── Rust ──────────────────────────────────────────────
161+
162+
163+
RUST_RULES: list[LanguageRule] = [
164+
LanguageRule(
165+
language="rust",
166+
category="security",
167+
name="unsafe-block",
168+
description="Unsafe block bypasses Rust's safety guarantees",
169+
pattern="unsafe {",
170+
severity="high",
171+
fix_hint="Document why unsafe is necessary; use safe alternatives when possible",
172+
),
173+
LanguageRule(
174+
language="rust",
175+
category="code_quality",
176+
name="unwrap-panic",
177+
description="unwrap() panics on None/Err — use ? operator",
178+
pattern=".unwrap()",
179+
severity="medium",
180+
fix_hint="Use .unwrap_or(), .unwrap_or_default(), or the ? operator",
181+
),
182+
LanguageRule(
183+
language="rust",
184+
category="code_quality",
185+
name="expect-panic",
186+
description="expect() panics with message — use ? in libraries",
187+
pattern=".expect(",
188+
severity="low",
189+
fix_hint="Use ? operator in library code; expect() is OK in main/tests",
190+
),
191+
LanguageRule(
192+
language="rust",
193+
category="performance",
194+
name="clone-heavy",
195+
description="Excessive .clone() may indicate ownership issues",
196+
pattern=".clone()",
197+
severity="low",
198+
fix_hint="Use references (&T) or Cow<T> to avoid unnecessary cloning",
199+
),
200+
LanguageRule(
201+
language="rust",
202+
category="security",
203+
name="raw-pointer-deref",
204+
description="Raw pointer dereference is unsafe",
205+
pattern="*const ",
206+
severity="high",
207+
fix_hint="Use safe references (&T, &mut T) instead of raw pointers",
208+
),
209+
LanguageRule(
210+
language="rust",
211+
category="code_quality",
212+
name="todo-macro",
213+
description="todo!() macro will panic at runtime",
214+
pattern="todo!()",
215+
severity="medium",
216+
fix_hint="Implement the missing functionality or use unimplemented!()",
217+
),
218+
]
219+
220+
221+
# ── Registry ─────────────────────────────────────────
222+
223+
224+
ALL_RULES = JS_TS_RULES + GO_RULES + RUST_RULES
225+
226+
RULES_BY_LANGUAGE: dict[str, list[LanguageRule]] = {}
227+
for _rule in ALL_RULES:
228+
lang = _rule.language
229+
if lang not in RULES_BY_LANGUAGE:
230+
RULES_BY_LANGUAGE[lang] = []
231+
RULES_BY_LANGUAGE[lang].append(_rule)
232+
233+
234+
def get_rules_for_language(
235+
language: str,
236+
) -> list[LanguageRule]:
237+
"""Get analysis rules for a specific language."""
238+
lang = language.lower()
239+
rules = RULES_BY_LANGUAGE.get(lang, [])
240+
# TypeScript inherits JavaScript rules
241+
if lang == "typescript":
242+
rules = rules + RULES_BY_LANGUAGE.get("javascript", [])
243+
return rules
244+
245+
246+
def get_analysis_prompt(
247+
language: str,
248+
file_content: str,
249+
file_path: str,
250+
) -> str:
251+
"""Generate a language-specific analysis prompt."""
252+
rules = get_rules_for_language(language)
253+
if not rules:
254+
return ""
255+
256+
rule_descriptions = "\n".join(
257+
f"- [{r.severity.upper()}] {r.name}: {r.description} (look for: {r.pattern})" for r in rules
258+
)
259+
260+
return f"""Analyze this {language} file for issues:
261+
262+
File: {file_path}
263+
264+
Language-specific rules to check:
265+
{rule_descriptions}
266+
267+
For each issue found, provide:
268+
1. Rule name that triggered
269+
2. Line number(s)
270+
3. Description of the problem
271+
4. Suggested fix
272+
273+
Code:
274+
```
275+
{file_content[:5000]}
276+
```
277+
"""
278+
279+
280+
def get_supported_languages() -> list[str]:
281+
"""Get list of languages with specialized rules."""
282+
return sorted(set(RULES_BY_LANGUAGE.keys()))

0 commit comments

Comments
 (0)