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.
Files changed (85) hide show
  1. package/README.md +214 -22
  2. package/dist/cli.js +130 -4
  3. package/dist/cli.js.map +1 -1
  4. package/dist/core/action-executor.d.ts +31 -0
  5. package/dist/core/action-executor.d.ts.map +1 -0
  6. package/dist/core/action-executor.js +148 -0
  7. package/dist/core/action-executor.js.map +1 -0
  8. package/dist/core/actions.d.ts +30 -0
  9. package/dist/core/actions.d.ts.map +1 -0
  10. package/dist/core/actions.js +116 -0
  11. package/dist/core/actions.js.map +1 -0
  12. package/dist/core/cache.d.ts +1 -1
  13. package/dist/core/cache.d.ts.map +1 -1
  14. package/dist/core/cache.js +2 -3
  15. package/dist/core/cache.js.map +1 -1
  16. package/dist/core/concurrency.d.ts +4 -0
  17. package/dist/core/concurrency.d.ts.map +1 -1
  18. package/dist/core/concurrency.js +11 -0
  19. package/dist/core/concurrency.js.map +1 -1
  20. package/dist/core/db.d.ts +23 -1
  21. package/dist/core/db.d.ts.map +1 -1
  22. package/dist/core/db.js +192 -0
  23. package/dist/core/db.js.map +1 -1
  24. package/dist/core/dedup.d.ts +7 -3
  25. package/dist/core/dedup.d.ts.map +1 -1
  26. package/dist/core/dedup.js +186 -43
  27. package/dist/core/dedup.js.map +1 -1
  28. package/dist/core/diff-analyzer.d.ts +19 -0
  29. package/dist/core/diff-analyzer.d.ts.map +1 -0
  30. package/dist/core/diff-analyzer.js +97 -0
  31. package/dist/core/diff-analyzer.js.map +1 -0
  32. package/dist/core/graphql.d.ts +14 -1
  33. package/dist/core/graphql.d.ts.map +1 -1
  34. package/dist/core/graphql.js +72 -1
  35. package/dist/core/graphql.js.map +1 -1
  36. package/dist/core/holistic-ranker.d.ts +14 -0
  37. package/dist/core/holistic-ranker.d.ts.map +1 -0
  38. package/dist/core/holistic-ranker.js +95 -0
  39. package/dist/core/holistic-ranker.js.map +1 -0
  40. package/dist/core/intent.d.ts +18 -0
  41. package/dist/core/intent.d.ts.map +1 -0
  42. package/dist/core/intent.js +111 -0
  43. package/dist/core/intent.js.map +1 -0
  44. package/dist/core/issue-scanner.d.ts +30 -0
  45. package/dist/core/issue-scanner.d.ts.map +1 -0
  46. package/dist/core/issue-scanner.js +139 -0
  47. package/dist/core/issue-scanner.js.map +1 -0
  48. package/dist/core/issue-scoring.d.ts +24 -0
  49. package/dist/core/issue-scoring.d.ts.map +1 -0
  50. package/dist/core/issue-scoring.js +171 -0
  51. package/dist/core/issue-scoring.js.map +1 -0
  52. package/dist/core/provider.d.ts +3 -0
  53. package/dist/core/provider.d.ts.map +1 -1
  54. package/dist/core/provider.js +34 -7
  55. package/dist/core/provider.js.map +1 -1
  56. package/dist/core/retryable-provider.d.ts +26 -0
  57. package/dist/core/retryable-provider.d.ts.map +1 -0
  58. package/dist/core/retryable-provider.js +75 -0
  59. package/dist/core/retryable-provider.js.map +1 -0
  60. package/dist/core/scanner.d.ts +6 -1
  61. package/dist/core/scanner.d.ts.map +1 -1
  62. package/dist/core/scanner.js +136 -28
  63. package/dist/core/scanner.js.map +1 -1
  64. package/dist/core/scoring.d.ts +5 -1
  65. package/dist/core/scoring.d.ts.map +1 -1
  66. package/dist/core/scoring.js +93 -5
  67. package/dist/core/scoring.js.map +1 -1
  68. package/dist/core/semantic-matcher.d.ts +18 -0
  69. package/dist/core/semantic-matcher.d.ts.map +1 -0
  70. package/dist/core/semantic-matcher.js +106 -0
  71. package/dist/core/semantic-matcher.js.map +1 -0
  72. package/dist/core/types.d.ts +56 -1
  73. package/dist/core/types.d.ts.map +1 -1
  74. package/dist/core/vision.d.ts +3 -0
  75. package/dist/core/vision.d.ts.map +1 -1
  76. package/dist/core/vision.js +23 -0
  77. package/dist/core/vision.js.map +1 -1
  78. package/dist/index.d.ts +11 -1
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +17 -1
  81. package/dist/index.js.map +1 -1
  82. package/dist/server/app.d.ts.map +1 -1
  83. package/dist/server/app.js +29 -0
  84. package/dist/server/app.js.map +1 -1
  85. 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 Open Source Maintainers</h3>
5
+ <h3 align="center">AI-Powered PR & Issue Triage for Maintainers & Enterprise Teams</h3>
6
6
 
7
7
  <p align="center">
8
- <em>"3,100 PRs. Which ones should I merge?"</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-220_passing-2DA44E?style=flat-square" alt="Tests" />
16
- <img src="https://img.shields.io/badge/signals-20-8B5CF6?style=flat-square" alt="20 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 PR triage system that **deduplicates, scores, and ranks** pull requests so maintainers can focus on merging what matters. Available as a **CLI tool**, **persistent server with REST API**, and **GitHub Action**.
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
- - šŸ“Š **20-Signal Scoring** — Includes new Scope Coherence + PR Complexity analysis
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
- Scoring[20-Signal Scoring Engine]
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
- Dedup[Embedding Dedup<br/>LanceDB]
273
+ Diff[Diff Analyzer<br/>Code Quality Ā· Risk]
274
+ Dedup[Cross-type Dedup<br/>LanceDB + LLM Verify]
145
275
  Vision[Vision Doc Alignment]
146
- Rep[Contributor Reputation]
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
- Scanner --> Dedup
308
+ IScore --> LLM
309
+ Scanner --> Diff
310
+ Diff --> LLM
311
+ Diff --> Scoring
312
+ Scanner & IScan --> Dedup
313
+ Dedup --> LLM
172
314
  Scanner --> Vision
173
- Scoring --> Rep
174
- Scoring --> SQLite
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, 20 signals)
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 (20 signals, no LLM)
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
- ## 20-Signal Scoring
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 | **Scope Coherence** | 0.06 | Directory spread, title-to-files alignment |
360
- | 20 | **PR Complexity** | 0.05 | Size analysis, AI detection, overengineering |
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
- When an LLM provider is configured, a **quality score** (0–100) is blended at **60% LLM / 40% heuristic**.
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 Open Source Maintainers')
297
- .version('0.4.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('-m, --model <name>', 'LLM model name (overrides provider default)')
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('-m, --model <name>', 'LLM model name (overrides provider default)')
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')