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.
@@ -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
- # Changelog
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
- console.error('Fatal error:', err.message);
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.1.6",
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/*.test.js",
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": {
@@ -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
  }
@@ -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) {