alif-digest 1.0.13 → 1.1.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/CONTRIBUTING.md +54 -0
- package/README.md +88 -62
- package/ROADMAP.md +36 -0
- package/dist/cli/commands/config.d.ts +2 -0
- package/dist/cli/commands/config.js +68 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/debug.js +121 -28
- package/dist/cli/commands/debug.js.map +1 -1
- package/dist/cli/commands/init.js +19 -6
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/run.d.ts +2 -0
- package/dist/cli/commands/run.js +12 -5
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/schedule.js +11 -8
- package/dist/cli/commands/schedule.js.map +1 -1
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config-schema.d.ts +10 -1
- package/dist/core/config-schema.js +7 -1
- package/dist/core/config-schema.js.map +1 -1
- package/dist/core/debug/llm-tester.js +3 -1
- package/dist/core/debug/llm-tester.js.map +1 -1
- package/dist/core/default-keywords.d.ts +5 -0
- package/dist/core/default-keywords.js +80 -6
- package/dist/core/default-keywords.js.map +1 -1
- package/dist/core/filters/deduplicator.d.ts +5 -2
- package/dist/core/filters/deduplicator.js +67 -11
- package/dist/core/filters/deduplicator.js.map +1 -1
- package/dist/core/filters/keywords.d.ts +1 -0
- package/dist/core/filters/keywords.js +3 -0
- package/dist/core/filters/keywords.js.map +1 -1
- package/dist/core/logger.d.ts +16 -0
- package/dist/core/logger.js +40 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/pipeline.d.ts +1 -0
- package/dist/core/pipeline.js +130 -28
- package/dist/core/pipeline.js.map +1 -1
- package/dist/core/scheduler.js +2 -1
- package/dist/core/scheduler.js.map +1 -1
- package/dist/core/scraper-types.d.ts +1 -0
- package/dist/core/scraper-types.js.map +1 -1
- package/dist/core/scrapers/scrape-scraper.js +25 -2
- package/dist/core/scrapers/scrape-scraper.js.map +1 -1
- package/dist/db/article-store.d.ts +6 -1
- package/dist/db/article-store.js +4 -1
- package/dist/db/article-store.js.map +1 -1
- package/dist/db/migrate.js +2 -1
- package/dist/db/migrate.js.map +1 -1
- package/dist/providers/delivery/slack.js +3 -1
- package/dist/providers/delivery/slack.js.map +1 -1
- package/dist/providers/delivery/webhook.js +3 -1
- package/dist/providers/delivery/webhook.js.map +1 -1
- package/dist/providers/llm/anthropic.d.ts +1 -0
- package/dist/providers/llm/anthropic.js +20 -2
- package/dist/providers/llm/anthropic.js.map +1 -1
- package/dist/providers/llm/common.d.ts +10 -0
- package/dist/providers/llm/common.js +26 -0
- package/dist/providers/llm/common.js.map +1 -1
- package/dist/providers/llm/index.d.ts +1 -0
- package/dist/providers/llm/ollama.d.ts +1 -0
- package/dist/providers/llm/ollama.js +21 -3
- package/dist/providers/llm/ollama.js.map +1 -1
- package/dist/providers/llm/openrouter.d.ts +1 -0
- package/dist/providers/llm/openrouter.js +20 -3
- package/dist/providers/llm/openrouter.js.map +1 -1
- package/package.json +3 -1
- package/src/cli/commands/config.ts +74 -0
- package/src/cli/commands/debug.ts +137 -29
- package/src/cli/commands/init.ts +19 -6
- package/src/cli/commands/run.ts +18 -9
- package/src/cli/commands/schedule.ts +11 -8
- package/src/cli/index.ts +4 -0
- package/src/core/config-schema.ts +7 -1
- package/src/core/debug/llm-tester.ts +4 -2
- package/src/core/default-keywords.ts +87 -6
- package/src/core/filters/deduplicator.ts +81 -12
- package/src/core/filters/keywords.ts +4 -0
- package/src/core/logger.ts +51 -0
- package/src/core/pipeline.ts +163 -33
- package/src/core/scheduler.ts +2 -1
- package/src/core/scraper-types.ts +1 -0
- package/src/core/scrapers/scrape-scraper.ts +25 -2
- package/src/db/article-store.ts +8 -4
- package/src/db/migrate.ts +2 -1
- package/src/providers/delivery/slack.ts +5 -2
- package/src/providers/delivery/webhook.ts +5 -2
- package/src/providers/llm/anthropic.ts +25 -2
- package/src/providers/llm/common.ts +28 -0
- package/src/providers/llm/index.ts +1 -0
- package/src/providers/llm/ollama.ts +21 -2
- package/src/providers/llm/openrouter.ts +25 -3
- package/tests/api-scraper.test.ts +60 -0
- package/tests/config-manager.test.ts +46 -1
- package/tests/deduplicator.test.ts +158 -0
- package/tests/delivery-providers.test.ts +86 -0
- package/tests/html-arxiv-scrapers.test.ts +90 -0
- package/tests/json-scraper.test.ts +84 -0
- package/tests/rss-scraper.test.ts +66 -0
- package/tests/schedule.test.ts +164 -0
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Contributing to Alif
|
|
2
|
+
|
|
3
|
+
## 🏗 Architecture
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
Feeds (81 default sources)
|
|
7
|
+
→ Scrapers (RSS, API, JSON, HTML, ArXiv)
|
|
8
|
+
→ Exact URI Pruning
|
|
9
|
+
→ Layer 1: Positive Keywords - Negative Penalties
|
|
10
|
+
→ Top 150 Candidates
|
|
11
|
+
→ NLP Deduplicator (Talisman Fuzzy Clustering)
|
|
12
|
+
→ Top 25 Candidates
|
|
13
|
+
→ Layer 2: LLM Batch Scorer
|
|
14
|
+
→ Final Score = (L1 * 0.70) + (L2 * 0.30)
|
|
15
|
+
→ LLM Analyzer (summary + category)
|
|
16
|
+
→ Delivery (Slack / Webhook)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- **Sources**: Configured in `~/.config/alif/feeds.json`
|
|
20
|
+
- **History**: Local SQLite database at `~/.config/alif/alif.db`
|
|
21
|
+
- **Providers**: `src/providers/llm/` and `src/providers/delivery/`
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🧑💻 Local Development
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install
|
|
29
|
+
npm run dev -- init # set up your local config
|
|
30
|
+
npm run dev -- run # run from source
|
|
31
|
+
npm run test # run test suite
|
|
32
|
+
npm run lint # lint check
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
> [!TIP]
|
|
36
|
+
> Configuration lives at `~/.config/alif/config.json`. You still need to run `npm run dev -- init` once before running from source.
|
|
37
|
+
|
|
38
|
+
### Troubleshooting Husky (GUI clients)
|
|
39
|
+
|
|
40
|
+
If pre-commit hooks fail in GitHub Desktop because `npm` is not found:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
mkdir -p ~/.config/husky
|
|
44
|
+
echo 'export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"' > ~/.config/husky/init.sh
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 🤝 How to Contribute
|
|
50
|
+
|
|
51
|
+
1. Fork the repo.
|
|
52
|
+
2. Create your feature branch (`git checkout -b feature/amazing-scraper`).
|
|
53
|
+
3. Commit your changes (`git commit -m 'feat: add NewsAPI scraper'`).
|
|
54
|
+
4. Push and open a Pull Request.
|
package/README.md
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
**The Autonomous AI Signal Digest CLI.**
|
|
4
4
|
|
|
5
|
-
Alif (ألف) scours the web for high-signal AI developments,
|
|
5
|
+
Alif (ألف) scours the web for high-signal AI developments, cuts through the noise using a multi-layer scoring system, and delivers a curated daily digest directly to your workspace — powered by your own LLM.
|
|
6
6
|
|
|
7
|
+
[](https://www.npmjs.com/package/alif-digest)
|
|
7
8
|
[](https://opensource.org/licenses/MIT)
|
|
8
9
|
[](https://www.typescriptlang.org/)
|
|
9
10
|
|
|
@@ -11,17 +12,15 @@ Alif (ألف) scours the web for high-signal AI developments, selects the most r
|
|
|
11
12
|
|
|
12
13
|
## ⚡️ Quick Start
|
|
13
14
|
|
|
14
|
-
Alif helps you track AI breakthroughs by aggregating and analyzing high-signal sources. Follow these steps to get started:
|
|
15
|
-
|
|
16
15
|
### 1. Install
|
|
17
16
|
|
|
18
17
|
```bash
|
|
19
18
|
npm install -g alif-digest
|
|
20
19
|
```
|
|
21
20
|
|
|
22
|
-
### 2.
|
|
21
|
+
### 2. Initialize
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
Alif will interactively guide you through connecting an LLM (Local Ollama, Anthropic, or OpenRouter) and setting up your delivery channels.
|
|
25
24
|
|
|
26
25
|
```bash
|
|
27
26
|
alif init
|
|
@@ -29,26 +28,42 @@ alif init
|
|
|
29
28
|
|
|
30
29
|
### 3. Run
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
Alif scrapes all sources, applies multi-layer scoring, analyzes the top items with your LLM, and delivers the results.
|
|
33
32
|
|
|
34
33
|
```bash
|
|
35
34
|
alif run
|
|
35
|
+
|
|
36
|
+
# Skip source cooldown (re-fetch all sources immediately)
|
|
37
|
+
alif run --force
|
|
36
38
|
```
|
|
37
39
|
|
|
38
40
|
### 4. Schedule
|
|
39
41
|
|
|
40
|
-
Keep the signals flowing
|
|
42
|
+
Keep the signals flowing automatically.
|
|
41
43
|
|
|
42
44
|
```bash
|
|
43
|
-
alif schedule add
|
|
44
|
-
alif schedule
|
|
45
|
+
alif schedule add # Schedule a recurring digest
|
|
46
|
+
alif schedule list # View scheduled jobs
|
|
47
|
+
alif schedule check # Trigger any pending scheduled jobs
|
|
45
48
|
```
|
|
46
49
|
|
|
47
50
|
---
|
|
48
51
|
|
|
52
|
+
## ✨ Features
|
|
53
|
+
|
|
54
|
+
- **Multi-Layer AI Scoring**: Specialized two-stage evaluation engine combining keyword heuristics and deep NLP analysis to surface truly important signals.
|
|
55
|
+
- **Smart Deduplication**: Blazing-fast fuzzy clustering ensures you never read three variations of the same news story.
|
|
56
|
+
- **Aggressive Noise Reduction**: Automatically downranks PR pieces, sponsored fluff, and empty listicles.
|
|
57
|
+
- **Broad Content Ingestion**: Seamlessly scrapes various RSS, JSON API, raw HTML, and ArXiv feeds.
|
|
58
|
+
- **Customizable Delivery**: Push your personalized daily digests directly to Slack or any Webhook.
|
|
59
|
+
- **Bring Your Own AI**: Works out-of-the-box with local Ollama models or cloud providers like OpenRouter.
|
|
60
|
+
- **Built-in Automation**: Schedule recurring digests to keep your feeds curated entirely on autopilot.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
49
64
|
## 🤖 Models
|
|
50
65
|
|
|
51
|
-
Use standard instruction-tuned models that support structured output. Avoid thinking/reasoning models.
|
|
66
|
+
Use **standard instruction-tuned models** that support structured output. Avoid thinking/reasoning models (e.g. DeepSeek R1, Qwen Thinking variants) as they interfere with JSON schema generation.
|
|
52
67
|
|
|
53
68
|
### Tested Models
|
|
54
69
|
|
|
@@ -59,86 +74,97 @@ Use standard instruction-tuned models that support structured output. Avoid thin
|
|
|
59
74
|
|
|
60
75
|
---
|
|
61
76
|
|
|
62
|
-
## 🛠
|
|
77
|
+
## 🛠 CLI Reference
|
|
63
78
|
|
|
64
|
-
|
|
79
|
+
### `alif init`
|
|
65
80
|
|
|
66
|
-
|
|
81
|
+
Interactive wizard to configure your LLM provider, model, delivery channel, and preferences.
|
|
67
82
|
|
|
68
|
-
|
|
69
|
-
- **Persistence**: Local SQLite database handles article deduplication and history.
|
|
70
|
-
- **Workflow**: `Scraper` → `Deduplicator` → `Keyword Scorer` → `LLM Analyzer` → `Delivery`.
|
|
83
|
+
### `alif run [--force] [--verbose] [--quiet]`
|
|
71
84
|
|
|
72
|
-
|
|
85
|
+
Run the full pipeline.
|
|
73
86
|
|
|
74
|
-
|
|
87
|
+
- `--force`: Bypass the source cooldown to re-fetch all sources immediately.
|
|
88
|
+
- `--verbose`: Stream detailed layer-by-layer scoring and prompt output (great for debugging LLMs).
|
|
89
|
+
- `--quiet`: Suppress all output except for errors.
|
|
75
90
|
|
|
76
|
-
|
|
77
|
-
Alif ships with a set of default base keywords. If you want to change what Alif considers "high-signal" (or silence certain topics), add a `customKeywords` object to the `preferences` section in `~/.config/alif/config.json`:
|
|
91
|
+
### `alif config`
|
|
78
92
|
|
|
79
|
-
|
|
80
|
-
"preferences": {
|
|
81
|
-
"signalThreshold": 60,
|
|
82
|
-
"sequentialAnalysis": true,
|
|
83
|
-
"customKeywords": {
|
|
84
|
-
"my-favorite-framework": 100,
|
|
85
|
-
"topic-i-want-to-ignore": 0,
|
|
86
|
-
"gpt-5": 50
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
```
|
|
93
|
+
Manage your configuration without manually editing `config.json`.
|
|
90
94
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
```bash
|
|
96
|
+
alif config show # Print current config
|
|
97
|
+
alif config set signalThreshold 70 # Update a preference value
|
|
98
|
+
alif config set maxItemsPerRun 5 # Change how many items are delivered
|
|
99
|
+
alif config set sequentialAnalysis true # Enable one-by-one LLM processing
|
|
100
|
+
alif config toggle-ai-scoring # Toggle Layer 2 AI Article Scoring on/off
|
|
101
|
+
alif config set logLevel verbose # Change log verbosity (silent, normal, verbose)
|
|
102
|
+
alif config set noColor true # Disable ANSI colored output
|
|
103
|
+
```
|
|
94
104
|
|
|
95
|
-
###
|
|
105
|
+
### `alif schedule`
|
|
96
106
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
107
|
+
```bash
|
|
108
|
+
alif schedule add # Add a new cron schedule
|
|
109
|
+
alif schedule list # List all schedules
|
|
110
|
+
alif schedule delete # Remove a schedule
|
|
111
|
+
alif schedule check # Run any due schedules
|
|
112
|
+
```
|
|
100
113
|
|
|
101
|
-
###
|
|
114
|
+
### `alif debug`
|
|
102
115
|
|
|
103
116
|
```bash
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
# Run the CLI from source (requires one-time initialization)
|
|
108
|
-
npm run dev -- init
|
|
109
|
-
npm run dev -- run
|
|
117
|
+
alif debug llm <provider> --model <name> [--key <api-key>]
|
|
118
|
+
# Test your LLM connection and see the full audit trail (prompt, response, latency)
|
|
110
119
|
|
|
111
|
-
|
|
112
|
-
|
|
120
|
+
alif debug audit-feeds [--output <path>]
|
|
121
|
+
# Scrape all feeds dry-run: see item counts and content sizes per source
|
|
122
|
+
# Saves a detailed JSON report (default: ~/.config/alif/audit_report.json)
|
|
113
123
|
```
|
|
114
124
|
|
|
115
|
-
|
|
116
|
-
> **Developer Hint**: The CLI looks for configuration in `~/.config/alif/config.json`. If you are running in development, you still need to run `npm run dev -- init` once to generate your local config and database path.
|
|
125
|
+
---
|
|
117
126
|
|
|
118
|
-
|
|
127
|
+
## ⚙️ Configuration Reference
|
|
119
128
|
|
|
120
|
-
|
|
129
|
+
Config is stored at `~/.config/alif/config.json`. Most values can be changed with `alif config set`.
|
|
121
130
|
|
|
122
|
-
|
|
131
|
+
| Preference | Type | Default | Description |
|
|
132
|
+
| ------------------------- | --------- | ---------- | ---------------------------------------------------------------- |
|
|
133
|
+
| `signalThreshold` | `number` | `60` | Minimum score (0–100) for an article to qualify |
|
|
134
|
+
| `maxItemsPerRun` | `number` | `10` | Max articles delivered per run |
|
|
135
|
+
| `sourceCooldownMinutes` | `number` | `5` | Minimum gap between re-fetching the same source |
|
|
136
|
+
| `sequentialAnalysis` | `boolean` | `false` | Analyze articles one-by-one (recommended for small local models) |
|
|
137
|
+
| `enableAIArticlesScoring` | `boolean` | `true` | Enable Layer 2 LLM-based article scoring |
|
|
138
|
+
| `customKeywords` | `object` | `{}` | Add or override keyword signal weights |
|
|
139
|
+
| `negativeKeywords` | `object` | `{}` | Add custom noise/penalty keywords |
|
|
140
|
+
| `logLevel` | `string` | `'normal'` | Set logging verbosity: `'silent'`, `'normal'`, or `'verbose'` |
|
|
141
|
+
| `noColor` | `boolean` | `false` | Disable colored terminal output (`NO_COLOR=1` also works) |
|
|
123
142
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
143
|
+
**Custom keywords example:**
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
"customKeywords": {
|
|
147
|
+
"my-framework": 30,
|
|
148
|
+
"competitor-name": 0
|
|
149
|
+
},
|
|
150
|
+
"negativeKeywords": {
|
|
151
|
+
"webinar": 20
|
|
152
|
+
}
|
|
127
153
|
```
|
|
128
154
|
|
|
129
|
-
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 🤝 Architecture & Contributing
|
|
158
|
+
|
|
159
|
+
Interested in how Alif works under the hood or want to run it locally?
|
|
130
160
|
|
|
131
|
-
|
|
132
|
-
2. Create your feature branch (`git checkout -b feature/amazing-scraper`).
|
|
133
|
-
3. Commit your changes (`git commit -m 'Add support for NewsAPI'`).
|
|
134
|
-
4. Push to the branch (`git push origin feature/amazing-scraper`).
|
|
135
|
-
5. Open a Pull Request.
|
|
161
|
+
Check out our [**Contributing Guidelines**](CONTRIBUTING.md) for information on the architecture, local development setup, and how to submit pull requests!
|
|
136
162
|
|
|
137
163
|
---
|
|
138
164
|
|
|
139
165
|
## 📢 Acknowledgements
|
|
140
166
|
|
|
141
|
-
|
|
167
|
+
Thanks to [Roland](https://github.com/rolandbrecht/) for the initial technical foundation, and [Antigravity](https://antigravity.google/) for the help in building this project.
|
|
142
168
|
|
|
143
169
|
## 📄 License
|
|
144
170
|
|
package/ROADMAP.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Alif-Digest Roadmap
|
|
2
|
+
|
|
3
|
+
Items planned for future development, roughly in priority order.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🔴 High Priority
|
|
8
|
+
|
|
9
|
+
### Config pre-flight validation
|
|
10
|
+
|
|
11
|
+
`alif validate` or an automatic pre-flight check at the start of `alif run` that catches misconfigured/missing API keys, malformed URLs, etc. — before scraping has already happened.
|
|
12
|
+
|
|
13
|
+
### Expand default keywords
|
|
14
|
+
|
|
15
|
+
The default keyword list in `default-keywords.ts` is thin. A richer, categorized list (models, research, tools, policy) would meaningfully improve signal quality out of the box.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 🟡 Medium Priority
|
|
20
|
+
|
|
21
|
+
### `alif history` and `alif status`
|
|
22
|
+
|
|
23
|
+
- `alif history` — show last N delivered digests from the local DB
|
|
24
|
+
- `alif status` — show source health table (last checked, items found, errors)
|
|
25
|
+
|
|
26
|
+
### `alif run --sequential`
|
|
27
|
+
|
|
28
|
+
Allow overriding sequential analysis mode per-run from the CLI without editing config.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 🟢 Future Delivery Channels
|
|
33
|
+
|
|
34
|
+
- **Discord** — dedicated provider for Discord webhook format
|
|
35
|
+
- **Telegram** — Telegram Bot API delivery
|
|
36
|
+
- **Email** — via SMTP or a transactional email service (Resend, Mailgun)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { ConfigManager } from '../../core/config-manager.js';
|
|
3
|
+
import { logger } from '../../core/logger.js';
|
|
4
|
+
export const configCommand = new Command('config').description('Manage Alif configuration');
|
|
5
|
+
configCommand
|
|
6
|
+
.command('show')
|
|
7
|
+
.description('Display current configuration')
|
|
8
|
+
.action(() => {
|
|
9
|
+
const cm = ConfigManager.getInstance();
|
|
10
|
+
if (!cm.exists()) {
|
|
11
|
+
logger.error('Alif is not initialized. Run "alif init" first.');
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
const config = cm.load();
|
|
15
|
+
logger.log(JSON.stringify(config, null, 2));
|
|
16
|
+
});
|
|
17
|
+
configCommand
|
|
18
|
+
.command('set <key> <value>')
|
|
19
|
+
.description('Update a preference value (e.g. alif config set signalThreshold 70)')
|
|
20
|
+
.action((key, value) => {
|
|
21
|
+
const cm = ConfigManager.getInstance();
|
|
22
|
+
if (!cm.exists()) {
|
|
23
|
+
logger.error('Alif is not initialized. Run "alif init" first.');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
const config = cm.load();
|
|
27
|
+
const prefs = config.preferences;
|
|
28
|
+
if (!(key in prefs)) {
|
|
29
|
+
logger.error(`Unknown preference key: "${key}"`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
const current = prefs[key];
|
|
33
|
+
if (typeof current === 'boolean') {
|
|
34
|
+
prefs[key] = value === 'true' || value === '1';
|
|
35
|
+
}
|
|
36
|
+
else if (typeof current === 'number') {
|
|
37
|
+
const num = Number(value);
|
|
38
|
+
if (isNaN(num)) {
|
|
39
|
+
logger.error(`Value "${value}" is not a valid number.`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
prefs[key] = num;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
prefs[key] = value;
|
|
46
|
+
}
|
|
47
|
+
cm.save({ ...config, preferences: prefs });
|
|
48
|
+
logger.success(`Set ${key} = ${prefs[key]}`);
|
|
49
|
+
});
|
|
50
|
+
configCommand
|
|
51
|
+
.command('toggle-ai-scoring')
|
|
52
|
+
.description('Toggle AI Article Scoring (Layer 2) on or off')
|
|
53
|
+
.action(() => {
|
|
54
|
+
const cm = ConfigManager.getInstance();
|
|
55
|
+
if (!cm.exists()) {
|
|
56
|
+
logger.error('Alif is not initialized. Run "alif init" first.');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
const config = cm.load();
|
|
60
|
+
const current = config.preferences.enableAIArticlesScoring;
|
|
61
|
+
const updated = !current;
|
|
62
|
+
cm.save({
|
|
63
|
+
...config,
|
|
64
|
+
preferences: { ...config.preferences, enableAIArticlesScoring: updated },
|
|
65
|
+
});
|
|
66
|
+
logger.success(`AI Article Scoring is now ${updated ? 'ENABLED ✅' : 'DISABLED ❌'}`);
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,11 @@ import { LLMTester } from '../../core/debug/llm-tester.js';
|
|
|
3
3
|
import { OllamaProvider } from '../../providers/llm/ollama.js';
|
|
4
4
|
import { AnthropicProvider } from '../../providers/llm/anthropic.js';
|
|
5
5
|
import { OpenRouterProvider } from '../../providers/llm/openrouter.js';
|
|
6
|
+
import { ScraperOrchestrator } from '../../core/orchestrator.js';
|
|
7
|
+
import { ConfigManager } from '../../core/config-manager.js';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { logger } from '../../core/logger.js';
|
|
6
11
|
export const debugCommand = new Command('debug').description('Debug utilities for Alif');
|
|
7
12
|
debugCommand
|
|
8
13
|
.command('llm')
|
|
@@ -12,6 +17,7 @@ debugCommand
|
|
|
12
17
|
.option('--endpoint <url>', 'Base URL/Endpoint (default from config)')
|
|
13
18
|
.option('--key <token>', 'API Key (if required)')
|
|
14
19
|
.option('--sequential', 'Process items one-by-one')
|
|
20
|
+
.option('--score', 'Test Layer 2 AI Article Scoring instead of analysis')
|
|
15
21
|
.action(async (providerName, options) => {
|
|
16
22
|
let provider;
|
|
17
23
|
switch (providerName.toLowerCase()) {
|
|
@@ -40,36 +46,123 @@ debugCommand
|
|
|
40
46
|
default:
|
|
41
47
|
throw new Error(`Unknown provider: ${providerName}`);
|
|
42
48
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
49
|
+
logger.log('\n' + '='.repeat(50));
|
|
50
|
+
logger.info('LLM DIAGNOSTIC AUDIT TRAIL');
|
|
51
|
+
logger.log('='.repeat(50));
|
|
52
|
+
if (options.score) {
|
|
53
|
+
// --- SCORE DEBUG MODE ---
|
|
54
|
+
const SCORE_TEST_TITLES = [
|
|
55
|
+
'OpenAI releases GPT-5 — biggest model leap in three years',
|
|
56
|
+
'DeepSeek V4 open-sourced: beats GPT-4o on 12 benchmarks',
|
|
57
|
+
'Anthropic raises $5B Series E at $75B valuation',
|
|
58
|
+
'Cline 2.0 released: autonomous coding agent with computer use',
|
|
59
|
+
'Top 10 AI tools you should be using in 2025',
|
|
60
|
+
'Sponsored: How Company X saved millions with AI',
|
|
61
|
+
'New EU AI Act enforcement deadlines announced',
|
|
62
|
+
'NVIDIA Blackwell B200 GPUs now available for cloud providers',
|
|
63
|
+
'Google Antigravity adds agentic coding to all tiers',
|
|
64
|
+
'A beginner tutorial on building a RAG pipeline',
|
|
65
|
+
];
|
|
66
|
+
const startTime = Date.now();
|
|
67
|
+
const scores = await provider.score(SCORE_TEST_TITLES);
|
|
68
|
+
const latency = Date.now() - startTime;
|
|
69
|
+
logger.info(`\n[1] SCORING PROMPT SENT: ${SCORE_TEST_TITLES.length} titles`);
|
|
70
|
+
logger.info('-'.repeat(30));
|
|
71
|
+
SCORE_TEST_TITLES.forEach((t, i) => logger.debug(` ${i}: ${t}`));
|
|
72
|
+
logger.info('\n[2] SCORES RETURNED:');
|
|
73
|
+
logger.info('-'.repeat(30));
|
|
74
|
+
SCORE_TEST_TITLES.forEach((t, i) => {
|
|
75
|
+
const s = scores[i] ?? '?';
|
|
76
|
+
const bar = '█'.repeat(Math.round(Number(s) / 5));
|
|
77
|
+
logger.info(` ${String(s).padStart(3)} ${bar.padEnd(20)} ${t}`);
|
|
78
|
+
});
|
|
79
|
+
logger.info('\n[3] LATENCY:');
|
|
80
|
+
logger.info('-'.repeat(10));
|
|
81
|
+
logger.info(`${latency}ms`);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// --- ANALYSIS DEBUG MODE (existing) ---
|
|
85
|
+
const tester = new LLMTester(provider);
|
|
86
|
+
const { results, debugInfo, totalLatency } = await tester.runTest({
|
|
87
|
+
sequential: options.sequential,
|
|
88
|
+
});
|
|
89
|
+
if (debugInfo) {
|
|
90
|
+
logger.info('\n[1] PROMPT SENT TO LLM:');
|
|
91
|
+
logger.info('-'.repeat(30));
|
|
92
|
+
logger.debug(debugInfo.prompt);
|
|
93
|
+
logger.info('\n[2] RAW RESPONSE FROM LLM:');
|
|
94
|
+
logger.info('-'.repeat(30));
|
|
95
|
+
if (!debugInfo.rawResponse || debugInfo.rawResponse.trim() === '') {
|
|
96
|
+
logger.warn('<<< EMPTY RESPONSE >>>');
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
logger.debug(debugInfo.rawResponse);
|
|
100
|
+
}
|
|
101
|
+
logger.info('\n[3] LATENCY:');
|
|
102
|
+
logger.info('-'.repeat(10));
|
|
103
|
+
logger.info(`${debugInfo.latencyMs}ms`);
|
|
61
104
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
105
|
+
logger.info('\n[4] PARSED RESULTS:');
|
|
106
|
+
logger.info('-'.repeat(30));
|
|
107
|
+
results.forEach((res, idx) => {
|
|
108
|
+
logger.info(`${idx + 1}. [${res.category}] ${res.summary || 'Summary failed'}`);
|
|
109
|
+
});
|
|
110
|
+
logger.log('\n' + '='.repeat(50));
|
|
111
|
+
logger.info(`TOTAL PROCESS TIME: ${totalLatency}ms`);
|
|
112
|
+
logger.log('='.repeat(50));
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
debugCommand
|
|
116
|
+
.command('audit-feeds')
|
|
117
|
+
.description('Audit all configured feeds for volume and data size')
|
|
118
|
+
.option('--output <path>', 'Custom output path for JSON report')
|
|
119
|
+
.action(async (options) => {
|
|
120
|
+
const configManager = ConfigManager.getInstance();
|
|
121
|
+
if (!configManager.exists()) {
|
|
122
|
+
throw new Error('Alif is not initialized. Run "alif init" first.');
|
|
123
|
+
}
|
|
124
|
+
const config = configManager.load();
|
|
125
|
+
if (!fs.existsSync(config.feedsPath)) {
|
|
126
|
+
throw new Error(`Feeds file not found at ${config.feedsPath}`);
|
|
65
127
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
128
|
+
const feeds = JSON.parse(fs.readFileSync(config.feedsPath, 'utf-8'));
|
|
129
|
+
logger.info(`[Diagnostic] Auditing ${feeds.length} feeds...`);
|
|
130
|
+
const orchestrator = new ScraperOrchestrator();
|
|
131
|
+
const startTime = Date.now();
|
|
132
|
+
const results = await orchestrator.runAll(feeds);
|
|
133
|
+
const duration = Date.now() - startTime;
|
|
134
|
+
const auditData = results.map((res) => {
|
|
135
|
+
const totalChars = res.items.reduce((acc, item) => acc + (item.content?.length || 0) + (item.title?.length || 0), 0);
|
|
136
|
+
return {
|
|
137
|
+
source: res.source,
|
|
138
|
+
status: res.status,
|
|
139
|
+
itemCount: res.items.length,
|
|
140
|
+
totalCharacters: totalChars,
|
|
141
|
+
averageItemSize: res.items.length > 0 ? Math.round(totalChars / res.items.length) : 0,
|
|
142
|
+
error: res.error,
|
|
143
|
+
};
|
|
70
144
|
});
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
145
|
+
const summary = {
|
|
146
|
+
totalFeeds: feeds.length,
|
|
147
|
+
successfulFeeds: auditData.filter((d) => d.status === 'ok').length,
|
|
148
|
+
failedFeeds: auditData.filter((d) => d.status === 'error').length,
|
|
149
|
+
totalItems: auditData.reduce((acc, d) => acc + d.itemCount, 0),
|
|
150
|
+
totalCharacters: auditData.reduce((acc, d) => acc + d.totalCharacters, 0),
|
|
151
|
+
durationMs: duration,
|
|
152
|
+
timestamp: new Date().toISOString(),
|
|
153
|
+
};
|
|
154
|
+
const reportPath = options.output || path.join(configManager.getConfigDir(), 'audit_report.json');
|
|
155
|
+
fs.writeFileSync(reportPath, JSON.stringify({ summary, details: auditData }, null, 2));
|
|
156
|
+
logger.log('\n' + '='.repeat(50));
|
|
157
|
+
logger.info('FEED AUDIT SUMMARY');
|
|
158
|
+
logger.log('='.repeat(50));
|
|
159
|
+
logger.info(`Total Feeds: ${summary.totalFeeds}`);
|
|
160
|
+
logger.info(`Successful: ${summary.successfulFeeds}`);
|
|
161
|
+
logger.info(`Failed: ${summary.failedFeeds}`);
|
|
162
|
+
logger.info(`Total Items: ${summary.totalItems}`);
|
|
163
|
+
logger.info(`Total Content: ${(summary.totalCharacters / 1024 / 1024).toFixed(2)} MB`);
|
|
164
|
+
logger.info(`Time taken: ${(duration / 1000).toFixed(2)}s`);
|
|
165
|
+
logger.log('='.repeat(50));
|
|
166
|
+
logger.success(`\nDetailed report saved to: ${reportPath}`);
|
|
74
167
|
});
|
|
75
168
|
//# sourceMappingURL=debug.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debug.js","sourceRoot":"","sources":["../../../src/cli/commands/debug.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"debug.js","sourceRoot":"","sources":["../../../src/cli/commands/debug.ts"],"names":[],"mappings":"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"}
|