euparliamentmonitor 0.8.20 → 0.8.22

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 (42) hide show
  1. package/package.json +4 -4
  2. package/scripts/generators/news-enhanced.d.ts +12 -0
  3. package/scripts/generators/news-enhanced.js +37 -5
  4. package/scripts/generators/pipeline/analysis-classification.js +79 -13
  5. package/scripts/generators/pipeline/analysis-existing.js +55 -3
  6. package/scripts/generators/pipeline/analysis-stage.d.ts +1 -1
  7. package/scripts/generators/pipeline/analysis-stage.js +5 -6
  8. package/scripts/generators/pipeline/analysis-threats.d.ts +1 -1
  9. package/scripts/generators/pipeline/analysis-threats.js +1 -1
  10. package/scripts/generators/pipeline/fetch-stage.d.ts +10 -0
  11. package/scripts/generators/pipeline/fetch-stage.js +263 -87
  12. package/scripts/generators/pipeline/generate-stage.d.ts +8 -0
  13. package/scripts/generators/pipeline/generate-stage.js +38 -7
  14. package/scripts/generators/strategies/article-strategy.d.ts +2 -2
  15. package/scripts/generators/strategies/article-strategy.js +3 -3
  16. package/scripts/generators/strategies/breaking-news-strategy.js +38 -34
  17. package/scripts/generators/strategies/committee-reports-strategy.js +32 -30
  18. package/scripts/generators/strategies/motions-strategy.js +31 -31
  19. package/scripts/generators/strategies/propositions-strategy.js +25 -33
  20. package/scripts/index.d.ts +1 -1
  21. package/scripts/index.js +1 -1
  22. package/scripts/mcp/ep-mcp-client.d.ts +17 -1
  23. package/scripts/mcp/ep-mcp-client.js +67 -16
  24. package/scripts/mcp/mcp-connection.js +9 -1
  25. package/scripts/templates/article-template.js +30 -13
  26. package/scripts/templates/section-builders.js +2 -5
  27. package/scripts/types/index.d.ts +1 -1
  28. package/scripts/types/mcp.d.ts +7 -0
  29. package/scripts/types/political-classification.d.ts +1 -1
  30. package/scripts/utils/content-metadata.js +60 -17
  31. package/scripts/utils/file-utils.d.ts +2 -2
  32. package/scripts/utils/file-utils.js +2 -2
  33. package/scripts/utils/html-sanitize.d.ts +10 -0
  34. package/scripts/utils/html-sanitize.js +32 -0
  35. package/scripts/utils/metadata-utils.d.ts +11 -4
  36. package/scripts/utils/metadata-utils.js +17 -0
  37. package/scripts/utils/political-classification.d.ts +7 -7
  38. package/scripts/utils/political-classification.js +7 -7
  39. package/scripts/utils/political-risk-assessment.d.ts +1 -1
  40. package/scripts/utils/political-risk-assessment.js +1 -1
  41. package/scripts/utils/significance-scoring.d.ts +5 -5
  42. package/scripts/utils/significance-scoring.js +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "euparliamentmonitor",
3
- "version": "0.8.20",
3
+ "version": "0.8.22",
4
4
  "type": "module",
5
5
  "description": "European Parliament Intelligence Platform - Monitor political activity with systematic transparency",
6
6
  "main": "scripts/index.js",
@@ -140,8 +140,8 @@
140
140
  "@types/d3": "7.4.3",
141
141
  "@types/node": "25.5.2",
142
142
  "@types/papaparse": "5.5.2",
143
- "@typescript-eslint/eslint-plugin": "8.58.0",
144
- "@typescript-eslint/parser": "8.58.0",
143
+ "@typescript-eslint/eslint-plugin": "8.58.1",
144
+ "@typescript-eslint/parser": "8.58.1",
145
145
  "@vitest/coverage-v8": "4.1.3",
146
146
  "@vitest/ui": "4.1.3",
147
147
  "chart.js": "4.5.1",
