euparliamentmonitor 0.8.19 → 0.8.21
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 +7 -7
- package/scripts/constants/language-articles.d.ts +4 -0
- package/scripts/constants/language-articles.js +20 -0
- package/scripts/constants/language-ui.d.ts +8 -8
- package/scripts/constants/language-ui.js +64 -64
- package/scripts/constants/languages.d.ts +2 -2
- package/scripts/constants/languages.js +2 -2
- package/scripts/generators/news-enhanced.js +13 -3
- package/scripts/generators/pipeline/analysis-classification.d.ts +49 -0
- package/scripts/generators/pipeline/analysis-classification.js +333 -0
- package/scripts/generators/pipeline/analysis-existing.d.ts +67 -0
- package/scripts/generators/pipeline/analysis-existing.js +547 -0
- package/scripts/generators/pipeline/analysis-helpers.d.ts +140 -0
- package/scripts/generators/pipeline/analysis-helpers.js +266 -0
- package/scripts/generators/pipeline/analysis-risk.d.ts +49 -0
- package/scripts/generators/pipeline/analysis-risk.js +417 -0
- package/scripts/generators/pipeline/analysis-stage.d.ts +19 -39
- package/scripts/generators/pipeline/analysis-stage.js +219 -1704
- package/scripts/generators/pipeline/analysis-threats.d.ts +41 -0
- package/scripts/generators/pipeline/analysis-threats.js +142 -0
- package/scripts/generators/pipeline/fetch-stage.d.ts +25 -15
- package/scripts/generators/pipeline/fetch-stage.js +293 -117
- package/scripts/generators/strategies/article-strategy.d.ts +126 -7
- package/scripts/generators/strategies/article-strategy.js +491 -1
- package/scripts/generators/strategies/breaking-news-strategy.js +98 -8
- package/scripts/generators/strategies/committee-reports-strategy.js +23 -2
- package/scripts/generators/strategies/month-ahead-strategy.js +23 -2
- package/scripts/generators/strategies/monthly-review-strategy.js +13 -1
- package/scripts/generators/strategies/motions-strategy.js +15 -1
- package/scripts/generators/strategies/propositions-strategy.js +15 -1
- package/scripts/generators/strategies/week-ahead-strategy.js +19 -1
- package/scripts/generators/strategies/weekly-review-strategy.js +17 -1
- package/scripts/generators/synthesis-summary.d.ts +93 -0
- package/scripts/generators/synthesis-summary.js +364 -0
- package/scripts/index.d.ts +5 -2
- package/scripts/index.js +6 -1
- package/scripts/mcp/ep-mcp-client.d.ts +34 -1
- package/scripts/mcp/ep-mcp-client.js +110 -2
- package/scripts/mcp/mcp-connection.d.ts +3 -1
- package/scripts/mcp/mcp-connection.js +35 -4
- package/scripts/templates/article-template.js +24 -22
- package/scripts/templates/section-builders.js +2 -5
- package/scripts/types/index.d.ts +2 -1
- package/scripts/types/mcp.d.ts +7 -0
- package/scripts/types/political-classification.d.ts +1 -1
- package/scripts/types/quality.d.ts +9 -6
- package/scripts/types/significance.d.ts +130 -0
- package/scripts/types/significance.js +4 -0
- package/scripts/utils/article-quality-scorer.d.ts +13 -11
- package/scripts/utils/article-quality-scorer.js +36 -23
- 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/political-classification.d.ts +8 -7
- package/scripts/utils/political-classification.js +8 -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 +97 -0
- package/scripts/utils/significance-scoring.js +190 -0
|
@@ -9,6 +9,7 @@ import { buildBreakingAnalysis, buildBreakingSwot, buildBreakingDashboard, build
|
|
|
9
9
|
import { buildSwotSection } from '../swot-content.js';
|
|
10
10
|
import { buildDashboardSection } from '../dashboard-content.js';
|
|
11
11
|
import { buildIntelligenceMindmapSection } from '../mindmap-content.js';
|
|
12
|
+
import { loadAnalysisContext, buildAnalysisInsightsSection, extractAnalysisSummary, } from './article-strategy.js';
|
|
12
13
|
import { pl } from '../../utils/metadata-utils.js';
|
|
13
14
|
/** Base keywords shared by all Breaking News articles */
|
|
14
15
|
const BREAKING_NEWS_BASE_KEYWORDS = [
|
|
@@ -100,6 +101,50 @@ function buildBreakingTitleSuffix(feedData) {
|
|
|
100
101
|
parts.push(pl(feedData.procedures.length, 'Procedure', 'Procedures'));
|
|
101
102
|
return parts.join(', ');
|
|
102
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Extract a substantive summary from an analysis file if available.
|
|
106
|
+
*
|
|
107
|
+
* @param ctx - Analysis context
|
|
108
|
+
* @param method - Analysis method to look up
|
|
109
|
+
* @param maxLength - Maximum summary length
|
|
110
|
+
* @returns Extracted summary or empty string
|
|
111
|
+
*/
|
|
112
|
+
function extractAISummaryFromMethod(ctx, method, maxLength) {
|
|
113
|
+
const file = ctx.files.get(method);
|
|
114
|
+
if (!file)
|
|
115
|
+
return '';
|
|
116
|
+
// `extractAnalysisSummary()` already runs `prepareAnalysisBody()` which
|
|
117
|
+
// returns empty for scaffold content and strips non-prose blocks, so no
|
|
118
|
+
// separate `hasSubstantiveAIContent()` pre-check is needed.
|
|
119
|
+
const summary = extractAnalysisSummary(file.content, maxLength);
|
|
120
|
+
return summary.length > 50 ? summary : '';
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Enrich script-generated DeepAnalysis fields with substantive AI analysis
|
|
124
|
+
* content when available. AI-produced analysis files (deep-analysis,
|
|
125
|
+
* synthesis-summary) contain real political intelligence that should
|
|
126
|
+
* replace generic boilerplate text in the "what", "why", and "outlook" fields.
|
|
127
|
+
*
|
|
128
|
+
* @param analysis - Script-generated DeepAnalysis object
|
|
129
|
+
* @param ctx - Loaded analysis context (may be null)
|
|
130
|
+
* @returns Enriched DeepAnalysis with AI content replacing boilerplate where available
|
|
131
|
+
*/
|
|
132
|
+
function enrichAnalysisWithAIContent(analysis, ctx) {
|
|
133
|
+
if (!ctx)
|
|
134
|
+
return analysis;
|
|
135
|
+
const aiDeep = extractAISummaryFromMethod(ctx, 'deep-analysis', 800);
|
|
136
|
+
const aiSynth = extractAISummaryFromMethod(ctx, 'synthesis-summary', 600);
|
|
137
|
+
const aiCoalition = extractAISummaryFromMethod(ctx, 'coalition-analysis', 400);
|
|
138
|
+
// Distribute AI content across the deep analysis fields
|
|
139
|
+
const aiWhat = aiDeep || aiSynth;
|
|
140
|
+
const aiWhy = aiDeep && aiSynth ? aiSynth : '';
|
|
141
|
+
return {
|
|
142
|
+
...analysis,
|
|
143
|
+
what: aiWhat || analysis.what,
|
|
144
|
+
why: aiWhy || analysis.why,
|
|
145
|
+
outlook: aiCoalition || analysis.outlook,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
103
148
|
// ─── Strategy implementation ──────────────────────────────────────────────────
|
|
104
149
|
/**
|
|
105
150
|
* Article strategy for {@link ArticleCategory.BREAKING_NEWS}.
|
|
@@ -127,6 +172,8 @@ export class BreakingNewsStrategy {
|
|
|
127
172
|
* @returns Populated breaking news data payload
|
|
128
173
|
*/
|
|
129
174
|
async fetchData(client, date) {
|
|
175
|
+
// Load analysis context once for all return paths (graceful: null if absent)
|
|
176
|
+
const analysisContext = loadAnalysisContext(date, 'breaking');
|
|
130
177
|
// Step 0: Check for pre-fetched feed data file (set by --feed-data CLI arg).
|
|
131
178
|
// This allows agentic workflows to pass MCP data fetched via framework tools
|
|
132
179
|
// into the generator without requiring a direct MCP connection.
|
|
@@ -149,19 +196,34 @@ export class BreakingNewsStrategy {
|
|
|
149
196
|
fetchCoalitionDynamics(client),
|
|
150
197
|
]);
|
|
151
198
|
}
|
|
152
|
-
return {
|
|
199
|
+
return {
|
|
200
|
+
date,
|
|
201
|
+
feedData: fileFeedData,
|
|
202
|
+
anomalyRaw,
|
|
203
|
+
coalitionRaw,
|
|
204
|
+
reportRaw: '',
|
|
205
|
+
analysisContext,
|
|
206
|
+
};
|
|
153
207
|
}
|
|
154
208
|
console.log(' ⚠️ Pre-fetched feed data failed to load — falling through to MCP fetch');
|
|
155
209
|
}
|
|
156
210
|
if (client) {
|
|
157
211
|
console.log(' 📡 Fetching EP feed data (primary) and analytical context...');
|
|
158
212
|
}
|
|
159
|
-
// Step 1: Fetch feed data (PRIMARY news content) — '
|
|
160
|
-
|
|
213
|
+
// Step 1: Fetch feed data (PRIMARY news content) — 'one-week' to avoid
|
|
214
|
+
// 404s during EP recess / low-activity periods when 'today' has no data
|
|
215
|
+
const feedData = await fetchBreakingNewsFeedData(client, 'one-week');
|
|
161
216
|
// When client is null, feedData is undefined — MCP unavailable
|
|
162
217
|
if (!feedData) {
|
|
163
218
|
console.log(' ⚠️ MCP unavailable — no feed data or analytical context');
|
|
164
|
-
return {
|
|
219
|
+
return {
|
|
220
|
+
date,
|
|
221
|
+
feedData,
|
|
222
|
+
anomalyRaw: '',
|
|
223
|
+
coalitionRaw: '',
|
|
224
|
+
reportRaw: '',
|
|
225
|
+
analysisContext,
|
|
226
|
+
};
|
|
165
227
|
}
|
|
166
228
|
const totalFeedItems = feedData.adoptedTexts.length +
|
|
167
229
|
feedData.events.length +
|
|
@@ -174,14 +236,28 @@ export class BreakingNewsStrategy {
|
|
|
174
236
|
}
|
|
175
237
|
else {
|
|
176
238
|
console.log(' ⚠️ No feed data available — skipping analytical context fetch');
|
|
177
|
-
return {
|
|
239
|
+
return {
|
|
240
|
+
date,
|
|
241
|
+
feedData,
|
|
242
|
+
anomalyRaw: '',
|
|
243
|
+
coalitionRaw: '',
|
|
244
|
+
reportRaw: '',
|
|
245
|
+
analysisContext,
|
|
246
|
+
};
|
|
178
247
|
}
|
|
179
248
|
// Step 2: Fetch analytical context only when at least one feed item is available
|
|
180
249
|
const [anomalyRaw, coalitionRaw] = await Promise.all([
|
|
181
250
|
fetchVotingAnomalies(client),
|
|
182
251
|
fetchCoalitionDynamics(client),
|
|
183
252
|
]);
|
|
184
|
-
return {
|
|
253
|
+
return {
|
|
254
|
+
date,
|
|
255
|
+
feedData,
|
|
256
|
+
anomalyRaw,
|
|
257
|
+
coalitionRaw,
|
|
258
|
+
reportRaw: '',
|
|
259
|
+
analysisContext,
|
|
260
|
+
};
|
|
185
261
|
}
|
|
186
262
|
/**
|
|
187
263
|
* Build the breaking news HTML body for the specified language.
|
|
@@ -193,14 +269,28 @@ export class BreakingNewsStrategy {
|
|
|
193
269
|
buildContent(data, lang) {
|
|
194
270
|
const base = buildBreakingNewsContent(data.date, data.anomalyRaw, data.coalitionRaw, data.reportRaw, '', lang, [], [], [], data.feedData);
|
|
195
271
|
const analysis = buildBreakingAnalysis(data.date, data.feedData, data.anomalyRaw, data.coalitionRaw, lang);
|
|
196
|
-
|
|
272
|
+
// Enrich script-generated analysis with AI-produced content when available
|
|
273
|
+
const enriched = enrichAnalysisWithAIContent(analysis, data.analysisContext);
|
|
274
|
+
const deepSection = buildDeepAnalysisSection(enriched, lang, 'en');
|
|
197
275
|
const mindmapData = buildBreakingMindmap(data.feedData, lang);
|
|
198
276
|
const mindmapSection = buildIntelligenceMindmapSection(mindmapData, lang);
|
|
199
277
|
const swotData = buildBreakingSwot(data.feedData, data.anomalyRaw, data.coalitionRaw, lang);
|
|
200
278
|
const swotSection = buildSwotSection(swotData, lang);
|
|
201
279
|
const dashboardData = buildBreakingDashboard(data.feedData, lang);
|
|
202
280
|
const dashboardSection = buildDashboardSection(dashboardData, lang);
|
|
203
|
-
const
|
|
281
|
+
const analysisInsights = buildAnalysisInsightsSection(data.analysisContext, [
|
|
282
|
+
'deep-analysis',
|
|
283
|
+
'synthesis-summary',
|
|
284
|
+
'stakeholder-analysis',
|
|
285
|
+
'coalition-analysis',
|
|
286
|
+
'cross-session-intelligence',
|
|
287
|
+
'voting-patterns',
|
|
288
|
+
'risk-matrix',
|
|
289
|
+
'quantitative-swot',
|
|
290
|
+
'significance-classification',
|
|
291
|
+
'political-threat-landscape',
|
|
292
|
+
], lang);
|
|
293
|
+
const injection = deepSection + mindmapSection + swotSection + dashboardSection + analysisInsights;
|
|
204
294
|
// Inject before the closing </div> of .article-content
|
|
205
295
|
if (injection) {
|
|
206
296
|
const closingTag = '</div>';
|
|
@@ -10,6 +10,7 @@ import { buildCommitteeAnalysis, buildCommitteeSwot, buildCommitteeDashboard, bu
|
|
|
10
10
|
import { buildSwotSection } from '../swot-content.js';
|
|
11
11
|
import { buildDashboardSection } from '../dashboard-content.js';
|
|
12
12
|
import { buildIntelligenceMindmapSection } from '../mindmap-content.js';
|
|
13
|
+
import { loadAnalysisContext, buildAnalysisInsightsSection } from './article-strategy.js';
|
|
13
14
|
import { pl } from '../../utils/metadata-utils.js';
|
|
14
15
|
/** European Parliament home-page URL used as source reference */
|
|
15
16
|
const EP_SOURCE_URL = 'https://www.europarl.europa.eu';
|
|
@@ -356,7 +357,12 @@ export class CommitteeReportsStrategy {
|
|
|
356
357
|
fetchEPFeedData(client, 'one-month', feedDateRange),
|
|
357
358
|
]);
|
|
358
359
|
const committeeDataList = committeeDataRaw.filter((committee) => committee !== null);
|
|
359
|
-
return {
|
|
360
|
+
return {
|
|
361
|
+
date,
|
|
362
|
+
committeeDataList,
|
|
363
|
+
feedData,
|
|
364
|
+
analysisContext: loadAnalysisContext(date, 'committee-reports'),
|
|
365
|
+
};
|
|
360
366
|
}
|
|
361
367
|
/**
|
|
362
368
|
* Build the committee reports HTML body.
|
|
@@ -376,7 +382,22 @@ export class CommitteeReportsStrategy {
|
|
|
376
382
|
const swotSection = buildSwotSection(swotData, lang);
|
|
377
383
|
const dashboardData = buildCommitteeDashboard(data.committeeDataList, lang);
|
|
378
384
|
const dashboardSection = buildDashboardSection(dashboardData, lang);
|
|
379
|
-
const
|
|
385
|
+
const analysisInsights = buildAnalysisInsightsSection(data.analysisContext, [
|
|
386
|
+
'deep-analysis',
|
|
387
|
+
'synthesis-summary',
|
|
388
|
+
'stakeholder-analysis',
|
|
389
|
+
'coalition-analysis',
|
|
390
|
+
'cross-session-intelligence',
|
|
391
|
+
'significance-classification',
|
|
392
|
+
'impact-matrix',
|
|
393
|
+
'actor-mapping',
|
|
394
|
+
], lang);
|
|
395
|
+
const injection = feedSection +
|
|
396
|
+
deepSection +
|
|
397
|
+
mindmapSection +
|
|
398
|
+
swotSection +
|
|
399
|
+
dashboardSection +
|
|
400
|
+
analysisInsights;
|
|
380
401
|
// Inject before the closing </div> of .article-content
|
|
381
402
|
if (injection) {
|
|
382
403
|
const closingTag = '</div>';
|
|
@@ -9,6 +9,7 @@ import { buildProspectiveAnalysis, buildProspectiveSwot, buildProspectiveDashboa
|
|
|
9
9
|
import { buildSwotSection } from '../swot-content.js';
|
|
10
10
|
import { buildDashboardSection } from '../dashboard-content.js';
|
|
11
11
|
import { buildIntelligenceMindmapSection } from '../mindmap-content.js';
|
|
12
|
+
import { loadAnalysisContext, buildAnalysisInsightsSection } from './article-strategy.js';
|
|
12
13
|
import { pl } from '../../utils/metadata-utils.js';
|
|
13
14
|
/** Keywords shared by all Month Ahead articles */
|
|
14
15
|
const MONTH_AHEAD_KEYWORDS = [
|
|
@@ -125,7 +126,15 @@ export class MonthAheadStrategy {
|
|
|
125
126
|
]);
|
|
126
127
|
const keywords = [...MONTH_AHEAD_KEYWORDS, ...buildKeywords(monthData)];
|
|
127
128
|
const monthLabel = formatMonthLabel(dateRange.start);
|
|
128
|
-
return {
|
|
129
|
+
return {
|
|
130
|
+
date,
|
|
131
|
+
dateRange,
|
|
132
|
+
monthData,
|
|
133
|
+
keywords,
|
|
134
|
+
monthLabel,
|
|
135
|
+
feedData,
|
|
136
|
+
analysisContext: loadAnalysisContext(date, 'month-ahead'),
|
|
137
|
+
};
|
|
129
138
|
}
|
|
130
139
|
/**
|
|
131
140
|
* Build the month-ahead HTML body for the specified language.
|
|
@@ -144,7 +153,19 @@ export class MonthAheadStrategy {
|
|
|
144
153
|
const swotSection = buildSwotSection(swotData, lang);
|
|
145
154
|
const dashboardData = buildProspectiveDashboard(data.monthData, 'month', lang);
|
|
146
155
|
const dashboardSection = buildDashboardSection(dashboardData, lang);
|
|
147
|
-
|
|
156
|
+
const analysisInsights = buildAnalysisInsightsSection(data.analysisContext, [
|
|
157
|
+
'deep-analysis',
|
|
158
|
+
'synthesis-summary',
|
|
159
|
+
'stakeholder-analysis',
|
|
160
|
+
'coalition-analysis',
|
|
161
|
+
'cross-session-intelligence',
|
|
162
|
+
'significance-classification',
|
|
163
|
+
'political-threat-landscape',
|
|
164
|
+
'risk-matrix',
|
|
165
|
+
'forces-analysis',
|
|
166
|
+
'legislative-velocity-risk',
|
|
167
|
+
], lang);
|
|
168
|
+
return base.replace('<!-- /article-content -->', analysisSection + mindmapSection + swotSection + dashboardSection + analysisInsights);
|
|
148
169
|
}
|
|
149
170
|
/**
|
|
150
171
|
* Return language-specific metadata for the month-ahead article.
|
|
@@ -9,6 +9,7 @@ import { buildVotingAnalysis, buildVotingSwot, buildVotingDashboard, buildVoting
|
|
|
9
9
|
import { buildSwotSection } from '../swot-content.js';
|
|
10
10
|
import { buildDashboardSection } from '../dashboard-content.js';
|
|
11
11
|
import { buildIntelligenceMindmapSection } from '../mindmap-content.js';
|
|
12
|
+
import { loadAnalysisContext, buildAnalysisInsightsSection } from './article-strategy.js';
|
|
12
13
|
import { pl } from '../../utils/metadata-utils.js';
|
|
13
14
|
import { isPlaceholderText } from '../../constants/analysis-constants.js';
|
|
14
15
|
/** Base keywords shared by all Monthly Review articles */
|
|
@@ -159,6 +160,7 @@ export class MonthlyReviewStrategy {
|
|
|
159
160
|
questions,
|
|
160
161
|
monthLabel,
|
|
161
162
|
feedData,
|
|
163
|
+
analysisContext: loadAnalysisContext(date, 'month-in-review'),
|
|
162
164
|
};
|
|
163
165
|
}
|
|
164
166
|
/**
|
|
@@ -178,7 +180,17 @@ export class MonthlyReviewStrategy {
|
|
|
178
180
|
const swotSection = buildSwotSection(swotData, lang);
|
|
179
181
|
const dashboardData = buildVotingDashboard(data.votingRecords, data.votingPatterns, data.anomalies, lang);
|
|
180
182
|
const dashboardSection = buildDashboardSection(dashboardData, lang);
|
|
181
|
-
|
|
183
|
+
const analysisInsights = buildAnalysisInsightsSection(data.analysisContext, [
|
|
184
|
+
'deep-analysis',
|
|
185
|
+
'synthesis-summary',
|
|
186
|
+
'stakeholder-analysis',
|
|
187
|
+
'coalition-analysis',
|
|
188
|
+
'cross-session-intelligence',
|
|
189
|
+
'voting-patterns',
|
|
190
|
+
'significance-classification',
|
|
191
|
+
'legislative-velocity-risk',
|
|
192
|
+
], lang);
|
|
193
|
+
return base.replace('<!-- /article-content -->', deepSection + mindmapSection + swotSection + dashboardSection + analysisInsights);
|
|
182
194
|
}
|
|
183
195
|
/**
|
|
184
196
|
* Return language-specific metadata for the monthly review article.
|
|
@@ -9,6 +9,7 @@ import { buildVotingAnalysis, buildVotingSwot, buildVotingDashboard, buildVoting
|
|
|
9
9
|
import { buildSwotSection } from '../swot-content.js';
|
|
10
10
|
import { buildDashboardSection } from '../dashboard-content.js';
|
|
11
11
|
import { buildIntelligenceMindmapSection } from '../mindmap-content.js';
|
|
12
|
+
import { loadAnalysisContext, buildAnalysisInsightsSection } from './article-strategy.js';
|
|
12
13
|
import { pl } from '../../utils/metadata-utils.js';
|
|
13
14
|
import { isPlaceholderText } from '../../constants/analysis-constants.js';
|
|
14
15
|
/** Base keywords shared by all Motions articles */
|
|
@@ -152,6 +153,7 @@ export class MotionsStrategy {
|
|
|
152
153
|
anomalies,
|
|
153
154
|
questions,
|
|
154
155
|
feedData,
|
|
156
|
+
analysisContext: loadAnalysisContext(date, 'motions'),
|
|
155
157
|
};
|
|
156
158
|
}
|
|
157
159
|
/**
|
|
@@ -178,6 +180,17 @@ export class MotionsStrategy {
|
|
|
178
180
|
? buildVotingDashboard(data.votingRecords, data.votingPatterns, data.anomalies, lang)
|
|
179
181
|
: null;
|
|
180
182
|
const dashboardSection = buildDashboardSection(dashboardData, lang);
|
|
183
|
+
const analysisInsights = buildAnalysisInsightsSection(data.analysisContext, [
|
|
184
|
+
'deep-analysis',
|
|
185
|
+
'synthesis-summary',
|
|
186
|
+
'stakeholder-analysis',
|
|
187
|
+
'coalition-analysis',
|
|
188
|
+
'cross-session-intelligence',
|
|
189
|
+
'voting-patterns',
|
|
190
|
+
'political-threat-landscape',
|
|
191
|
+
'risk-matrix',
|
|
192
|
+
'actor-mapping',
|
|
193
|
+
], lang);
|
|
181
194
|
// Inject at the explicit <!-- /article-content --> marker so the section
|
|
182
195
|
// stays inside the .article-content styling scope. The marker is always
|
|
183
196
|
// emitted by generateMotionsContent as the last child of that wrapper and
|
|
@@ -187,7 +200,8 @@ export class MotionsStrategy {
|
|
|
187
200
|
deepSection +
|
|
188
201
|
mindmapSection +
|
|
189
202
|
swotSection +
|
|
190
|
-
dashboardSection
|
|
203
|
+
dashboardSection +
|
|
204
|
+
analysisInsights;
|
|
191
205
|
if (injection) {
|
|
192
206
|
return base.replace('<!-- /article-content -->', `${injection}\n`);
|
|
193
207
|
}
|
|
@@ -10,6 +10,7 @@ import { buildPropositionsAnalysis, buildPropositionsSwot, buildPropositionsDash
|
|
|
10
10
|
import { buildSwotSection } from '../swot-content.js';
|
|
11
11
|
import { buildDashboardSection } from '../dashboard-content.js';
|
|
12
12
|
import { buildIntelligenceMindmapSection } from '../mindmap-content.js';
|
|
13
|
+
import { loadAnalysisContext, buildAnalysisInsightsSection } from './article-strategy.js';
|
|
13
14
|
import { pl } from '../../utils/metadata-utils.js';
|
|
14
15
|
/** Base keywords shared by all Propositions articles */
|
|
15
16
|
const PROPOSITIONS_BASE_KEYWORDS = [
|
|
@@ -198,6 +199,7 @@ export class PropositionsStrategy {
|
|
|
198
199
|
pipelineData,
|
|
199
200
|
procedureHtml,
|
|
200
201
|
feedData: feedResult,
|
|
202
|
+
analysisContext: loadAnalysisContext(date, 'propositions'),
|
|
201
203
|
};
|
|
202
204
|
}
|
|
203
205
|
/**
|
|
@@ -218,7 +220,19 @@ export class PropositionsStrategy {
|
|
|
218
220
|
const swotSection = buildSwotSection(swotData, lang);
|
|
219
221
|
const dashboardData = buildPropositionsDashboard(data.pipelineData, lang);
|
|
220
222
|
const dashboardSection = buildDashboardSection(dashboardData, lang);
|
|
221
|
-
const
|
|
223
|
+
const analysisInsights = buildAnalysisInsightsSection(data.analysisContext, [
|
|
224
|
+
'deep-analysis',
|
|
225
|
+
'synthesis-summary',
|
|
226
|
+
'stakeholder-analysis',
|
|
227
|
+
'coalition-analysis',
|
|
228
|
+
'cross-session-intelligence',
|
|
229
|
+
'risk-matrix',
|
|
230
|
+
'significance-classification',
|
|
231
|
+
'legislative-velocity-risk',
|
|
232
|
+
'significance-scoring',
|
|
233
|
+
'forces-analysis',
|
|
234
|
+
], lang);
|
|
235
|
+
const injection = deepSection + mindmapSection + swotSection + dashboardSection + analysisInsights;
|
|
222
236
|
// Inject before the closing </div> of .article-content
|
|
223
237
|
if (injection) {
|
|
224
238
|
const closingTag = '</div>';
|
|
@@ -9,6 +9,7 @@ import { buildProspectiveAnalysis, buildProspectiveSwot, buildProspectiveDashboa
|
|
|
9
9
|
import { buildSwotSection } from '../swot-content.js';
|
|
10
10
|
import { buildDashboardSection } from '../dashboard-content.js';
|
|
11
11
|
import { buildIntelligenceMindmapSection } from '../mindmap-content.js';
|
|
12
|
+
import { loadAnalysisContext, buildAnalysisInsightsSection } from './article-strategy.js';
|
|
12
13
|
import { pl } from '../../utils/metadata-utils.js';
|
|
13
14
|
// ─── Date-range helper ────────────────────────────────────────────────────────
|
|
14
15
|
/**
|
|
@@ -120,6 +121,7 @@ export class WeekAheadStrategy {
|
|
|
120
121
|
weekData,
|
|
121
122
|
keywords,
|
|
122
123
|
feedData,
|
|
124
|
+
analysisContext: loadAnalysisContext(date, 'week-ahead'),
|
|
123
125
|
};
|
|
124
126
|
}
|
|
125
127
|
/**
|
|
@@ -140,10 +142,26 @@ export class WeekAheadStrategy {
|
|
|
140
142
|
const swotSection = buildSwotSection(swotData, lang);
|
|
141
143
|
const dashboardData = buildProspectiveDashboard(data.weekData, 'week', lang);
|
|
142
144
|
const dashboardSection = buildDashboardSection(dashboardData, lang);
|
|
145
|
+
const analysisInsights = buildAnalysisInsightsSection(data.analysisContext, [
|
|
146
|
+
'deep-analysis',
|
|
147
|
+
'synthesis-summary',
|
|
148
|
+
'stakeholder-analysis',
|
|
149
|
+
'coalition-analysis',
|
|
150
|
+
'cross-session-intelligence',
|
|
151
|
+
'significance-classification',
|
|
152
|
+
'political-threat-landscape',
|
|
153
|
+
'risk-matrix',
|
|
154
|
+
'forces-analysis',
|
|
155
|
+
], lang);
|
|
143
156
|
// Inject at the explicit <!-- /article-content --> marker position so the
|
|
144
157
|
// section stays inside the .article-content styling scope. The marker is
|
|
145
158
|
// removed from the final HTML output to avoid unnecessary bytes.
|
|
146
|
-
const injection = (watchSection || '') +
|
|
159
|
+
const injection = (watchSection || '') +
|
|
160
|
+
analysisSection +
|
|
161
|
+
mindmapSection +
|
|
162
|
+
swotSection +
|
|
163
|
+
dashboardSection +
|
|
164
|
+
analysisInsights;
|
|
147
165
|
if (injection) {
|
|
148
166
|
return base.replace('<!-- /article-content -->', injection);
|
|
149
167
|
}
|
|
@@ -9,6 +9,7 @@ import { buildVotingAnalysis, buildVotingSwot, buildVotingDashboard, buildVoting
|
|
|
9
9
|
import { buildSwotSection } from '../swot-content.js';
|
|
10
10
|
import { buildDashboardSection } from '../dashboard-content.js';
|
|
11
11
|
import { buildIntelligenceMindmapSection } from '../mindmap-content.js';
|
|
12
|
+
import { loadAnalysisContext, buildAnalysisInsightsSection } from './article-strategy.js';
|
|
12
13
|
import { pl } from '../../utils/metadata-utils.js';
|
|
13
14
|
import { isPlaceholderText } from '../../constants/analysis-constants.js';
|
|
14
15
|
/** Base keywords shared by all Weekly Review articles */
|
|
@@ -163,6 +164,7 @@ export class WeeklyReviewStrategy {
|
|
|
163
164
|
anomalies,
|
|
164
165
|
questions,
|
|
165
166
|
feedData,
|
|
167
|
+
analysisContext: loadAnalysisContext(date, 'week-in-review'),
|
|
166
168
|
};
|
|
167
169
|
}
|
|
168
170
|
/**
|
|
@@ -186,7 +188,21 @@ export class WeeklyReviewStrategy {
|
|
|
186
188
|
const swotSection = buildSwotSection(swotData, lang);
|
|
187
189
|
const dashboardData = buildVotingDashboard(data.votingRecords, data.votingPatterns, data.anomalies, lang);
|
|
188
190
|
const dashboardSection = buildDashboardSection(dashboardData, lang);
|
|
189
|
-
|
|
191
|
+
const analysisInsights = buildAnalysisInsightsSection(data.analysisContext, [
|
|
192
|
+
'deep-analysis',
|
|
193
|
+
'synthesis-summary',
|
|
194
|
+
'stakeholder-analysis',
|
|
195
|
+
'coalition-analysis',
|
|
196
|
+
'cross-session-intelligence',
|
|
197
|
+
'voting-patterns',
|
|
198
|
+
'significance-classification',
|
|
199
|
+
], lang);
|
|
200
|
+
return base.replace('<!-- /article-content -->', adoptedTextsHtml +
|
|
201
|
+
deepSection +
|
|
202
|
+
mindmapSection +
|
|
203
|
+
swotSection +
|
|
204
|
+
dashboardSection +
|
|
205
|
+
analysisInsights);
|
|
190
206
|
}
|
|
191
207
|
/**
|
|
192
208
|
* Return language-specific metadata for the weekly review article.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { ConfidenceLevel } from '../types/analysis.js';
|
|
2
|
+
import type { SynthesisFinding, AggregatedSWOT, RiskOverview, SynthesisSummary } from '../types/significance.js';
|
|
3
|
+
/** Parsed YAML frontmatter fields relevant to synthesis */
|
|
4
|
+
interface ParsedFrontmatter {
|
|
5
|
+
readonly method: string;
|
|
6
|
+
readonly confidence: ConfidenceLevel;
|
|
7
|
+
readonly date: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Parse YAML frontmatter from a markdown file's content.
|
|
11
|
+
*
|
|
12
|
+
* Extracts `method`, `confidence`, and `date` fields from the `---` delimited
|
|
13
|
+
* YAML block at the start of the file. Returns null when no valid frontmatter
|
|
14
|
+
* is found.
|
|
15
|
+
*
|
|
16
|
+
* @param content - Raw markdown content
|
|
17
|
+
* @returns Parsed frontmatter or null
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseFrontmatter(content: string): ParsedFrontmatter | null;
|
|
20
|
+
/**
|
|
21
|
+
* Aggregate SWOT mention counts from a body of text.
|
|
22
|
+
*
|
|
23
|
+
* @param text - Combined analysis text
|
|
24
|
+
* @returns SWOT counts
|
|
25
|
+
*/
|
|
26
|
+
export declare function aggregateSWOT(text: string): AggregatedSWOT;
|
|
27
|
+
/**
|
|
28
|
+
* Aggregate risk-level mention counts from a body of text.
|
|
29
|
+
*
|
|
30
|
+
* @param text - Combined analysis text
|
|
31
|
+
* @returns Risk level counts
|
|
32
|
+
*/
|
|
33
|
+
export declare function aggregateRisks(text: string): RiskOverview;
|
|
34
|
+
/**
|
|
35
|
+
* Extract the first non-empty non-frontmatter heading or paragraph as a
|
|
36
|
+
* one-line summary from a markdown file.
|
|
37
|
+
*
|
|
38
|
+
* @param content - Raw markdown content
|
|
39
|
+
* @returns One-line summary string
|
|
40
|
+
*/
|
|
41
|
+
export declare function extractSummaryLine(content: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Determine the overall confidence level from a set of findings.
|
|
44
|
+
*
|
|
45
|
+
* Uses majority vote: whichever confidence level appears most often wins.
|
|
46
|
+
*
|
|
47
|
+
* @param findings - Findings with individual confidence levels
|
|
48
|
+
* @returns Aggregated confidence level
|
|
49
|
+
*/
|
|
50
|
+
export declare function aggregateConfidence(findings: readonly SynthesisFinding[]): ConfidenceLevel;
|
|
51
|
+
/**
|
|
52
|
+
* Recursively find all `.md` analysis files under a directory.
|
|
53
|
+
*
|
|
54
|
+
* Excludes:
|
|
55
|
+
* - The synthesis output file itself (prevents self-contamination on re-runs)
|
|
56
|
+
* - The `documents/` subdirectory (per-document analysis can bloat I/O and skew aggregation)
|
|
57
|
+
*
|
|
58
|
+
* @param dir - Absolute directory path
|
|
59
|
+
* @returns Array of absolute file paths
|
|
60
|
+
*/
|
|
61
|
+
export declare function findMarkdownFiles(dir: string): readonly string[];
|
|
62
|
+
/**
|
|
63
|
+
* Generate editorial recommendations based on aggregated analysis data.
|
|
64
|
+
*
|
|
65
|
+
* @param findings - Ranked findings
|
|
66
|
+
* @param swot - Aggregated SWOT counts
|
|
67
|
+
* @param risks - Risk level distribution
|
|
68
|
+
* @returns Array of recommendation strings
|
|
69
|
+
*/
|
|
70
|
+
export declare function generateEditorialRecommendations(findings: readonly SynthesisFinding[], swot: AggregatedSWOT, risks: RiskOverview): readonly string[];
|
|
71
|
+
/**
|
|
72
|
+
* Build a synthesis summary from all analysis files in a date directory.
|
|
73
|
+
*
|
|
74
|
+
* Scans the directory recursively for `.md` analysis files, parses their
|
|
75
|
+
* frontmatter, extracts findings, aggregates SWOT and risk mentions, and
|
|
76
|
+
* produces a {@link SynthesisSummary} object.
|
|
77
|
+
*
|
|
78
|
+
* @param dateOutputDir - Absolute path to the date-scoped analysis directory
|
|
79
|
+
* @param date - ISO date string (YYYY-MM-DD)
|
|
80
|
+
* @returns Synthesis summary object
|
|
81
|
+
*/
|
|
82
|
+
export declare function buildSynthesisSummary(dateOutputDir: string, date: string): SynthesisSummary;
|
|
83
|
+
/**
|
|
84
|
+
* Generate a markdown report from a synthesis summary.
|
|
85
|
+
*
|
|
86
|
+
* Follows the template format defined in `analysis/templates/synthesis-summary.md`.
|
|
87
|
+
*
|
|
88
|
+
* @param summary - Computed synthesis summary
|
|
89
|
+
* @returns Markdown string
|
|
90
|
+
*/
|
|
91
|
+
export declare function formatSynthesisMarkdown(summary: SynthesisSummary): string;
|
|
92
|
+
export {};
|
|
93
|
+
//# sourceMappingURL=synthesis-summary.d.ts.map
|