treliq 0.5.1 ā 0.7.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/README.md +214 -22
- package/dist/cli.js +130 -4
- package/dist/cli.js.map +1 -1
- package/dist/core/action-executor.d.ts +31 -0
- package/dist/core/action-executor.d.ts.map +1 -0
- package/dist/core/action-executor.js +148 -0
- package/dist/core/action-executor.js.map +1 -0
- package/dist/core/actions.d.ts +30 -0
- package/dist/core/actions.d.ts.map +1 -0
- package/dist/core/actions.js +116 -0
- package/dist/core/actions.js.map +1 -0
- package/dist/core/cache.d.ts +1 -1
- package/dist/core/cache.d.ts.map +1 -1
- package/dist/core/cache.js +2 -3
- package/dist/core/cache.js.map +1 -1
- package/dist/core/concurrency.d.ts +4 -0
- package/dist/core/concurrency.d.ts.map +1 -1
- package/dist/core/concurrency.js +11 -0
- package/dist/core/concurrency.js.map +1 -1
- package/dist/core/db.d.ts +23 -1
- package/dist/core/db.d.ts.map +1 -1
- package/dist/core/db.js +192 -0
- package/dist/core/db.js.map +1 -1
- package/dist/core/dedup.d.ts +7 -3
- package/dist/core/dedup.d.ts.map +1 -1
- package/dist/core/dedup.js +186 -43
- package/dist/core/dedup.js.map +1 -1
- package/dist/core/diff-analyzer.d.ts +19 -0
- package/dist/core/diff-analyzer.d.ts.map +1 -0
- package/dist/core/diff-analyzer.js +97 -0
- package/dist/core/diff-analyzer.js.map +1 -0
- package/dist/core/graphql.d.ts +14 -1
- package/dist/core/graphql.d.ts.map +1 -1
- package/dist/core/graphql.js +72 -1
- package/dist/core/graphql.js.map +1 -1
- package/dist/core/holistic-ranker.d.ts +14 -0
- package/dist/core/holistic-ranker.d.ts.map +1 -0
- package/dist/core/holistic-ranker.js +95 -0
- package/dist/core/holistic-ranker.js.map +1 -0
- package/dist/core/intent.d.ts +18 -0
- package/dist/core/intent.d.ts.map +1 -0
- package/dist/core/intent.js +111 -0
- package/dist/core/intent.js.map +1 -0
- package/dist/core/issue-scanner.d.ts +30 -0
- package/dist/core/issue-scanner.d.ts.map +1 -0
- package/dist/core/issue-scanner.js +139 -0
- package/dist/core/issue-scanner.js.map +1 -0
- package/dist/core/issue-scoring.d.ts +24 -0
- package/dist/core/issue-scoring.d.ts.map +1 -0
- package/dist/core/issue-scoring.js +171 -0
- package/dist/core/issue-scoring.js.map +1 -0
- package/dist/core/provider.d.ts +3 -0
- package/dist/core/provider.d.ts.map +1 -1
- package/dist/core/provider.js +34 -7
- package/dist/core/provider.js.map +1 -1
- package/dist/core/retryable-provider.d.ts +26 -0
- package/dist/core/retryable-provider.d.ts.map +1 -0
- package/dist/core/retryable-provider.js +75 -0
- package/dist/core/retryable-provider.js.map +1 -0
- package/dist/core/scanner.d.ts +6 -1
- package/dist/core/scanner.d.ts.map +1 -1
- package/dist/core/scanner.js +136 -28
- package/dist/core/scanner.js.map +1 -1
- package/dist/core/scoring.d.ts +5 -1
- package/dist/core/scoring.d.ts.map +1 -1
- package/dist/core/scoring.js +93 -5
- package/dist/core/scoring.js.map +1 -1
- package/dist/core/semantic-matcher.d.ts +18 -0
- package/dist/core/semantic-matcher.d.ts.map +1 -0
- package/dist/core/semantic-matcher.js +106 -0
- package/dist/core/semantic-matcher.js.map +1 -0
- package/dist/core/types.d.ts +56 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/vision.d.ts +3 -0
- package/dist/core/vision.d.ts.map +1 -1
- package/dist/core/vision.js +23 -0
- package/dist/core/vision.js.map +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/app.js +29 -0
- package/dist/server/app.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
<img src="docs/logo.png" alt="Treliq" width="140" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
<h3 align="center">AI-Powered PR Triage for
|
|
5
|
+
<h3 align="center">AI-Powered PR & Issue Triage for Maintainers & Enterprise Teams</h3>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<em>"
|
|
8
|
+
<em>"Too many open PRs and issues. Which ones should I review, merge, or close first?"</em>
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
12
|
<a href="https://www.npmjs.com/package/treliq"><img src="https://img.shields.io/npm/v/treliq?style=flat-square&color=CB3837&logo=npm" alt="npm version" /></a>
|
|
13
13
|
<a href="https://www.npmjs.com/package/treliq"><img src="https://img.shields.io/npm/dm/treliq?style=flat-square&color=CB3837" alt="npm downloads" /></a>
|
|
14
14
|
<a href="https://github.com/mahsumaktas/treliq/actions"><img src="https://img.shields.io/github/actions/workflow/status/mahsumaktas/treliq/ci.yml?branch=main&style=flat-square" alt="CI" /></a>
|
|
15
|
-
<img src="https://img.shields.io/badge/tests-
|
|
16
|
-
<img src="https://img.shields.io/badge/signals-
|
|
15
|
+
<img src="https://img.shields.io/badge/tests-384_passing-2DA44E?style=flat-square" alt="Tests" />
|
|
16
|
+
<img src="https://img.shields.io/badge/signals-21-8B5CF6?style=flat-square" alt="21 Signals" />
|
|
17
17
|
<img src="https://img.shields.io/badge/providers-4-FF6600?style=flat-square" alt="4 Providers" />
|
|
18
18
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="License: MIT" /></a>
|
|
19
19
|
<img src="https://img.shields.io/badge/TypeScript-5.7-3178C6?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript" />
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
26
|
-
Treliq is an intelligent
|
|
26
|
+
Treliq is an intelligent triage system that **deduplicates, scores, and ranks** pull requests and issues so maintainers can focus on what matters. Diff-aware code analysis, semantic issue-PR matching, tournament-style holistic re-ranking, auto-close duplicates, auto-merge high-quality PRs, and auto-label by intent. Available as a **CLI tool**, **persistent server with REST API**, and **GitHub Action**.
|
|
27
27
|
|
|
28
28
|
## The Problem
|
|
29
29
|
|
|
@@ -32,8 +32,134 @@ Existing tools review code (CodeRabbit, Greptile, Copilot). None answer the main
|
|
|
32
32
|
- **"These 5 PRs fix the same bug ā which one is best?"**
|
|
33
33
|
- **"Does this PR align with our roadmap?"**
|
|
34
34
|
- **"Show me the top 10 PRs I should review today."**
|
|
35
|
+
- **"Which issues have linked PRs? Which are stale?"**
|
|
36
|
+
- **"Auto-close all the duplicate PRs and spam issues."**
|
|
35
37
|
|
|
36
|
-
Code Review ā PR Triage. Treliq fills the gap
|
|
38
|
+
**Code Review ā PR Triage. Treliq fills the gap.**
|
|
39
|
+
|
|
40
|
+
### šÆ Who is this for?
|
|
41
|
+
- **Enterprise Engineering Teams:** Weekly release cut-offs approaching? Stop guessing which 20 PRs to merge. Treliq prioritizes bug fixes, high test coverage, and small diffs.
|
|
42
|
+
- **Open Source Maintainers:** Drowning in open PRs from random contributors? Automatically detect duplicate attempts, filter out spam, and prioritize trusted contributors.
|
|
43
|
+
- **Platform/DevOps Teams:** Run Treliq as a central server across multiple internal repositories and provide a unified PR dashboard for the whole company.
|
|
44
|
+
|
|
45
|
+
### ā” Zero Setup / Free Mode
|
|
46
|
+
Not ready to trust your codebase with an LLM? Try the 100% free, local heuristic engine with zero API keys required.
|
|
47
|
+
```bash
|
|
48
|
+
# Score PRs based on 21 signals (CI, coverage, conflicts, intent, etc.) completely locally
|
|
49
|
+
npx treliq scan -r owner/repo --no-llm
|
|
50
|
+
|
|
51
|
+
# Also scan issues alongside PRs
|
|
52
|
+
npx treliq scan -r owner/repo --no-llm --include-issues
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## What's New in v0.7.0
|
|
56
|
+
|
|
57
|
+
### Accuracy Pipeline (5 New Stages)
|
|
58
|
+
Code-aware, semantically intelligent scoring ā goes beyond metadata to analyze actual diffs:
|
|
59
|
+
|
|
60
|
+
| Stage | What it does |
|
|
61
|
+
|-------|-------------|
|
|
62
|
+
| **Diff-Aware Scoring** | Fetches PR diffs via GitHub API, LLM analyzes code quality (0-100), risk assessment, change type, and affected areas. New 3-way blend: `0.4 heuristic + 0.3 LLM text + 0.3 LLM diff` |
|
|
63
|
+
| **Intent-Aware Profiles** | 6 weight profiles (bugfix, feature, refactor, dependency, docs, chore) automatically adjust signal weights. Bugfix PRs boost CI/test weights; docs PRs reduce them. Weights normalized to sum=1.0 |
|
|
64
|
+
| **LLM Dedup Verification** | After embedding clusters, LLM verifies "are these really duplicates?" with subgroup splitting. Dissolves false positives, splits mixed clusters, selects best item per group. Max 20 clusters |
|
|
65
|
+
| **Issue-PR Semantic Matching** | Compares issue body + PR diff via LLM. Match quality: full/partial/unrelated. Bidirectional: PRs get +8/+3/-5 bonus, issues get linked_pr signal update (95/70/40) |
|
|
66
|
+
| **Holistic Re-ranking** | Tournament-style cross-item comparison: groups of 50 ā LLM picks top 10 per group ā finalists ā top 15. Rank bonus: #1 gets +30 points, #15 gets +2 |
|
|
67
|
+
|
|
68
|
+
All stages are LLM-optional ā `--no-llm` mode uses pure heuristic scoring with zero API calls.
|
|
69
|
+
|
|
70
|
+
**Full pipeline flow:**
|
|
71
|
+
```
|
|
72
|
+
Fetch PRs ā Score (21 signals + intent profiles) ā Diff Analysis ā LLM Blend
|
|
73
|
+
ā Dedup (embeddings + LLM verify) ā Vision Check ā Semantic Matching ā Holistic Re-rank ā Output
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Intent Classification (Signal #21)
|
|
77
|
+
3-tier detection pipeline classifies every PR and issue into one of 6 categories:
|
|
78
|
+
|
|
79
|
+
| Category | Score | Example |
|
|
80
|
+
|----------|-------|---------|
|
|
81
|
+
| bugfix | 90 | `fix: resolve memory leak in scanner` |
|
|
82
|
+
| feature | 85 | `feat: add dark mode toggle` |
|
|
83
|
+
| refactor | 60 | `refactor: extract scoring engine` |
|
|
84
|
+
| dependency | 35 | `chore(deps): bump express to v5` |
|
|
85
|
+
| docs | 30 | `docs: update API reference` |
|
|
86
|
+
| chore | 25 | `ci: add coverage upload step` |
|
|
87
|
+
|
|
88
|
+
Detection priority: **Conventional commit prefix** (100% confidence) ā **LLM classification** (with JSON parsing) ā **Heuristic keyword matching** (fallback).
|
|
89
|
+
|
|
90
|
+
### Full Issue Triage
|
|
91
|
+
Issues are now first-class citizens. Scan, score, deduplicate, and take action on issues alongside PRs.
|
|
92
|
+
|
|
93
|
+
- **`scan-issues` command** ā Standalone issue scanning with 12 dedicated signals
|
|
94
|
+
- **`--include-issues` flag** ā Scan issues alongside PRs in a single `scan` run
|
|
95
|
+
- **Cross-type dedup** ā PRs and issues embedded in the same vector space; clusters can be `pr`, `issue`, or `mixed`
|
|
96
|
+
- **12 Issue Signals**: staleness, body quality, label priority, activity, contributor trust, spam detection, milestone, reactions, linked PR status, assignee, reproducibility info, intent
|
|
97
|
+
|
|
98
|
+
### Auto-Actions Engine
|
|
99
|
+
Two-phase architecture: **ActionEngine** plans actions (pure, testable), **ActionExecutor** executes via GitHub API (re-fetches state before each action).
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# Preview what would happen (dry-run, default)
|
|
103
|
+
npx treliq scan -r owner/repo --auto-close-dupes --auto-close-spam --auto-merge --auto-label-intent
|
|
104
|
+
|
|
105
|
+
# Execute for real
|
|
106
|
+
npx treliq scan -r owner/repo --auto-close-dupes --auto-close-spam --auto-merge --auto-label-intent --confirm
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
| Action | Flag | Description |
|
|
110
|
+
|--------|------|-------------|
|
|
111
|
+
| Close duplicates | `--auto-close-dupes` | Keeps best-scored item in each cluster, closes the rest with comment |
|
|
112
|
+
| Close spam | `--auto-close-spam` | Closes items flagged as spam |
|
|
113
|
+
| Auto-merge | `--auto-merge` | Merges PRs with score >= threshold, approved, CI pass, no conflicts |
|
|
114
|
+
| Label intent | `--auto-label-intent` | Applies `intent:bugfix`, `intent:feature`, etc. labels |
|
|
115
|
+
|
|
116
|
+
Safety: **dry-run by default**, `--confirm` required for execution, batch limit 50, `--exclude` list, stale state re-check before each action.
|
|
117
|
+
|
|
118
|
+
### Test Suite (384 tests, 28 suites)
|
|
119
|
+
|
|
120
|
+
| Category | Tests | Suites |
|
|
121
|
+
|----------|-------|--------|
|
|
122
|
+
| **Accuracy pipeline** | 39 | DiffAnalyzer (7), IntentProfiles (8), DedupVerification (6), SemanticMatcher (7), HolisticRanker (7), Scoring blend (4) |
|
|
123
|
+
| **Intent & issue triage** | 71 | IntentClassifier (24), IssueScoringEngine (14), ActionEngine (18), ActionExecutor (18), IssueScanner (6), Issue GraphQL (5), cross-type dedup (4), intent signal (6) |
|
|
124
|
+
| **Core scoring & pipeline** | 274 | 21-signal scoring, concurrency, rate limiting, caching, batch embedding, dedup, vision, webhooks, auth, config, DB |
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## What's New in v0.6.0
|
|
129
|
+
|
|
130
|
+
### Performance Pipeline Redesign
|
|
131
|
+
Full rewrite of the scan pipeline for large-scale repos (1000+ PRs). First scan drops from ~140 min to ~15-20 min; incremental scans finish in ~5-8 min.
|
|
132
|
+
|
|
133
|
+
### Parallel Pipeline
|
|
134
|
+
- **Dedup + Vision run concurrently** via `Promise.all` (previously sequential)
|
|
135
|
+
- Each stage uses `ConcurrencyController` for internal parallelism
|
|
136
|
+
|
|
137
|
+
### Batch Embedding
|
|
138
|
+
- **Gemini** `batchEmbedContents` API ā 100 embeddings per call
|
|
139
|
+
- **OpenAI** array input ā 100 embeddings per call
|
|
140
|
+
- Automatic fallback to parallel individual embedding if batch fails
|
|
141
|
+
|
|
142
|
+
### RetryableProvider
|
|
143
|
+
- New wrapper around any LLM provider with exponential backoff + jitter
|
|
144
|
+
- Detects HTTP 429 and respects `Retry-After` headers
|
|
145
|
+
- Non-retryable status codes (400/401/403/404/422) fail fast
|
|
146
|
+
- `onThrottle` callback wired to adaptive concurrency controllers
|
|
147
|
+
|
|
148
|
+
### Adaptive Concurrency
|
|
149
|
+
- `ConcurrencyController.throttle()` halves parallelism on rate-limit hits
|
|
150
|
+
- `ConcurrencyController.recover()` increments back toward initial max
|
|
151
|
+
- Shared controllers across dedup + vision, auto-throttled via RetryableProvider
|
|
152
|
+
|
|
153
|
+
### Expanded Cache
|
|
154
|
+
- Embedding vectors and vision results now persisted in cache
|
|
155
|
+
- Incremental scans skip re-embedding and re-checking cached PRs
|
|
156
|
+
- Compact JSON format (no pretty-print) reduces cache file size
|
|
157
|
+
|
|
158
|
+
### Test Suite (244 tests)
|
|
159
|
+
- 17 test suites, 244/244 passing
|
|
160
|
+
- RetryableProvider, batch embedding, adaptive concurrency, parallel dedup/vision, expanded cache
|
|
161
|
+
|
|
162
|
+
---
|
|
37
163
|
|
|
38
164
|
## What's New in v0.5.1
|
|
39
165
|
|
|
@@ -105,7 +231,7 @@ Tested on OpenClaw PRs with 4 models:
|
|
|
105
231
|
- š” **Real-time SSE** ā Live dashboard updates via Server-Sent Events
|
|
106
232
|
- š **GitHub Webhooks** ā Auto-score PRs on open/update/close with HMAC-SHA256 verification
|
|
107
233
|
- š **GraphQL Fetching** ā ~80% fewer API calls using GitHub's GraphQL API
|
|
108
|
-
- š **
|
|
234
|
+
- š **21-Signal Scoring** ā Includes Scope Coherence, PR Complexity, and Intent analysis
|
|
109
235
|
- šļø **SQLite Persistence** ā Full scan history, PR state tracking, repository management
|
|
110
236
|
- ā” **Parallel LLM Scoring** ā Concurrency-controlled parallel scoring with configurable limits
|
|
111
237
|
- š¦ **Rate Limit Manager** ā Intelligent GitHub API pacing with automatic backoff
|
|
@@ -138,12 +264,19 @@ graph TB
|
|
|
138
264
|
end
|
|
139
265
|
|
|
140
266
|
subgraph Core
|
|
141
|
-
Scanner[Scanner]
|
|
142
|
-
|
|
267
|
+
Scanner[PR Scanner]
|
|
268
|
+
IScan[Issue Scanner]
|
|
269
|
+
Scoring[21-Signal PR Scoring]
|
|
270
|
+
IScore[12-Signal Issue Scoring]
|
|
271
|
+
Intent[Intent Classifier]
|
|
143
272
|
LLM[Multi-Provider LLM<br/>Gemini Ā· OpenAI Ā· Anthropic Ā· OpenRouter]
|
|
144
|
-
|
|
273
|
+
Diff[Diff Analyzer<br/>Code Quality Ā· Risk]
|
|
274
|
+
Dedup[Cross-type Dedup<br/>LanceDB + LLM Verify]
|
|
145
275
|
Vision[Vision Doc Alignment]
|
|
146
|
-
|
|
276
|
+
Semantic[Semantic Matcher<br/>Issue-PR Resolution]
|
|
277
|
+
Holistic[Holistic Ranker<br/>Tournament Re-ranking]
|
|
278
|
+
Actions[Action Engine<br/>close Ā· merge Ā· label]
|
|
279
|
+
Executor[Action Executor]
|
|
147
280
|
end
|
|
148
281
|
|
|
149
282
|
subgraph Persistence
|
|
@@ -165,19 +298,33 @@ graph TB
|
|
|
165
298
|
end
|
|
166
299
|
|
|
167
300
|
GH_REST & GH_GQL --> Scanner
|
|
301
|
+
GH_REST & GH_GQL --> IScan
|
|
168
302
|
WH --> REST
|
|
169
303
|
Scanner --> Scoring
|
|
304
|
+
IScan --> IScore
|
|
305
|
+
Scoring --> Intent
|
|
306
|
+
IScore --> Intent
|
|
170
307
|
Scoring --> LLM
|
|
171
|
-
|
|
308
|
+
IScore --> LLM
|
|
309
|
+
Scanner --> Diff
|
|
310
|
+
Diff --> LLM
|
|
311
|
+
Diff --> Scoring
|
|
312
|
+
Scanner & IScan --> Dedup
|
|
313
|
+
Dedup --> LLM
|
|
172
314
|
Scanner --> Vision
|
|
173
|
-
|
|
174
|
-
|
|
315
|
+
Semantic --> LLM
|
|
316
|
+
Scanner & IScan --> Semantic
|
|
317
|
+
Scanner & IScan --> Holistic
|
|
318
|
+
Holistic --> LLM
|
|
319
|
+
Scoring & IScore --> SQLite
|
|
320
|
+
Actions --> Executor
|
|
321
|
+
Executor --> GH_REST
|
|
175
322
|
Cache --> Scanner
|
|
176
323
|
REST --> Scanner
|
|
177
324
|
REST --> SSE
|
|
178
325
|
Scheduler --> Scanner
|
|
179
326
|
Scheduler --> Notif
|
|
180
|
-
CLI --> Scanner
|
|
327
|
+
CLI --> Scanner & IScan & Actions
|
|
181
328
|
Dashboard --> REST
|
|
182
329
|
Dashboard --> SSE
|
|
183
330
|
Action --> CLI
|
|
@@ -204,6 +351,12 @@ npx treliq score -r owner/repo -n 123 -f markdown
|
|
|
204
351
|
# Scan all open PRs (up to 100)
|
|
205
352
|
npx treliq scan -r owner/repo -m 100 -f json
|
|
206
353
|
|
|
354
|
+
# Scan PRs + Issues together
|
|
355
|
+
npx treliq scan -r owner/repo --include-issues -f json
|
|
356
|
+
|
|
357
|
+
# Scan only issues
|
|
358
|
+
npx treliq scan-issues -r owner/repo -m 200 -f table
|
|
359
|
+
|
|
207
360
|
# Find duplicate PR clusters
|
|
208
361
|
npx treliq dedup -r owner/repo
|
|
209
362
|
|
|
@@ -211,6 +364,25 @@ npx treliq dedup -r owner/repo
|
|
|
211
364
|
npx treliq scan -r owner/repo --trust-contributors
|
|
212
365
|
```
|
|
213
366
|
|
|
367
|
+
### Auto-Actions
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
# Preview auto-actions (dry-run ā safe, no changes made)
|
|
371
|
+
npx treliq scan -r owner/repo \
|
|
372
|
+
--auto-close-dupes \
|
|
373
|
+
--auto-close-spam \
|
|
374
|
+
--auto-merge --merge-threshold 90 --merge-method squash \
|
|
375
|
+
--auto-label-intent
|
|
376
|
+
|
|
377
|
+
# Execute auto-actions for real
|
|
378
|
+
npx treliq scan -r owner/repo \
|
|
379
|
+
--auto-close-dupes --auto-close-spam --auto-merge \
|
|
380
|
+
--auto-label-intent --confirm
|
|
381
|
+
|
|
382
|
+
# Exclude specific items from auto-actions
|
|
383
|
+
npx treliq scan -r owner/repo --auto-close-dupes --confirm --exclude 42,99,101
|
|
384
|
+
```
|
|
385
|
+
|
|
214
386
|
### Server Mode
|
|
215
387
|
|
|
216
388
|
```bash
|
|
@@ -241,6 +413,7 @@ The server exposes:
|
|
|
241
413
|
| `POST /api/repos/:owner/:repo/scan` | Trigger a new scan |
|
|
242
414
|
| `GET /api/repos/:owner/:repo/scans` | Scan history |
|
|
243
415
|
| `GET /api/repos/:owner/:repo/spam` | Spam PRs |
|
|
416
|
+
| `GET /api/repos/:owner/:repo/issues` | List scored issues (sortable, filterable) |
|
|
244
417
|
| `GET /api/events` | SSE real-time stream |
|
|
245
418
|
| `POST /webhooks` | GitHub webhook receiver |
|
|
246
419
|
| `GET /setup` | GitHub App setup guide |
|
|
@@ -263,7 +436,7 @@ npx treliq scan -r owner/repo -p openai --api-key sk-...
|
|
|
263
436
|
# Anthropic (embeddings auto-fallback to Gemini/OpenAI)
|
|
264
437
|
npx treliq scan -r owner/repo -p anthropic --api-key sk-ant-...
|
|
265
438
|
|
|
266
|
-
# Heuristic-only (no API keys needed,
|
|
439
|
+
# Heuristic-only (no API keys needed, 21 signals)
|
|
267
440
|
npx treliq scan -r owner/repo --no-llm
|
|
268
441
|
```
|
|
269
442
|
|
|
@@ -281,7 +454,7 @@ npx treliq init
|
|
|
281
454
|
# See example output
|
|
282
455
|
npx treliq demo
|
|
283
456
|
|
|
284
|
-
# Heuristic-only scoring (
|
|
457
|
+
# Heuristic-only scoring (21 signals, no LLM)
|
|
285
458
|
npx treliq scan -r owner/repo --no-llm
|
|
286
459
|
```
|
|
287
460
|
|
|
@@ -334,7 +507,7 @@ jobs:
|
|
|
334
507
|
});
|
|
335
508
|
```
|
|
336
509
|
|
|
337
|
-
##
|
|
510
|
+
## 21-Signal PR Scoring
|
|
338
511
|
|
|
339
512
|
| # | Signal | Weight | Description |
|
|
340
513
|
|---|--------|--------|-------------|
|
|
@@ -356,10 +529,28 @@ jobs:
|
|
|
356
529
|
| 16 | Body Quality | 0.04 | Description length, checklists, screenshots |
|
|
357
530
|
| 17 | Activity | 0.04 | Comment count ā engagement signal |
|
|
358
531
|
| 18 | Breaking Change | 0.04 | Risky files, large deletions, `!:` in title |
|
|
359
|
-
| 19 |
|
|
360
|
-
| 20 |
|
|
532
|
+
| 19 | Scope Coherence | 0.06 | Directory spread, title-to-files alignment |
|
|
533
|
+
| 20 | PR Complexity | 0.05 | Size analysis, AI detection, overengineering |
|
|
534
|
+
| 21 | **Intent** | 0.15 | bugfix/feature/refactor/dependency/docs/chore classification |
|
|
535
|
+
|
|
536
|
+
When an LLM provider is configured, scores are blended: **40% heuristic + 30% LLM text + 30% LLM diff** (with diff analysis) or **40% heuristic + 60% LLM** (without diff). Intent-aware profiles automatically adjust signal weights based on PR category (e.g., bugfix PRs boost CI/test weights).
|
|
361
537
|
|
|
362
|
-
|
|
538
|
+
### 12-Signal Issue Scoring
|
|
539
|
+
|
|
540
|
+
| # | Signal | Weight | Description |
|
|
541
|
+
|---|--------|--------|-------------|
|
|
542
|
+
| 1 | Staleness | 0.08 | Days since opened ā fresh issues preferred |
|
|
543
|
+
| 2 | Body Quality | 0.08 | Description length, checklists |
|
|
544
|
+
| 3 | Label Priority | 0.07ā0.10 | High-priority labels (bug, p0, security) boosted |
|
|
545
|
+
| 4 | Activity | 0.08 | Comment count ā engagement signal |
|
|
546
|
+
| 5 | Contributor Trust | 0.08 | Author association (owner/member/contributor) |
|
|
547
|
+
| 6 | Spam Detection | 0.10 | Empty body, short title, AI language markers |
|
|
548
|
+
| 7 | Milestone | 0.07 | Issues attached to milestones score higher |
|
|
549
|
+
| 8 | Reactions | 0.10 | Community interest via emoji reactions |
|
|
550
|
+
| 9 | Linked PR | 0.08 | Has linked PR(s) attempting to resolve |
|
|
551
|
+
| 10 | Assignee | 0.07 | Assigned = someone is working on it |
|
|
552
|
+
| 11 | Reproducibility | 0.07 | Steps to reproduce, expected/actual, code blocks |
|
|
553
|
+
| 12 | Intent | 0.09 | bugfix/feature/refactor/dependency/docs/chore |
|
|
363
554
|
|
|
364
555
|
## Configuration
|
|
365
556
|
|
|
@@ -445,5 +636,6 @@ MIT Ā© [Mahsum AktaÅ](https://github.com/mahsumaktas)
|
|
|
445
636
|
---
|
|
446
637
|
|
|
447
638
|
<p align="center">
|
|
448
|
-
<em>Built because 3,100 PRs won't triage themselves.</em>
|
|
639
|
+
<em>Built because 3,100 PRs and 2,000 issues won't triage themselves.</em>
|
|
449
640
|
</p>
|
|
641
|
+
<!-- webhook test Sun Feb 22 03:35:33 +03 2026 -->
|
package/dist/cli.js
CHANGED
|
@@ -293,8 +293,8 @@ function outputScoredPR(scored, format) {
|
|
|
293
293
|
}
|
|
294
294
|
program
|
|
295
295
|
.name('treliq')
|
|
296
|
-
.description('AI-Powered PR Triage for
|
|
297
|
-
.version('0.
|
|
296
|
+
.description('AI-Powered PR Triage for Maintainers & Enterprise Teams')
|
|
297
|
+
.version('0.7.0');
|
|
298
298
|
program
|
|
299
299
|
.command('init')
|
|
300
300
|
.description('Interactive setup wizard for Treliq')
|
|
@@ -394,7 +394,7 @@ program
|
|
|
394
394
|
.option('-t, --token <token>', 'GitHub token (or GITHUB_TOKEN env)')
|
|
395
395
|
.option('-k, --gemini-key <key>', 'Gemini API key (or GEMINI_API_KEY env)')
|
|
396
396
|
.option('-p, --provider <name>', 'LLM provider: gemini|openai|anthropic|openrouter|none')
|
|
397
|
-
.option('
|
|
397
|
+
.option('--model <name>', 'LLM model name (overrides provider default)')
|
|
398
398
|
.option('--api-key <key>', 'API key for the selected provider')
|
|
399
399
|
.option('--no-llm', 'Disable LLM scoring and use heuristic-only mode')
|
|
400
400
|
.option('-f, --format <format>', 'Output format: table|json|markdown', 'table')
|
|
@@ -403,6 +403,15 @@ program
|
|
|
403
403
|
.option('--trust-contributors', 'Exempt known contributors from spam detection', false)
|
|
404
404
|
.option('--no-cache', 'Force full rescan, ignore cache')
|
|
405
405
|
.option('--cache-file <path>', 'Custom cache file path', '.treliq-cache.json')
|
|
406
|
+
.option('--include-issues', 'Also scan and triage issues', false)
|
|
407
|
+
.option('--auto-close-dupes', 'Close duplicate PRs/Issues (dry-run without --confirm)', false)
|
|
408
|
+
.option('--auto-close-spam', 'Close spam PRs/Issues (dry-run without --confirm)', false)
|
|
409
|
+
.option('--auto-merge', 'Merge high-score approved PRs (dry-run without --confirm)', false)
|
|
410
|
+
.option('--merge-threshold <score>', 'Min score for auto-merge', '85')
|
|
411
|
+
.option('--merge-method <method>', 'Merge method: squash|merge|rebase', 'squash')
|
|
412
|
+
.option('--auto-label-intent', 'Label PRs/Issues by intent', false)
|
|
413
|
+
.option('--confirm', 'Execute auto-actions (not just dry-run)', false)
|
|
414
|
+
.option('--exclude <numbers>', 'Comma-separated PR/Issue numbers to exclude')
|
|
406
415
|
.action(async (opts) => {
|
|
407
416
|
const config = makeConfig(opts);
|
|
408
417
|
if (!config.token) {
|
|
@@ -415,6 +424,123 @@ program
|
|
|
415
424
|
const scanner = new scanner_1.TreliqScanner(config);
|
|
416
425
|
const result = await scanner.scan();
|
|
417
426
|
outputResult(result, config.outputFormat);
|
|
427
|
+
// Issue scanning (if --include-issues)
|
|
428
|
+
if (opts.includeIssues) {
|
|
429
|
+
const { IssueScanner } = await Promise.resolve().then(() => __importStar(require('./core/issue-scanner')));
|
|
430
|
+
const issueScanner = new IssueScanner({
|
|
431
|
+
repo: config.repo,
|
|
432
|
+
token: config.token,
|
|
433
|
+
maxIssues: config.maxPRs,
|
|
434
|
+
provider: config.provider,
|
|
435
|
+
});
|
|
436
|
+
const scoredIssues = await issueScanner.scan(result.rankedPRs);
|
|
437
|
+
result.rankedIssues = scoredIssues;
|
|
438
|
+
result.totalIssues = scoredIssues.length;
|
|
439
|
+
console.log(`\nš ${scoredIssues.length} issues scanned.`);
|
|
440
|
+
}
|
|
441
|
+
// Auto-actions
|
|
442
|
+
const hasAutoActions = opts.autoCloseDupes || opts.autoCloseSpam || opts.autoMerge || opts.autoLabelIntent;
|
|
443
|
+
if (hasAutoActions) {
|
|
444
|
+
try {
|
|
445
|
+
const { ActionEngine } = await Promise.resolve().then(() => __importStar(require('./core/actions')));
|
|
446
|
+
const exclude = opts.exclude ? opts.exclude.split(',').map(n => parseInt(n.trim(), 10)).filter(n => !isNaN(n) && n > 0) : [];
|
|
447
|
+
const mergeThreshold = parseInt(opts.mergeThreshold ?? '85', 10);
|
|
448
|
+
const mergeMethod = (opts.mergeMethod ?? 'squash');
|
|
449
|
+
const engine = new ActionEngine({
|
|
450
|
+
mergeThreshold,
|
|
451
|
+
mergeMethod,
|
|
452
|
+
exclude,
|
|
453
|
+
});
|
|
454
|
+
// Collect all items for action planning
|
|
455
|
+
const allItems = [...result.rankedPRs];
|
|
456
|
+
if (result.rankedIssues)
|
|
457
|
+
allItems.push(...result.rankedIssues);
|
|
458
|
+
const actions = [];
|
|
459
|
+
if (opts.autoCloseDupes)
|
|
460
|
+
actions.push(...engine.planCloseDuplicates(allItems, result.duplicateClusters));
|
|
461
|
+
if (opts.autoCloseSpam)
|
|
462
|
+
actions.push(...engine.planCloseSpam(allItems));
|
|
463
|
+
if (opts.autoMerge)
|
|
464
|
+
actions.push(...engine.planAutoMerge(result.rankedPRs));
|
|
465
|
+
if (opts.autoLabelIntent)
|
|
466
|
+
actions.push(...engine.planLabelIntent(allItems));
|
|
467
|
+
if (!opts.confirm) {
|
|
468
|
+
console.log('\n' + engine.formatDryRun(actions));
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
if (actions.length === 0) {
|
|
472
|
+
console.log('\nNo actions to execute.');
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
const { ActionExecutor } = await Promise.resolve().then(() => __importStar(require('./core/action-executor')));
|
|
476
|
+
const [owner, repo] = config.repo.split('/');
|
|
477
|
+
const octokit = new rest_1.Octokit({ auth: config.token });
|
|
478
|
+
const executor = new ActionExecutor(octokit, owner, repo);
|
|
479
|
+
console.log(`\nExecuting ${actions.length} actions...`);
|
|
480
|
+
const execResult = await executor.execute(actions);
|
|
481
|
+
console.log(`Done: ${execResult.executed} executed, ${execResult.skipped} skipped, ${execResult.failed} failed.`);
|
|
482
|
+
for (const detail of execResult.details) {
|
|
483
|
+
const icon = detail.status === 'executed' ? ' ā
' : detail.status === 'skipped' ? ' āļø' : ' ā';
|
|
484
|
+
console.log(`${icon} #${detail.target} ${detail.action}${detail.reason ? ` (${detail.reason})` : ''}`);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
catch (err) {
|
|
490
|
+
console.error(`Auto-actions failed: ${err.message}`);
|
|
491
|
+
process.exit(1);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
program
|
|
496
|
+
.command('scan-issues')
|
|
497
|
+
.description('Scan and triage open issues in a repository')
|
|
498
|
+
.requiredOption('-r, --repo <owner/repo>', 'GitHub repository')
|
|
499
|
+
.option('-t, --token <token>', 'GitHub token (or GITHUB_TOKEN env)')
|
|
500
|
+
.option('-p, --provider <name>', 'LLM provider: gemini|openai|anthropic|openrouter|none')
|
|
501
|
+
.option('--model <name>', 'LLM model name (overrides provider default)')
|
|
502
|
+
.option('--api-key <key>', 'API key for the selected provider')
|
|
503
|
+
.option('--no-llm', 'Disable LLM scoring')
|
|
504
|
+
.option('-f, --format <format>', 'Output format: table|json|markdown', 'table')
|
|
505
|
+
.option('-m, --max <number>', 'Max issues to scan', '500')
|
|
506
|
+
.action(async (opts) => {
|
|
507
|
+
const config = makeConfig(opts);
|
|
508
|
+
if (!config.token) {
|
|
509
|
+
console.error('ā GITHUB_TOKEN required. Set via env or --token flag.');
|
|
510
|
+
process.exit(1);
|
|
511
|
+
}
|
|
512
|
+
if (!config.provider && opts.llm !== false) {
|
|
513
|
+
showHeuristicFallbackWarning();
|
|
514
|
+
}
|
|
515
|
+
const { IssueScanner } = await Promise.resolve().then(() => __importStar(require('./core/issue-scanner')));
|
|
516
|
+
const issueScanner = new IssueScanner({
|
|
517
|
+
repo: config.repo,
|
|
518
|
+
token: config.token,
|
|
519
|
+
maxIssues: config.maxPRs,
|
|
520
|
+
provider: config.provider,
|
|
521
|
+
});
|
|
522
|
+
console.log(`š Scanning issues for ${config.repo}...`);
|
|
523
|
+
const issues = await issueScanner.scan();
|
|
524
|
+
if (opts.format === 'json') {
|
|
525
|
+
const clean = issues.map(({ embedding, ...rest }) => rest);
|
|
526
|
+
console.log(JSON.stringify(clean, null, 2));
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
console.log(`\nš Issue Report ā ${config.repo}`);
|
|
530
|
+
console.log(` ${issues.length} issues scanned | ${issues.filter(i => i.isSpam).length} spam\n`);
|
|
531
|
+
const rows = issues.slice(0, 30).map(issue => ({
|
|
532
|
+
'#': issue.number,
|
|
533
|
+
Score: issue.totalScore,
|
|
534
|
+
Title: issue.title.slice(0, 45),
|
|
535
|
+
Author: issue.author.slice(0, 12),
|
|
536
|
+
Labels: issue.labels.slice(0, 3).join(', '),
|
|
537
|
+
Comments: issue.commentCount,
|
|
538
|
+
Reactions: issue.reactionCount,
|
|
539
|
+
Intent: issue.intent ?? '-',
|
|
540
|
+
Spam: issue.isSpam ? 'Spam' : 'Clean',
|
|
541
|
+
}));
|
|
542
|
+
console.table(rows);
|
|
543
|
+
}
|
|
418
544
|
});
|
|
419
545
|
program
|
|
420
546
|
.command('score')
|
|
@@ -424,7 +550,7 @@ program
|
|
|
424
550
|
.option('-t, --token <token>', 'GitHub token')
|
|
425
551
|
.option('-k, --gemini-key <key>', 'Gemini API key (or GEMINI_API_KEY env)')
|
|
426
552
|
.option('-p, --provider <name>', 'LLM provider: gemini|openai|anthropic|openrouter|none')
|
|
427
|
-
.option('
|
|
553
|
+
.option('--model <name>', 'LLM model name (overrides provider default)')
|
|
428
554
|
.option('--api-key <key>', 'API key for the selected provider')
|
|
429
555
|
.option('--no-llm', 'Disable LLM scoring and use heuristic-only mode')
|
|
430
556
|
.option('-f, --format <format>', 'Output format', 'table')
|