intelwatch 1.1.6 → 1.3.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/CHANGELOG-DRAFT.md +44 -0
- package/CHANGELOG.md +105 -1
- package/Endrix-Intelwatch-DueDil.pdf +0 -0
- package/RELEASE.md +15 -0
- package/bin/intelwatch.js +6 -1
- package/package.json +3 -2
- package/src/commands/check.js +52 -1
- package/src/commands/digest.js +40 -1
- package/src/commands/profile.js +164 -34
- package/src/commands/report.js +25 -1
- package/src/index.js +36 -3
- package/src/license.js +194 -0
- package/src/providers/apollo.js +172 -0
- package/src/providers/clearbit.js +136 -0
- package/src/providers/index.js +30 -0
- package/src/providers/opencorporates.js +159 -0
- package/src/providers/pappers.js +75 -0
- package/src/providers/registry.js +531 -0
- package/src/scrapers/reddit-hn.js +161 -0
- package/src/trackers/brand.js +66 -3
- package/src/trackers/competitor.js +9 -10
- package/src/utils/error-handler.js +190 -0
- package/src/utils/export.js +323 -0
- package/src/utils/i18n.js +153 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# CHANGELOG-DRAFT — intelwatch v1.3.0
|
|
2
|
+
|
|
3
|
+
## ✨ New Features
|
|
4
|
+
|
|
5
|
+
### International Provider Architecture (`src/providers/`)
|
|
6
|
+
- **Provider Registry** (`registry.js`) — adapts company data source based on domain TLD
|
|
7
|
+
- `.fr` → Pappers (full French company data)
|
|
8
|
+
- `.com`, `.de`, `.uk`, etc. → OpenCorporates (180+ jurisdictions)
|
|
9
|
+
- Clearbit available as BYOK enrichment layer (domain → sector, size, revenue, tech)
|
|
10
|
+
- **Auto-detection** : `detectCountry()` maps TLD to ISO country code, `resolveProvider()` picks the best provider
|
|
11
|
+
- **Unified API** : `searchCompany()`, `getCompanyProfile()`, `getSubsidiaries()`, `lookupCompany()` — same interface, any provider
|
|
12
|
+
- **License-integrated** : full profile = Pro only, subsidiaries = Pro only, preview mode for Free tier
|
|
13
|
+
- **Pappers provider** (`pappers.js`) — thin adapter wrapping existing `scrapers/pappers.js`
|
|
14
|
+
- **OpenCorporates provider** (`opencorporates.js`) — real API integration with free tier (500 req/month, no key needed) + BYOK for higher limits
|
|
15
|
+
- **Clearbit provider** (`clearbit.js`) — scaffold with real API integration (BYOK: `CLEARBIT_API_KEY`)
|
|
16
|
+
- Competitor tracker refactored: uses `lookupCompany()` instead of direct Pappers import → works for any TLD
|
|
17
|
+
- Backward compat: `pappers` key still present in competitor snapshots for existing data
|
|
18
|
+
|
|
19
|
+
### Freemium License Gate
|
|
20
|
+
- **Centralized `src/license.js`** — single module for all Pro/Free tier logic
|
|
21
|
+
- License detection: `INTELWATCH_PRO_KEY` env → `INTELWATCH_LICENSE_KEY` env → `~/.intelwatch-license` file
|
|
22
|
+
- **Free tier:** JSON + CSV (50 rows), Reddit/HN (5 results), Pappers preview only
|
|
23
|
+
- **Pro tier:** JSON + CSV + XLS + PDF (unlimited), Reddit/HN (100), full profiles + subsidiaries
|
|
24
|
+
|
|
25
|
+
### Export System (CSV, XLS, PDF)
|
|
26
|
+
- `handleExport()` with license-aware gating — XLS/PDF throw `LICENSE_REQUIRED`
|
|
27
|
+
- CSV capped at 50 rows on Free tier with warning
|
|
28
|
+
|
|
29
|
+
### Reddit & Hacker News Mentions
|
|
30
|
+
- `src/scrapers/reddit-hn.js` — Reddit JSON API + HN Algolia API
|
|
31
|
+
- Results capped per license tier (Free: 5, Pro: 100)
|
|
32
|
+
- Brand tracker fetches Google News + Reddit + HN in parallel
|
|
33
|
+
|
|
34
|
+
## 🐛 Bug Fixes
|
|
35
|
+
- `handleError()` crash on null/undefined — graceful handling added
|
|
36
|
+
- Provider registry returns `tier`/`isPreview` even when provider unavailable
|
|
37
|
+
|
|
38
|
+
## 🧪 Tests — 136 pass, 0 fail (was 40)
|
|
39
|
+
- `test/providers.test.js` — 31 tests: detectCountry (11 TLDs), resolveProvider (5), listProviders (3), interface compliance (3), searchCompany (3), getCompanyProfile (2), getSubsidiaries license gate (1), individual provider availability (3)
|
|
40
|
+
- `test/license.test.js` — 19 tests
|
|
41
|
+
- `test/export.test.js` — 33 tests (incl. LICENSE_REQUIRED gates)
|
|
42
|
+
- `test/i18n.test.js` — 6 tests
|
|
43
|
+
- `test/error-handler.test.js` — 5 tests
|
|
44
|
+
- `test/reddit-hn.test.js` — 4 tests
|
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,95 @@
|
|
|
1
|
-
#
|
|
1
|
+
# CHANGELOG - v1.2 Draft
|
|
2
|
+
|
|
3
|
+
## Version 1.2.0 (en développement)
|
|
4
|
+
|
|
5
|
+
### 🐛 Bug Fixes
|
|
6
|
+
- **[✅ DONE]** Fix forces/faiblesses vides en terminal - problème de parsing JSON de la réponse AI
|
|
7
|
+
- Amélioration de la fonction `extractAIJSON` avec validation et conversion automatique
|
|
8
|
+
- Ajout de debug mode avec `DEBUG_AI=1` pour diagnostiquer les réponses AI
|
|
9
|
+
- Gestion des formats string/object pour strengths/weaknesses
|
|
10
|
+
- **[✅ DONE]** Robustesse du scraping (timeouts, retries, user-agent rotation)
|
|
11
|
+
- Module `fetcher.js` déjà implémenté avec retry et backoff exponential
|
|
12
|
+
- **[✅ DONE]** Meilleur error handling (messages clairs, pas de stack traces en prod)
|
|
13
|
+
- Nouveau module `error-handler.js` avec gestion globale des erreurs
|
|
14
|
+
- Messages user-friendly en production, stack traces seulement en debug
|
|
15
|
+
- Gestion spécialisée des erreurs réseau, HTTP, FS, AI API
|
|
16
|
+
|
|
17
|
+
### ✨ New Features
|
|
18
|
+
- **[✅ DONE]** Export JSON/CSV structuré pour commandes `check`, `digest`, `report`, et `profile`
|
|
19
|
+
- Module `export.js` avec formatage intelligent par type de commande
|
|
20
|
+
- Options `--export json|csv` et `--output <file>` ajoutées
|
|
21
|
+
- Support des structures complexes avec aplatissement pour CSV
|
|
22
|
+
- **[✅ DONE]** Option globale `--lang fr` (PDF + AI prompts in French)
|
|
23
|
+
- Module `i18n.js` avec labels multilingues (en/fr)
|
|
24
|
+
- Prompts AI adaptés selon la langue
|
|
25
|
+
- Affichage des forces/faiblesses/risques en français
|
|
26
|
+
- **[REPORTÉ v1.3]** Section transactions comparables (M&A/fundraising des concurrents avec liens articles)
|
|
27
|
+
|
|
28
|
+
### 🔧 Improvements
|
|
29
|
+
- **[✅ DONE]** Détection technologique : ajout de 20+ nouvelles technologies
|
|
30
|
+
- Passé de 35 à 56 technologies (+21)
|
|
31
|
+
- Frameworks modernes : Nuxt, Svelte, Astro, Remix
|
|
32
|
+
- CMS headless : Strapi, Contentful, Sanity, Prismic
|
|
33
|
+
- Hosting : Vercel, Netlify, analytics : Plausible, Fathom
|
|
34
|
+
- Build tools : Vite, CSS frameworks : Tailwind, Bootstrap
|
|
35
|
+
- **[REPORTÉ v1.3]** Amélioration qualité rapports HTML/PDF (formatage, lisibilité)
|
|
36
|
+
- **[REPORTÉ v1.3]** Géographie des implantations (scraping site web entreprise)
|
|
37
|
+
|
|
38
|
+
### 📊 Audit Code Effectué
|
|
39
|
+
- **Architecture** : entrée CLI propre via commander.js, modulaire et extensible
|
|
40
|
+
- **Tests** : 40 tests passent toujours, aucune régression introduite
|
|
41
|
+
- **Stack technique** : ESM, Node 18+, dépendances à jour, zero vulnérabilité
|
|
42
|
+
- **Qualité code** : structure commands/, utils/, scrapers/, ai/ respectée
|
|
43
|
+
- **Version** : CLI mise à jour de 1.0.0 → 1.1.6 dans index.js
|
|
44
|
+
- **Error handling** : gestion globale des erreurs, messages user-friendly
|
|
45
|
+
- **Performance** : fetcher.js déjà optimisé avec retry/backoff
|
|
46
|
+
- **Robustesse** : validation des entrées, parsing JSON amélioré
|
|
47
|
+
|
|
48
|
+
### 🆕 Nouveaux Modules Ajoutés
|
|
49
|
+
- `src/utils/export.js` — Export JSON/CSV avec formatage intelligent
|
|
50
|
+
- `src/utils/i18n.js` — Internationalisation (en/fr)
|
|
51
|
+
- `src/utils/error-handler.js` — Gestion d'erreurs globale et user-friendly
|
|
52
|
+
|
|
53
|
+
### 📈 Statistiques Finales
|
|
54
|
+
- **Couverture fonctionnalités** : 4/5 demandées implémentées (80%)
|
|
55
|
+
- **Technologies détectées** : 35 → 56 (+60% d'amélioration)
|
|
56
|
+
- **Nouvelles options CLI** : `--lang`, `--export`, `--output`
|
|
57
|
+
- **Commandes améliorées** : `check`, `digest`, `report`, `profile`
|
|
58
|
+
- **Zero breaking change** : compatibilité complète maintenue
|
|
59
|
+
|
|
60
|
+
### 🎯 Prêt pour Production
|
|
61
|
+
- Tests complets OK
|
|
62
|
+
- Documentation inline à jour
|
|
63
|
+
- Error handling robuste
|
|
64
|
+
- Compatibilité Node.js 18+ maintenue
|
|
65
|
+
- Aucune nouvelle dépendance npm ajoutée# Changelog
|
|
2
66
|
|
|
3
67
|
All notable changes to this project will be documented in this file.
|
|
68
|
+
|
|
69
|
+
## [1.1.6] - 2026-03-04
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
- Chart label positioning: top labels no longer overlap bars
|
|
73
|
+
- SVG chart renders full-width in PDF
|
|
74
|
+
|
|
75
|
+
## [1.1.5] - 2026-03-03
|
|
76
|
+
|
|
77
|
+
### Changed
|
|
78
|
+
- Dual-zone financial chart: full-width rendering, revenue zone + income/EBITDA zone
|
|
79
|
+
- KPI labels switched to English
|
|
80
|
+
- Emoji cleanup throughout PDF (removed redundant decorators)
|
|
81
|
+
|
|
82
|
+
### Added
|
|
83
|
+
- Press revenue estimates: Brave Search enrichment for subsidiary financial data when Pappers data is stale
|
|
84
|
+
|
|
85
|
+
## [1.1.4] - 2026-03-03
|
|
86
|
+
|
|
87
|
+
### Added
|
|
88
|
+
- **Financial Trend chart** — dual-zone SVG: revenue bars (top) + income & EBITDA bars (bottom)
|
|
89
|
+
- **Organic vs external growth** — code-built yearly breakdown, compares consolidated CA growth with known acquisition dates
|
|
90
|
+
- **Press revenue estimates** — cross-references press mentions for subsidiary revenue data
|
|
91
|
+
- **Sign fix** — negative results display correctly in charts and tables
|
|
92
|
+
|
|
4
93
|
## [1.1.3] - 2026-03-03
|
|
5
94
|
|
|
6
95
|
### Added
|
|
@@ -78,3 +167,18 @@ All notable changes to this project will be documented in this file.
|
|
|
78
167
|
### Fixed
|
|
79
168
|
- Array headers bug in tech-detect.js
|
|
80
169
|
- Anthropic model name updated to `claude-3-5-haiku-latest`
|
|
170
|
+
|
|
171
|
+
## [1.3.0] - 2026-03-21
|
|
172
|
+
### Added
|
|
173
|
+
- Pro License Paywall: Gated advanced features (PDF/XLS export, Deep Profile, International OSINT).
|
|
174
|
+
- Stripe Payment Link integration for Intelwatch Pro subscriptions.
|
|
175
|
+
- International Smart Routing: `.fr` hits Pappers, international hits Apollo/Clearbit/OpenCorporates.
|
|
176
|
+
- France Handoff: International companies based in France are handed off to Pappers for deeper financial data.
|
|
177
|
+
- Reddit JSON API & HackerNews Algolia integration for digital OSINT and sentiment tracking.
|
|
178
|
+
- M&A Deep Dorks: Restricts Brave Search queries to specialized PE/M&A news sources (cfnews, lesechos, fusacq, etc.)
|
|
179
|
+
- Export capabilities unified under `--export <json|csv|xls|pdf>` flag.
|
|
180
|
+
|
|
181
|
+
### Fixed
|
|
182
|
+
- M&A History PDF generation bug: Stopped truncating AI timeline events, full timeline is now preserved.
|
|
183
|
+
- Group Structure Classification: Prevented PE Funds (BPIFrance, IK Partners, etc.) from being improperly categorized as operational subsidiaries in the AI due diligence report.
|
|
184
|
+
- Fixed `pdfData` passthrough bug that caused empty PDF exports.
|
|
Binary file
|
package/RELEASE.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
✅ `intelwatch@1.2.0` est publié sur npm !
|
|
2
|
+
|
|
3
|
+
```bash
|
|
4
|
+
npm install -g intelwatch
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
### Ce qui est en ligne :
|
|
8
|
+
- Les bugs de parsing AI corrigés en prod
|
|
9
|
+
- L'export JSON et CSV natif pour les rapports
|
|
10
|
+
- Les `--lang fr` qui adaptent les rapports AI
|
|
11
|
+
- La détection de 56 technos (Nuxt, Vercel, Tailwind, etc.)
|
|
12
|
+
|
|
13
|
+
**Note Git** : J'ai fait le commit local de la v1.2.0, mais GitHub a refusé mon push (`403 Permission to Recognity/intelwatch.git denied to ashroth1`). Tu pourras faire un `git push origin main --tags` depuis ta machine quand tu as le temps.
|
|
14
|
+
|
|
15
|
+
Dis-moi si tu veux que le sous-agent attaque un autre CLI ou une autre feature d'intelwatch (ex: Inpi, ou détection SaaS) cette nuit !
|
package/bin/intelwatch.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { program } from '../src/index.js';
|
|
4
|
+
import { setupGlobalErrorHandler, handleError } from '../src/utils/error-handler.js';
|
|
4
5
|
|
|
6
|
+
// Setup global error handling
|
|
7
|
+
setupGlobalErrorHandler();
|
|
8
|
+
|
|
9
|
+
// Parse CLI arguments with error handling
|
|
5
10
|
program.parseAsync(process.argv).catch(err => {
|
|
6
|
-
|
|
11
|
+
handleError(err, 'CLI');
|
|
7
12
|
process.exit(1);
|
|
8
13
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "intelwatch",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Competitive intelligence CLI — track competitors, keywords, and brand mentions from the terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"intelwatch": "./bin/intelwatch.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"test": "node --test test
|
|
11
|
+
"test": "node --test test/storage.test.js test/sentiment.test.js test/tech-detect.test.js test/export.test.js test/i18n.test.js test/error-handler.test.js test/reddit-hn.test.js test/license.test.js test/providers.test.js",
|
|
12
12
|
"start": "node bin/intelwatch.js"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"cli-table3": "^0.6.5",
|
|
20
20
|
"commander": "^12.1.0",
|
|
21
21
|
"inquirer": "^10.2.2",
|
|
22
|
+
"xlsx": "^0.18.5",
|
|
22
23
|
"yaml": "^2.6.1"
|
|
23
24
|
},
|
|
24
25
|
"engines": {
|
package/src/commands/check.js
CHANGED
|
@@ -7,9 +7,17 @@ import { runKeywordCheck, diffKeywordSnapshots } from '../trackers/keyword.js';
|
|
|
7
7
|
import { runBrandCheck, diffBrandSnapshots } from '../trackers/brand.js';
|
|
8
8
|
import { runPersonCheck, diffPersonSnapshots } from '../trackers/person.js';
|
|
9
9
|
import { header, section, diffLine, success, warn, error, trackerTypeIcon } from '../utils/display.js';
|
|
10
|
+
import { exportToJSON, exportToCSV, formatForExport } from '../utils/export.js';
|
|
11
|
+
import { setLanguage, getLanguage } from '../utils/i18n.js';
|
|
12
|
+
|
|
13
|
+
export async function runCheck(options = {}) {
|
|
14
|
+
// Set language from global option
|
|
15
|
+
if (options.parent?.opts()?.lang) {
|
|
16
|
+
setLanguage(options.parent.opts().lang);
|
|
17
|
+
}
|
|
10
18
|
|
|
11
|
-
export async function runCheck(options) {
|
|
12
19
|
const trackers = loadTrackers();
|
|
20
|
+
const results = []; // Pour l'export
|
|
13
21
|
|
|
14
22
|
if (trackers.length === 0) {
|
|
15
23
|
warn('No trackers configured. Use `intelwatch track` to add one.');
|
|
@@ -28,6 +36,17 @@ export async function runCheck(options) {
|
|
|
28
36
|
let totalChanges = 0;
|
|
29
37
|
|
|
30
38
|
for (const tracker of toCheck) {
|
|
39
|
+
const result = {
|
|
40
|
+
trackerId: tracker.id,
|
|
41
|
+
name: tracker.name || tracker.url,
|
|
42
|
+
url: tracker.url,
|
|
43
|
+
type: tracker.type,
|
|
44
|
+
status: 'unknown',
|
|
45
|
+
changes: [],
|
|
46
|
+
snapshot: null,
|
|
47
|
+
error: null,
|
|
48
|
+
checkedAt: new Date().toISOString()
|
|
49
|
+
};
|
|
31
50
|
header(`${trackerTypeIcon(tracker.type)} ${tracker.name || tracker.keyword || tracker.brandName} [${tracker.id}]`);
|
|
32
51
|
|
|
33
52
|
try {
|
|
@@ -63,8 +82,17 @@ export async function runCheck(options) {
|
|
|
63
82
|
checkCount: (tracker.checkCount || 0) + 1,
|
|
64
83
|
});
|
|
65
84
|
|
|
85
|
+
// Update result data
|
|
86
|
+
result.status = snapshot.error ? 'error' : 'success';
|
|
87
|
+
result.changes = changes;
|
|
88
|
+
result.snapshot = snapshot;
|
|
89
|
+
result.techStack = snapshot.techStack;
|
|
90
|
+
result.seoScore = snapshot.seoSignals?.score;
|
|
91
|
+
result.sentiment = snapshot.sentiment;
|
|
92
|
+
|
|
66
93
|
if (snapshot.error) {
|
|
67
94
|
warn(` Error: ${snapshot.error}`);
|
|
95
|
+
result.error = snapshot.error;
|
|
68
96
|
} else {
|
|
69
97
|
success(` Check complete`);
|
|
70
98
|
}
|
|
@@ -255,7 +283,11 @@ export async function runCheck(options) {
|
|
|
255
283
|
} catch (err) {
|
|
256
284
|
error(` Failed: ${err.message}`);
|
|
257
285
|
updateTracker(tracker.id, { status: 'error', lastError: err.message });
|
|
286
|
+
result.status = 'error';
|
|
287
|
+
result.error = err.message;
|
|
258
288
|
}
|
|
289
|
+
|
|
290
|
+
results.push(result);
|
|
259
291
|
}
|
|
260
292
|
|
|
261
293
|
console.log('');
|
|
@@ -264,4 +296,23 @@ export async function runCheck(options) {
|
|
|
264
296
|
} else {
|
|
265
297
|
console.log(chalk.gray('No changes detected.'));
|
|
266
298
|
}
|
|
299
|
+
|
|
300
|
+
// ── Export ─────────────────────────────────────────────────────────────────
|
|
301
|
+
if (options.export) {
|
|
302
|
+
try {
|
|
303
|
+
const formatted = formatForExport(results, 'check');
|
|
304
|
+
|
|
305
|
+
if (options.export.toLowerCase() === 'json') {
|
|
306
|
+
const result = exportToJSON(formatted, options.output);
|
|
307
|
+
console.log(chalk.green(`\n ✅ ${result}\n`));
|
|
308
|
+
} else if (options.export.toLowerCase() === 'csv') {
|
|
309
|
+
const result = exportToCSV(formatted, options.output);
|
|
310
|
+
console.log(chalk.green(`\n ✅ ${result}\n`));
|
|
311
|
+
} else {
|
|
312
|
+
console.log(chalk.yellow(`\n ⚠️ Unsupported export format: ${options.export}. Use 'json' or 'csv'.\n`));
|
|
313
|
+
}
|
|
314
|
+
} catch (e) {
|
|
315
|
+
console.error(chalk.red(`\n ❌ Export failed: ${e.message}\n`));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
267
318
|
}
|
package/src/commands/digest.js
CHANGED
|
@@ -5,9 +5,17 @@ import { diffKeywordSnapshots } from '../trackers/keyword.js';
|
|
|
5
5
|
import { diffBrandSnapshots } from '../trackers/brand.js';
|
|
6
6
|
import { createTable, header, section, trackerTypeIcon, warn } from '../utils/display.js';
|
|
7
7
|
import { hasAIKey, callAI, getAIConfig } from '../ai/client.js';
|
|
8
|
+
import { exportToJSON, exportToCSV, formatForExport } from '../utils/export.js';
|
|
9
|
+
import { setLanguage, getLanguage } from '../utils/i18n.js';
|
|
10
|
+
|
|
11
|
+
export async function runDigest(options = {}) {
|
|
12
|
+
// Set language from global option
|
|
13
|
+
if (options.parent?.opts()?.lang) {
|
|
14
|
+
setLanguage(options.parent.opts().lang);
|
|
15
|
+
}
|
|
8
16
|
|
|
9
|
-
export async function runDigest() {
|
|
10
17
|
const trackers = loadTrackers();
|
|
18
|
+
const digestData = []; // Pour l'export
|
|
11
19
|
|
|
12
20
|
if (trackers.length === 0) {
|
|
13
21
|
warn('No trackers configured. Use `intelwatch track` to add one.');
|
|
@@ -62,6 +70,18 @@ export async function runDigest() {
|
|
|
62
70
|
summary.slice(0, 50),
|
|
63
71
|
]);
|
|
64
72
|
|
|
73
|
+
// Data pour export
|
|
74
|
+
digestData.push({
|
|
75
|
+
trackerId: tracker.id,
|
|
76
|
+
name: target,
|
|
77
|
+
type: tracker.type,
|
|
78
|
+
changes: changes,
|
|
79
|
+
changesCount: changes.length,
|
|
80
|
+
lastCheck: latest.timestamp || latest.createdAt,
|
|
81
|
+
summary: summary.length > 50 ? summary.slice(0, 47) + '...' : summary,
|
|
82
|
+
status: latest.error ? 'error' : 'active'
|
|
83
|
+
});
|
|
84
|
+
|
|
65
85
|
totalChanges += changes.length;
|
|
66
86
|
}
|
|
67
87
|
|
|
@@ -83,6 +103,25 @@ export async function runDigest() {
|
|
|
83
103
|
} else {
|
|
84
104
|
console.log(chalk.gray('\nTip: set OPENAI_API_KEY or ANTHROPIC_API_KEY for AI-powered digest analysis.'));
|
|
85
105
|
}
|
|
106
|
+
|
|
107
|
+
// ── Export ─────────────────────────────────────────────────────────────────
|
|
108
|
+
if (options.export) {
|
|
109
|
+
try {
|
|
110
|
+
const formatted = formatForExport(digestData, 'digest');
|
|
111
|
+
|
|
112
|
+
if (options.export.toLowerCase() === 'json') {
|
|
113
|
+
const result = exportToJSON(formatted, options.output);
|
|
114
|
+
console.log(chalk.green(`\n ✅ ${result}\n`));
|
|
115
|
+
} else if (options.export.toLowerCase() === 'csv') {
|
|
116
|
+
const result = exportToCSV(formatted, options.output);
|
|
117
|
+
console.log(chalk.green(`\n ✅ ${result}\n`));
|
|
118
|
+
} else {
|
|
119
|
+
console.log(chalk.yellow(`\n ⚠️ Unsupported export format: ${options.export}. Use 'json' or 'csv'.\n`));
|
|
120
|
+
}
|
|
121
|
+
} catch (e) {
|
|
122
|
+
console.error(chalk.red(`\n ❌ Export failed: ${e.message}\n`));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
86
125
|
}
|
|
87
126
|
|
|
88
127
|
async function runAIDigestSummary(trackers) {
|