@@ -169,7 +169,7 @@
169
169
  "node": ">=25"
170
170
  },
171
171
  "dependencies": {
172
- "european-parliament-mcp-server": "1.1.28"
172
+ "european-parliament-mcp-server": "1.2.0"
173
173
  },
174
174
  "optionalDependencies": {
175
175
  "worldbank-mcp": "1.0.1"
@@ -11,4 +11,16 @@ export { applyCommitteeInfo, applyDocuments, applyEffectiveness, FEATURED_COMMIT
11
11
  export { PLACEHOLDER_MARKER, getMotionsFallbackData, generateMotionsContent, buildPoliticalAlignmentSection, };
12
12
  export { buildPropositionsContent };
13
13
  export type { PipelineData };
14
+ /**
15
+ * AI-generated article title passed by the agentic workflow.
16
+ * When provided, this OVERRIDES any script-generated title.
17
+ * The AI agent (Opus 4.6) must analyse the content and produce this.
18
+ */
19
+ export declare const aiTitle: string;
20
+ /**
21
+ * AI-generated article description/subtitle passed by the agentic workflow.
22
+ * When provided, this OVERRIDES any script-generated description.
23
+ * The AI agent (Opus 4.6) must analyse the content and produce this.
24
+ */
25
+ export declare const aiDescription: string;
14
26
  //# sourceMappingURL=news-enhanced.d.ts.map
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * When the `--analysis` flag is supplied (all 9 agentic workflows do this),
13
13
  * the analysis stage runs **before** article generation, producing structured
14
- * political intelligence artifacts under `analysis/{date}/{article-type}/`. These
14
+ * political intelligence artifacts under `analysis/daily/{date}/{article-type}/`. These
15
15
  * artifacts are committed to the repository for review and improvement.
16
16
  *
17
17
  * Pipeline stages:
@@ -45,7 +45,7 @@ import { closeEPMCPClient } from '../mcp/ep-mcp-client.js';
45
45
  import { ensureDirectoryExists } from '../utils/file-utils.js';
46
46
  // ─── Pipeline-stage imports ───────────────────────────────────────────────────
47
47
  import { initializeMCPClient, fetchEPFeedData } from './pipeline/fetch-stage.js';
48
- import { createStrategyRegistry, generateArticleForStrategy } from './pipeline/generate-stage.js';
48
+ import { createStrategyRegistry, generateArticleForStrategy, setAIMetadata, } from './pipeline/generate-stage.js';
49
49
  import { writeGenerationMetadata } from './pipeline/output-stage.js';
50
50
  import { runAnalysisStage, ALL_ANALYSIS_METHODS, VALID_ANALYSIS_METHODS, hasSubstantiveData, deriveArticleTypeSlug, } from './pipeline/analysis-stage.js';
51
51
  // ─── Content-module imports (bounded contexts) ───────────────────────────────
@@ -73,6 +73,22 @@ const analysisOnlyArg = args.includes('--analysis-only');
73
73
  const analysisVerboseArg = args.includes('--analysis-verbose');
74
74
  const analysisDirArg = args.find((arg) => arg.startsWith('--analysis-dir='));
75
75
  const analysisMethodsArg = args.find((arg) => arg.startsWith('--analysis-methods='));
76
+ const titleArg = args.find((arg) => arg.startsWith('--title='));
77
+ const descriptionArg = args.find((arg) => arg.startsWith('--description='));
78
+ /**
79
+ * AI-generated article title passed by the agentic workflow.
80
+ * When provided, this OVERRIDES any script-generated title.
81
+ * The AI agent (Opus 4.6) must analyse the content and produce this.
82
+ */
83
+ export const aiTitle = titleArg ? titleArg.slice('--title='.length).trim() : '';
84
+ /**
85
+ * AI-generated article description/subtitle passed by the agentic workflow.
86
+ * When provided, this OVERRIDES any script-generated description.
87
+ * The AI agent (Opus 4.6) must analyse the content and produce this.
88
+ */
89
+ export const aiDescription = descriptionArg
90
+ ? descriptionArg.slice('--description='.length).trim()
91
+ : '';
76
92
  /** Path to a JSON file containing pre-fetched EP feed data (optional). */
77
93
  const feedDataPath = feedDataArg?.startsWith('--feed-data=')
78
94
  ? feedDataArg.slice('--feed-data='.length).trim()
@@ -177,7 +193,7 @@ function parseAnalysisMethods() {
177
193
  * Run the optional analysis stage (Fetch → Analysis) before article generation.
178
194
  *
179
195
  * This function is **side-effect-only**: it writes analysis markdown and a
180
- * `manifest.json` to disk under `analysis/{date}/{article-type}/`. The returned
196
+ * `manifest.json` to disk under `analysis/daily/{date}/{article-type}/`. The returned
181
197
  * {@link AnalysisContext} is informational; strategies read analysis output
182
198
  * from disk rather than consuming the context object in-memory. Analysis
183
199
  * artifacts are committed to the repository for review and political
@@ -203,7 +219,7 @@ async function maybeRunAnalysis(date, client) {
203
219
  const trimmedAnalysisDirBase = rawAnalysisDirBase?.trim();
204
220
  const analysisDirBase = trimmedAnalysisDirBase && trimmedAnalysisDirBase.length > 0
205
221
  ? trimmedAnalysisDirBase
206
- : 'analysis';
222
+ : 'analysis/daily';
207
223
  const enabledMethods = parseAnalysisMethods();
208
224
  console.log('');
209
225
  console.log('🔬 Running analysis stage...');
@@ -332,6 +348,20 @@ async function runAnalysisWithGuard(date, client) {
332
348
  }
333
349
  return analysisCtx;
334
350
  }
351
+ /**
352
+ * Wire AI-provided title/description from CLI `--title` and `--description` flags.
353
+ * The AI agent (Opus 4.6) passes these after analysing the content.
354
+ * They override ALL script-generated metadata for the English version.
355
+ */
356
+ function wireAIMetadata() {
357
+ if (aiTitle || aiDescription) {
358
+ setAIMetadata(aiTitle, aiDescription);
359
+ if (aiTitle)
360
+ console.log(`📝 AI-provided title: "${aiTitle}"`);
361
+ if (aiDescription)
362
+ console.log(`📝 AI-provided description: "${aiDescription}"`);
363
+ }
364
+ }
335
365
  /**
336
366
  * Main execution: initialise the MCP client, optionally run analysis stage,
337
367
  * iterate over requested article types, delegate to the appropriate strategy,
@@ -341,6 +371,8 @@ async function main() {
341
371
  console.log('');
342
372
  console.log('🚀 Starting news generation...');
343
373
  console.log('');
374
+ // Wire AI-provided title/description from CLI flags.
375
+ wireAIMetadata();
344
376
  // When --feed-data is provided, expose the path via env so strategies can
345
377
  // load pre-fetched data without requiring a live MCP connection.
346
378
  if (feedDataPath) {
@@ -362,7 +394,7 @@ async function main() {
362
394
  // Expose analysis dir/slug via env vars so strategies can locate analysis
363
395
  // artifacts without hard-coding paths. Follows the EP_FEED_DATA_FILE pattern.
364
396
  if (analysisCtx) {
365
- // Base dir: parent of date-scoped dir (e.g. 'analysis' from 'analysis/2026-04-06/breaking')
397
+ // Base dir: parent of date-scoped dir (e.g. 'analysis/daily' from 'analysis/daily/2026-04-06/breaking')
366
398
  const analysisOutputParent = path.dirname(analysisCtx.outputDir);
367
399
  const analysisBaseDir = path.dirname(analysisOutputParent);
368
400
  process.env['EP_ANALYSIS_DIR'] = analysisBaseDir;
@@ -34,12 +34,77 @@ const EVENT_PUBLIC_LOW = 3;
34
34
  const EVENT_DEFAULT_URGENCY = 5;
35
35
  const EVENT_INSTITUTIONAL_HIGH = 7;
36
36
  const EVENT_INSTITUTIONAL_LOW = 4;
37
- /** Default dimension scores for adopted texts (plenary-approved) */
38
- const ADOPTED_PARLIAMENTARY = 7;
39
- const ADOPTED_POLICY = 6;
40
- const ADOPTED_PUBLIC = 5;
41
- const ADOPTED_URGENCY = 4;
42
- const ADOPTED_INSTITUTIONAL = 6;
37
+ /** Base dimension scores for adopted texts (plenary-approved) */
38
+ const ADOPTED_PARLIAMENTARY_BASE = 6;
39
+ const ADOPTED_POLICY_BASE = 5;
40
+ const ADOPTED_PUBLIC_BASE = 4;
41
+ const ADOPTED_URGENCY_BASE = 3;
42
+ const ADOPTED_INSTITUTIONAL_BASE = 5;
43
+ // ─── Content-aware scoring keywords for adopted text differentiation ──────────
44
+ /** Title keywords indicating high-impact legislative texts (directives, regulations) */
45
+ const HIGH_IMPACT_TITLE_KEYWORDS = /\b(?:directive|regulation|regulation\s+\(eu\)|codecision|ordinary\s+legislative|COD|budget|defence|security|tariff|anti-corruption|banking|single\s+market)\b/i;
46
+ /** Title keywords indicating moderate-impact texts */
47
+ const MODERATE_IMPACT_TITLE_KEYWORDS = /\b(?:resolution|decision|recommendation|amendment|framework|strategy|agreement|trade|environment|climate|digital|data\s+protection|consumer|health)\b/i;
48
+ /** Procedure references indicating ordinary legislative procedure (highest significance) */
49
+ const COD_PROCEDURE_PATTERN = /\bCOD\b|\b\d{4}\/\d{4}\(COD\)/i;
50
+ /** Pattern for recent EP10 adopted texts (current term) */
51
+ const EP10_ADOPTED_TEXT_PATTERN = /TA-10-202[5-9]/i;
52
+ /**
53
+ * Score an adopted text based on its actual content metadata.
54
+ *
55
+ * Analyses the title, reference, and any procedure type information to
56
+ * produce differentiated scores rather than flat constants. High-impact
57
+ * legislative texts (directives, regulations, COD procedures) score higher
58
+ * than routine administrative texts.
59
+ *
60
+ * @param title - Adopted text title or label
61
+ * @param reference - EP reference identifier
62
+ * @param workType - Optional work type field from EP data
63
+ * @param procedureReference - Optional procedure reference
64
+ * @returns Per-dimension scoring input
65
+ */
66
+ function scoreAdoptedText(title, reference, workType, procedureReference) {
67
+ let parliamentary = ADOPTED_PARLIAMENTARY_BASE;
68
+ let policy = ADOPTED_POLICY_BASE;
69
+ let publicInterest = ADOPTED_PUBLIC_BASE;
70
+ let urgency = ADOPTED_URGENCY_BASE;
71
+ let institutional = ADOPTED_INSTITUTIONAL_BASE;
72
+ const combined = [title, reference, workType, procedureReference].filter(Boolean).join(' ');
73
+ // Boost for high-impact legislative keywords
74
+ if (HIGH_IMPACT_TITLE_KEYWORDS.test(combined)) {
75
+ parliamentary += 2;
76
+ policy += 2;
77
+ publicInterest += 2;
78
+ institutional += 2;
79
+ }
80
+ else if (MODERATE_IMPACT_TITLE_KEYWORDS.test(combined)) {
81
+ parliamentary += 1;
82
+ policy += 1;
83
+ publicInterest += 1;
84
+ institutional += 1;
85
+ }
86
+ // Boost for ordinary legislative procedure (COD) — highest parliamentary significance
87
+ if (COD_PROCEDURE_PATTERN.test(combined)) {
88
+ parliamentary += 1;
89
+ policy += 1;
90
+ }
91
+ // Boost urgency for current-term (EP10) adopted texts
92
+ if (EP10_ADOPTED_TEXT_PATTERN.test(reference)) {
93
+ urgency += 2;
94
+ }
95
+ // Boost for legislative resolution work types
96
+ if (workType && /legislative.*resolution|position.*first.*reading/i.test(workType)) {
97
+ parliamentary += 1;
98
+ policy += 1;
99
+ }
100
+ return {
101
+ parliamentarySignificance: Math.min(10, parliamentary),
102
+ policyImpact: Math.min(10, policy),
103
+ publicInterest: Math.min(10, publicInterest),
104
+ temporalUrgency: Math.min(10, urgency),
105
+ institutionalRelevance: Math.min(10, institutional),
106
+ };
107
+ }
43
108
  /** Analysis method identifier for significance scoring */
44
109
  export const METHOD_SIGNIFICANCE_SCORING_ID = 'significance-scoring';
45
110
  // ─── Per-method markdown builders ────────────────────────────────────────────
@@ -290,14 +355,15 @@ export function buildSignificanceScoringMarkdown(fetchedData, date) {
290
355
  }),
291
356
  ...adoptedTexts.map((t) => {
292
357
  const at = t;
358
+ const title = String(at['title'] ?? at['label'] ?? 'Adopted Text');
359
+ const reference = String(at['id'] ?? '');
360
+ const workType = typeof at['work_type'] === 'string' ? at['work_type'] : undefined;
361
+ const procedureRef = typeof at['procedure_reference'] === 'string' ? at['procedure_reference'] : undefined;
362
+ const scores = scoreAdoptedText(title, reference, workType, procedureRef);
293
363
  return {
294
- title: String(at['title'] ?? at['label'] ?? 'Adopted Text'),
295
- reference: String(at['id'] ?? ''),
296
- parliamentarySignificance: ADOPTED_PARLIAMENTARY,
297
- policyImpact: ADOPTED_POLICY,
298
- publicInterest: ADOPTED_PUBLIC,
299
- temporalUrgency: ADOPTED_URGENCY,
300
- institutionalRelevance: ADOPTED_INSTITUTIONAL,
364
+ title,
365
+ reference,
366
+ ...scores,
301
367
  };
302
368
  }),
303
369
  ];
@@ -49,12 +49,42 @@ export function buildDeepAnalysisMarkdown(fetchedData, date) {
49
49
  adoptedTexts.length +
50
50
  questions.length +
51
51
  mepUpdates.length;
52
+ // Build a concrete list of the top adopted texts for the AI agent to analyze
53
+ const topAdoptedTexts = adoptedTexts
54
+ .slice(0, 20)
55
+ .map((t) => {
56
+ const adoptedText = t;
57
+ const title = String(adoptedText['title'] ?? adoptedText['label'] ?? 'Untitled');
58
+ const id = String(adoptedText['id'] ?? '');
59
+ const workType = String(adoptedText['work_type'] ?? '');
60
+ const procRef = String(adoptedText['procedure_reference'] ?? '');
61
+ return `| ${sanitizeCell(id)} | ${sanitizeCell(title.slice(0, 100))} | ${sanitizeCell(workType)} | ${sanitizeCell(procRef)} |`;
62
+ })
63
+ .join('\n');
64
+ const topEvents = events
65
+ .slice(0, 10)
66
+ .map((e) => {
67
+ const ev = e;
68
+ const title = String(ev['title'] ?? ev['label'] ?? 'Untitled');
69
+ const id = String(ev['id'] ?? ev['eventId'] ?? '');
70
+ return `| ${sanitizeCell(id)} | ${sanitizeCell(title.slice(0, 120))} |`;
71
+ })
72
+ .join('\n');
73
+ const topProcedures = procedures
74
+ .slice(0, 10)
75
+ .map((p) => {
76
+ const pr = p;
77
+ const title = String(pr['title'] ?? pr['label'] ?? 'Untitled');
78
+ const id = String(pr['procedureId'] ?? pr['id'] ?? '');
79
+ return `| ${sanitizeCell(id)} | ${sanitizeCell(title.slice(0, 120))} |`;
80
+ })
81
+ .join('\n');
52
82
  return (header +
53
83
  `# Deep Multi-Perspective Analysis
54
84
 
55
85
  ## Pipeline Data Context
56
86
 
57
- > **Note:** This section contains script-generated data inventory for reference. The AI agent must replace everything starting from the "AI Agent Instructions" heading below with substantive political intelligence analysis.
87
+ > **Note:** This section contains script-generated data inventory AND concrete document references for the AI agent to analyze. The AI agent must replace everything starting from the "AI Agent Instructions" heading below with substantive political intelligence analysis.
58
88
 
59
89
  | Data Source | Count |
60
90
  |-------------|-------|
@@ -75,13 +105,35 @@ export function buildDeepAnalysisMarkdown(fetchedData, date) {
75
105
  | Citizens | ${questions.length + mepUpdates.length} (questions + MEP updates) |
76
106
  | EU Institutions | ${events.length + procedures.length} (events + procedures) |
77
107
 
108
+ ${adoptedTexts.length > 0
109
+ ? `### Key Adopted Texts Available for Analysis
110
+
111
+ | Reference | Title | Work Type | Procedure |
112
+ |-----------|-------|-----------|-----------|
113
+ ${topAdoptedTexts}
114
+ `
115
+ : ''}${events.length > 0
116
+ ? `### Key Events Available for Analysis
117
+
118
+ | Reference | Title |
119
+ |-----------|-------|
120
+ ${topEvents}
121
+ `
122
+ : ''}${procedures.length > 0
123
+ ? `### Key Procedures Available for Analysis
124
+
125
+ | Reference | Title |
126
+ |-----------|-------|
127
+ ${topProcedures}
128
+ `
129
+ : ''}
78
130
  ---
79
131
 
80
132
  ## AI Agent Instructions
81
133
 
82
- > **Instructions for AI Agent (Opus 4.6):** Read ALL methodology documents in analysis/methodologies/ before writing. Using the data inventory above and the raw EP MCP data files, produce a deep multi-perspective analysis following the political-style-guide.md depth Level 3 format. Your analysis MUST:
134
+ > **Instructions for AI Agent:** Read ALL methodology documents in analysis/methodologies/ before writing. Using the concrete document references above and the raw EP MCP data, produce a deep multi-perspective analysis following the political-style-guide.md depth Level 3 format. Your analysis MUST:
83
135
  >
84
- > 1. **Identify the 3-5 most politically significant items** from the available data, citing specific document IDs
136
+ > 1. **Identify the 3-5 most politically significant items** from the document tables above, citing specific document IDs (e.g. TA-10-2026-0092)
85
137
  > 2. **Analyse each from ≥3 stakeholder perspectives** (Political Groups, Civil Society, Industry, National Governments, Citizens, EU Institutions)
86
138
  > 3. **Apply the SWOT framework** to the overall parliamentary activity pattern for this date
87
139
  > 4. **Assess coalition dynamics** — which groups are aligning/diverging based on the adopted texts?
@@ -26,7 +26,7 @@ export interface AnalysisStageOptions {
26
26
  readonly articleTypes: readonly ArticleCategory[];
27
27
  /** ISO date string (YYYY-MM-DD) for this analysis run */
28
28
  readonly date: string;
29
- /** Base output directory (e.g. 'analysis') */
29
+ /** Base output directory (e.g. 'analysis/daily') */
30
30
  readonly outputDir: string;
31
31
  /**
32
32
  * Filesystem-safe slug identifying the article type for this run.
@@ -11,7 +11,7 @@
11
11
  * news articles in all 14 languages.
12
12
  *
13
13
  * This stage is **side-effect-only**: it writes analysis markdown and a
14
- * `manifest.json` to disk under `analysis/{date}/{article-type}/`. When
14
+ * `manifest.json` to disk under `analysis/daily/{date}/{article-type}/`. When
15
15
  * `articleTypeSlug` is provided (recommended for agentic workflows), each
16
16
  * article type writes to its own subdirectory, preventing merge conflicts
17
17
  * when multiple workflows run concurrently on the same date.
@@ -29,7 +29,7 @@
29
29
  * const ctx = await runAnalysisStage(fetchedData, {
30
30
  * articleTypes: [ArticleCategory.WEEK_AHEAD],
31
31
  * date: '2026-03-26',
32
- * outputDir: 'analysis',
32
+ * outputDir: 'analysis/daily',
33
33
  * });
34
34
  * console.log(ctx.completedMethods);
35
35
  * ```
@@ -73,10 +73,9 @@ export const ALL_ANALYSIS_METHODS = [
73
73
  // Publication scoring & synthesis
74
74
  'significance-scoring',
75
75
  'synthesis-summary',
76
- // NOTE: 'document-analysis' is intentionally excluded from the default set.
77
- // It writes one markdown + one JSON file per feed item and can significantly
78
- // increase runtime and repository output size. Callers must opt-in by
79
- // explicitly listing it in `enabledMethods`.
76
+ // Per-document intelligence analysis stores complete EP document data
77
+ // alongside per-document political intelligence analysis markdown files.
78
+ 'document-analysis',
80
79
  ];
81
80
  /**
82
81
  * All valid analysis method names, including opt-in methods like
@@ -4,7 +4,7 @@ import type { AnalysisMethod } from './analysis-stage.js';
4
4
  * Build markdown for the political threat landscape assessment.
5
5
  *
6
6
  * Uses the pipeline `date` parameter to ensure the assessment date in the
7
- * generated markdown matches the `analysis/{date}/` folder, overriding
7
+ * generated markdown matches the `analysis/daily/{date}/` folder, overriding
8
8
  * the `new Date()` timestamp that `assessPoliticalThreats()` stamps internally.
9
9
  *
10
10
  * @param fetchedData - Raw fetched EP data
@@ -17,7 +17,7 @@ import { sanitizeCell, safeArr, toThreatInput, buildMarkdownHeader, EMPTY_TABLE_
17
17
  * Build markdown for the political threat landscape assessment.
18
18
  *
19
19
  * Uses the pipeline `date` parameter to ensure the assessment date in the
20
- * generated markdown matches the `analysis/{date}/` folder, overriding
20
+ * generated markdown matches the `analysis/daily/{date}/` folder, overriding
21
21
  * the `new Date()` timestamp that `assessPoliticalThreats()` stamps internally.
22
22
  *
23
23
  * @param fetchedData - Raw fetched EP data
@@ -217,6 +217,7 @@ export declare function fetchPipelineFromMCP(client: EuropeanParliamentMCPClient
217
217
  export declare function fetchProcedureStatusFromMCP(client: EuropeanParliamentMCPClient | null, procedureId: string): Promise<string>;
218
218
  /**
219
219
  * Fetch adopted texts feed from MCP.
220
+ * Falls back to a wider timeframe when the initial timeframe returns no data.
220
221
  *
221
222
  * @param client - MCP client or null
222
223
  * @param timeframe - How far back to look (default: 'one-week')
@@ -225,6 +226,8 @@ export declare function fetchProcedureStatusFromMCP(client: EuropeanParliamentMC
225
226
  export declare function fetchAdoptedTextsFeed(client: EuropeanParliamentMCPClient | null, timeframe?: FeedTimeframe): Promise<AdoptedTextFeedItem[]>;
226
227
  /**
227
228
  * Fetch events feed from MCP.
229
+ * Falls back to a wider timeframe when the initial timeframe returns no data
230
+ * (common during parliamentary recess when the EP API returns 404 for narrow windows).
228
231
  *
229
232
  * @param client - MCP client or null
230
233
  * @param timeframe - How far back to look (default: 'one-week')
@@ -233,6 +236,7 @@ export declare function fetchAdoptedTextsFeed(client: EuropeanParliamentMCPClien
233
236
  export declare function fetchEventsFeed(client: EuropeanParliamentMCPClient | null, timeframe?: FeedTimeframe): Promise<EventFeedItem[]>;
234
237
  /**
235
238
  * Fetch procedures feed from MCP.
239
+ * Falls back to a wider timeframe when the initial timeframe returns no data.
236
240
  *
237
241
  * @param client - MCP client or null
238
242
  * @param timeframe - How far back to look (default: 'one-week')
@@ -267,6 +271,7 @@ export declare function fetchMEPsFeedWithTotal(client: EuropeanParliamentMCPClie
267
271
  }>;
268
272
  /**
269
273
  * Fetch documents feed from MCP.
274
+ * Falls back to a wider timeframe when the initial timeframe returns no data.
270
275
  *
271
276
  * @param client - MCP client or null
272
277
  * @param timeframe - How far back to look (default: 'one-week')
@@ -275,6 +280,7 @@ export declare function fetchMEPsFeedWithTotal(client: EuropeanParliamentMCPClie
275
280
  export declare function fetchDocumentsFeed(client: EuropeanParliamentMCPClient | null, timeframe?: FeedTimeframe): Promise<DocumentFeedItem[]>;
276
281
  /**
277
282
  * Fetch plenary documents feed from MCP.
283
+ * Falls back to a wider timeframe when the initial timeframe returns no data.
278
284
  *
279
285
  * @param client - MCP client or null
280
286
  * @param timeframe - How far back to look (default: 'one-week')
@@ -283,6 +289,7 @@ export declare function fetchDocumentsFeed(client: EuropeanParliamentMCPClient |
283
289
  export declare function fetchPlenaryDocumentsFeed(client: EuropeanParliamentMCPClient | null, timeframe?: FeedTimeframe): Promise<DocumentFeedItem[]>;
284
290
  /**
285
291
  * Fetch committee documents feed from MCP.
292
+ * Falls back to a wider timeframe when the initial timeframe returns no data.
286
293
  *
287
294
  * @param client - MCP client or null
288
295
  * @param timeframe - How far back to look (default: 'one-week')
@@ -291,6 +298,7 @@ export declare function fetchPlenaryDocumentsFeed(client: EuropeanParliamentMCPC
291
298
  export declare function fetchCommitteeDocumentsFeed(client: EuropeanParliamentMCPClient | null, timeframe?: FeedTimeframe): Promise<DocumentFeedItem[]>;
292
299
  /**
293
300
  * Fetch plenary session documents feed from MCP.
301
+ * Falls back to a wider timeframe when the initial timeframe returns no data.
294
302
  *
295
303
  * @param client - MCP client or null
296
304
  * @param timeframe - How far back to look (default: 'one-week')
@@ -299,6 +307,7 @@ export declare function fetchCommitteeDocumentsFeed(client: EuropeanParliamentMC
299
307
  export declare function fetchPlenarySessionDocumentsFeed(client: EuropeanParliamentMCPClient | null, timeframe?: FeedTimeframe): Promise<DocumentFeedItem[]>;
300
308
  /**
301
309
  * Fetch external documents feed from MCP.
310
+ * Falls back to a wider timeframe when the initial timeframe returns no data.
302
311
  *
303
312
  * @param client - MCP client or null
304
313
  * @param timeframe - How far back to look (default: 'one-week')
@@ -307,6 +316,7 @@ export declare function fetchPlenarySessionDocumentsFeed(client: EuropeanParliam
307
316
  export declare function fetchExternalDocumentsFeed(client: EuropeanParliamentMCPClient | null, timeframe?: FeedTimeframe): Promise<DocumentFeedItem[]>;
308
317
  /**
309
318
  * Fetch parliamentary questions feed from MCP.
319
+ * Falls back to a wider timeframe when the initial timeframe returns no data.
310
320
  *
311
321
  * @param client - MCP client or null
312
322
  * @param timeframe - How far back to look (default: 'one-week')