intelwatch 1.1.0 → 1.1.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "intelwatch",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Competitive intelligence CLI — track competitors, keywords, and brand mentions from the terminal",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -12,6 +12,7 @@
12
12
  "start": "node bin/intelwatch.js"
13
13
  },
14
14
  "dependencies": {
15
+ "@recognity/pdf-report": "file:../shared-pdf",
15
16
  "axios": "^1.7.9",
16
17
  "chalk": "^5.3.0",
17
18
  "cheerio": "^1.0.0",
@@ -5,6 +5,7 @@ import { searchPressMentions } from '../scrapers/brave-search.js';
5
5
  import { analyzeSite } from '../scrapers/site-analyzer.js';
6
6
  import { callAI, hasAIKey } from '../ai/client.js';
7
7
  import { header, section, warn, error } from '../utils/display.js';
8
+ import { generatePDF } from '@recognity/pdf-report';
8
9
 
9
10
  const LICENSE_URL = 'https://recognity.fr/tools/intelwatch';
10
11
 
@@ -215,11 +216,13 @@ export async function runMA(sirenOrName, options) {
215
216
  }
216
217
 
217
218
  // ── Press & mentions ───────────────────────────────────────────────────────
219
+ let pressResults = [];
218
220
  if (identity.name) {
219
221
  section(' 📣 Presse & réputation');
220
222
  console.log(chalk.gray(` Searching mentions for "${identity.name}"...`));
221
223
  try {
222
224
  const press = await searchPressMentions(identity.name);
225
+ pressResults = press.mentions || [];
223
226
  if (press.mentionCount > 0) {
224
227
  const bd = press.mentions.reduce((acc, m) => {
225
228
  const k = /positive/.test(m.sentiment) ? 'positive'
@@ -299,6 +302,74 @@ Rédige une synthèse en 5 points : 1) Profil, 2) Gouvernance & actionnariat, 3)
299
302
  }
300
303
  }
301
304
 
305
+ // ── PDF export ──────────────────────────────────────────────────────────────
306
+ if (options.format === 'pdf') {
307
+ const outputPath = options.output || `profile-${siren}.pdf`;
308
+ const fmtEuro = (n) => {
309
+ if (n == null) return '—';
310
+ const abs = Math.abs(n);
311
+ const sign = n < 0 ? '-' : '';
312
+ if (abs >= 1e9) return `${sign}${(abs/1e9).toFixed(2)}B€`;
313
+ if (abs >= 1e6) return `${sign}${(abs/1e6).toFixed(1)}M€`;
314
+ if (abs >= 1e3) return `${sign}${Math.round(abs/1e3)}K€`;
315
+ return `${sign}${abs}€`;
316
+ };
317
+
318
+ const pressMentions = [];
319
+ if (pressResults?.length) {
320
+ pressResults.forEach(m => {
321
+ pressMentions.push({ title: m.title || '', source: m.source || '', sentiment: m.sentiment || 'neutral' });
322
+ });
323
+ }
324
+
325
+ const pdfData = {
326
+ aiSummary: null, // filled below if AI was used
327
+ competitors: [{
328
+ name: identity.name || siren,
329
+ url: identity.website || 'N/A',
330
+ tech: [identity.formeJuridique, identity.nafLabel, identity.nafCode].filter(Boolean),
331
+ social: {},
332
+ pappers: {
333
+ siren: identity.siren,
334
+ forme: identity.formeJuridique,
335
+ creation: identity.dateCreation,
336
+ naf: identity.nafCode ? identity.nafCode + ' — ' + identity.nafLabel : null,
337
+ ca: financialHistory?.[0]?.ca != null ? fmtEuro(financialHistory[0].ca) : 'N/A',
338
+ effectifs: identity.effectifs || 'N/A',
339
+ dirigeants: dirigeants?.map(d => d.nom || d.denomination || '?').slice(0, 5) || [],
340
+ },
341
+ press: pressMentions.length ? {
342
+ total: pressMentions.length,
343
+ positive: pressMentions.filter(m => m.sentiment === 'positive').length,
344
+ neutral: pressMentions.filter(m => m.sentiment === 'neutral').length,
345
+ negative: pressMentions.filter(m => m.sentiment === 'negative').length,
346
+ mentions: pressMentions.slice(0, 15),
347
+ } : undefined,
348
+ strengths: [],
349
+ weaknesses: [],
350
+ summary: `${identity.name || siren} — ${identity.formeJuridique || ''}, ${identity.nafLabel || ''}. Created ${identity.dateCreation || '?'}. ${financialHistory?.length ? `Financial history: ${financialHistory.length} years available.` : 'No financial data available.'}`,
351
+ }]
352
+ };
353
+
354
+ try {
355
+ await generatePDF({
356
+ type: 'intel-report',
357
+ title: `Deep Profile — ${identity.name || siren}`,
358
+ subtitle: `Company due diligence report · ${new Date().toLocaleDateString('en-GB', { year: 'numeric', month: 'long', day: 'numeric' })}`,
359
+ output: outputPath,
360
+ branding: {
361
+ company: 'Recognity',
362
+ footer: 'Powered by Recognity · recognity.fr',
363
+ colors: { primary: '#0a0a0a', accent: '#c8a961' },
364
+ },
365
+ data: pdfData,
366
+ });
367
+ console.log(chalk.green(`\n ✅ PDF report saved to ${outputPath}\n`));
368
+ } catch (e) {
369
+ warn(` PDF generation failed: ${e.message}`);
370
+ }
371
+ }
372
+
302
373
  // ── Footer ─────────────────────────────────────────────────────────────────
303
374
  console.log('');
304
375
  const today = new Date().toLocaleDateString('fr-FR', { year: 'numeric', month: 'long', day: 'numeric' });
package/src/index.js CHANGED
@@ -159,9 +159,11 @@ program
159
159
 
160
160
  program
161
161
  .command('profile <siren-or-name>')
162
- .description('M&A due diligence report for a French company (requires Pro license)')
162
+ .description('Deep company profile — due diligence report (requires Pro license)')
163
163
  .option('--preview', 'Run limited preview: company identity + last year financials only')
164
164
  .option('--ai', 'Generate an AI-powered due diligence summary (requires AI API key)')
165
+ .option('--format <type>', 'Output format: terminal (default) or pdf')
166
+ .option('--output <path>', 'Output file path for PDF')
165
167
  .action(async (sirenOrName, options) => {
166
168
  await runMA(sirenOrName, options);
167
169
  });
@@ -27,7 +27,7 @@ export async function pappersSearchByName(name, options = {}) {
27
27
  },
28
28
  timeout: 10000,
29
29
  });
30
- return { results: resp.data.resultats || [], error: null };
30
+ return { results: resp.data.resultats || resp.data.entreprises || [], error: null };
31
31
  } catch (err) {
32
32
  return { results: [], error: err.message };
33
33
  }