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.
- package/package.json +4 -4
- package/scripts/generators/news-enhanced.d.ts +12 -0
- package/scripts/generators/news-enhanced.js +37 -5
- package/scripts/generators/pipeline/analysis-classification.js +79 -13
- package/scripts/generators/pipeline/analysis-existing.js +55 -3
- package/scripts/generators/pipeline/analysis-stage.d.ts +1 -1
- package/scripts/generators/pipeline/analysis-stage.js +5 -6
- package/scripts/generators/pipeline/analysis-threats.d.ts +1 -1
- package/scripts/generators/pipeline/analysis-threats.js +1 -1
- package/scripts/generators/pipeline/fetch-stage.d.ts +10 -0
- package/scripts/generators/pipeline/fetch-stage.js +263 -87
- package/scripts/generators/pipeline/generate-stage.d.ts +8 -0
- package/scripts/generators/pipeline/generate-stage.js +38 -7
- package/scripts/generators/strategies/article-strategy.d.ts +2 -2
- package/scripts/generators/strategies/article-strategy.js +3 -3
- package/scripts/generators/strategies/breaking-news-strategy.js +38 -34
- package/scripts/generators/strategies/committee-reports-strategy.js +32 -30
- package/scripts/generators/strategies/motions-strategy.js +31 -31
- package/scripts/generators/strategies/propositions-strategy.js +25 -33
- package/scripts/index.d.ts +1 -1
- package/scripts/index.js +1 -1
- package/scripts/mcp/ep-mcp-client.d.ts +17 -1
- package/scripts/mcp/ep-mcp-client.js +67 -16
- package/scripts/mcp/mcp-connection.js +9 -1
- package/scripts/templates/article-template.js +30 -13
- package/scripts/templates/section-builders.js +2 -5
- package/scripts/types/index.d.ts +1 -1
- package/scripts/types/mcp.d.ts +7 -0
- package/scripts/types/political-classification.d.ts +1 -1
- package/scripts/utils/content-metadata.js +60 -17
- package/scripts/utils/file-utils.d.ts +2 -2
- package/scripts/utils/file-utils.js +2 -2
- package/scripts/utils/html-sanitize.d.ts +10 -0
- package/scripts/utils/html-sanitize.js +32 -0
- package/scripts/utils/metadata-utils.d.ts +11 -4
- package/scripts/utils/metadata-utils.js +17 -0
- package/scripts/utils/political-classification.d.ts +7 -7
- package/scripts/utils/political-classification.js +7 -7
- package/scripts/utils/political-risk-assessment.d.ts +1 -1
- package/scripts/utils/political-risk-assessment.js +1 -1
- package/scripts/utils/significance-scoring.d.ts +5 -5
- 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.
|
|
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.
|
|
144
|
-
"@typescript-eslint/parser": "8.58.
|
|
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.
|
|
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
|
-
/**
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
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
|
|
295
|
-
reference
|
|
296
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
77
|
-
//
|
|
78
|
-
|
|
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')
|