euparliamentmonitor 0.8.40 โ†’ 0.8.42

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 (31) hide show
  1. package/README.md +23 -5
  2. package/package.json +6 -5
  3. package/scripts/constants/language-ui.d.ts +2 -0
  4. package/scripts/constants/language-ui.js +17 -0
  5. package/scripts/constants/languages.d.ts +1 -1
  6. package/scripts/constants/languages.js +1 -1
  7. package/scripts/generators/news-enhanced.js +81 -40
  8. package/scripts/generators/pipeline/analysis-stage.d.ts +89 -0
  9. package/scripts/generators/pipeline/analysis-stage.js +259 -30
  10. package/scripts/generators/pipeline/fetch-stage.d.ts +1 -1
  11. package/scripts/generators/pipeline/fetch-stage.js +6 -6
  12. package/scripts/generators/political-intelligence-descriptions.d.ts +139 -0
  13. package/scripts/generators/political-intelligence-descriptions.js +2068 -0
  14. package/scripts/generators/political-intelligence.d.ts +118 -0
  15. package/scripts/generators/political-intelligence.js +1146 -0
  16. package/scripts/generators/sitemap.d.ts +13 -0
  17. package/scripts/generators/sitemap.js +616 -127
  18. package/scripts/index.d.ts +1 -1
  19. package/scripts/index.js +1 -1
  20. package/scripts/lint-prompts.js +218 -0
  21. package/scripts/mcp/ep-mcp-client.d.ts +20 -8
  22. package/scripts/mcp/ep-mcp-client.js +88 -13
  23. package/scripts/templates/section-builders.js +5 -3
  24. package/scripts/types/mcp.d.ts +14 -14
  25. package/scripts/utils/content-validator.d.ts +19 -3
  26. package/scripts/utils/content-validator.js +19 -3
  27. package/scripts/utils/file-utils.d.ts +54 -0
  28. package/scripts/utils/file-utils.js +86 -0
  29. package/scripts/utils/fix-articles.js +7 -57
  30. package/scripts/utils/world-bank-data.d.ts +67 -2
  31. package/scripts/utils/world-bank-data.js +105 -2
package/README.md CHANGED
@@ -115,6 +115,13 @@ import {
115
115
  - [๐Ÿ“” API Documentation](https://euparliamentmonitor.com/docs/api/index.html) - TypeDoc-generated API reference
116
116
  - [๐Ÿ““ Test Coverage](https://euparliamentmonitor.com/docs/coverage/index.html) - Interactive coverage report
117
117
 
118
+ **๐Ÿค– Agentic Pipeline:**
119
+ - [Agent Catalog](.github/agents/README.md) โ€” custom Copilot agents (analysis producers / consumers / gh-aw infrastructure)
120
+ - [Skills Library](.github/skills/README.md) โ€” shared skills (security, compliance, intelligence, gh-aw)
121
+ - [Prompt Library](.github/prompts/README.md) โ€” 10-file bounded-context prompt set (`00`โ†’`09`) + `npm run lint:prompts` drift-guard
122
+ - [Workflows](.github/workflows/README.md) + [WORKFLOWS.md](WORKFLOWS.md) โ€” 10 `news-*.md` agentic workflows + CI workflows
123
+ - [Analysis Chain](analysis/README.md) โ€” 5-stage pipeline (Data โ†’ Analysis โ†’ Completeness Gate โ†’ Article โ†’ Single PR), methodologies, 39 templates, quality thresholds
124
+
118
125
  **๐Ÿ”’ ISMS Compliance:**
119
126
  - [๐Ÿ›ก๏ธ Hack23 ISMS Framework](https://github.com/Hack23/ISMS-PUBLIC) - Information Security Management System
120
127
  - [๐Ÿ” Secure Development Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Secure_Development_Policy.md) - Development standards
@@ -124,12 +131,17 @@ import {
124
131
 
125
132
  **MCP Server Integration**: The project uses the
126
133
  [European-Parliament-MCP-Server](https://github.com/Hack23/European-Parliament-MCP-Server)
127
- v1.2.10 for accessing real EU Parliament data via the Model Context Protocol.
134
+ v1.2.13 for accessing real EU Parliament data via the Model Context Protocol.
128
135
 
129
136
  - **MCP Server Status**: โœ… Fully operational โ€” 60+ EP data tools available
130
137
  (feeds, direct lookups, analytical tools, intelligence correlation)
131
- - **Agentic Workflows**: 10 gh-aw markdown workflows for automated news
132
- generation with AI-driven political intelligence analysis
138
+ - **Agentic Workflows**: 18 gh-aw markdown workflows โ€” 8 split-pair article types (`news-<type>-analysis.md` + `news-<type>-article.md`) + `news-article-generator.md` + `news-translate.md` (compiled with
139
+ `gh-aw v0.69.3`) for automated news generation with AI-driven political
140
+ intelligence analysis. See [`.github/workflows/README.md`](.github/workflows/README.md).
141
+ - **Analysis Chain**: 5-stage pipeline (Data โ†’ Analysis โ†’ Completeness Gate โ†’
142
+ Article โ†’ Single PR) producing 39 structured analysis templates per run.
143
+ See [`analysis/README.md`](analysis/README.md) and
144
+ [`analysis/methodologies/ai-driven-analysis-guide.md`](analysis/methodologies/ai-driven-analysis-guide.md).
133
145
  - **Fallback Mode**: News generation can work with reduced data when EP API
134
146
  endpoints are temporarily unavailable
135
147
  - **Environment Variable**: Set `USE_EP_MCP=false` to disable MCP client
@@ -467,7 +479,9 @@ npm run generate-news -- --types=week-ahead --languages=all
467
479
  # Generate language-specific index pages
468
480
  npm run generate-news-indexes
469
481
 
470
- # Generate sitemap.xml
482
+ # Generate sitemap.xml, sitemap_<lang>.html, and political-intelligence_<lang>.html
483
+ # (14 language-specific sitemap pages + 14 language-specific Political Intelligence
484
+ # pages that index every methodology, template, and daily analysis run)
471
485
  npm run generate-sitemap
472
486
  ```
473
487
 
@@ -511,7 +525,11 @@ euparliamentmonitor/
511
525
  โ”œโ”€โ”€ index-{lang}.html # Language-specific index pages
512
526
  โ”œโ”€โ”€ typedoc.json # TypeDoc configuration
513
527
  โ”œโ”€โ”€ tsconfig.json # TypeScript configuration
514
- โ”œโ”€โ”€ sitemap.xml # SEO sitemap
528
+ โ”œโ”€โ”€ sitemap.xml # SEO sitemap with hreflang alternates
529
+ โ”œโ”€โ”€ sitemap.html # Human-readable sitemap (English)
530
+ โ”œโ”€โ”€ sitemap_{lang}.html # Per-language human-readable sitemaps
531
+ โ”œโ”€โ”€ political-intelligence.html # Index of every methodology + template + daily analysis run
532
+ โ”œโ”€โ”€ political-intelligence_{lang}.html # Localized political-intelligence pages
515
533
  โ””โ”€โ”€ package.json # Project dependencies
516
534
  ```
517
535
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "euparliamentmonitor",
3
- "version": "0.8.40",
3
+ "version": "0.8.42",
4
4
  "type": "module",
5
5
  "description": "European Parliament Intelligence Platform - Monitor political activity with systematic transparency",
6
6
  "main": "scripts/index.js",
@@ -70,6 +70,7 @@
70
70
  "validate-articles:strict": "npx tsx src/utils/validate-articles.ts --strict",
71
71
  "validate-analysis": "npx tsx src/utils/validate-analysis-completeness.ts",
72
72
  "validate-ep-api": "npx tsx src/utils/validate-ep-api.ts",
73
+ "lint:prompts": "node scripts/lint-prompts.js",
73
74
  "htmlhint": "sh -c 'htmlhint *.html; set -- news/*.html; if [ -e \"$1\" ]; then htmlhint \"$@\"; else echo \"No news/*.html files to lint\"; fi'",
74
75
  "serve": "python3 -m http.server 8080",
75
76
  "test": "vitest run",
@@ -144,8 +145,8 @@
144
145
  "@types/papaparse": "5.5.2",
145
146
  "@typescript-eslint/eslint-plugin": "8.59.0",
146
147
  "@typescript-eslint/parser": "8.59.0",
147
- "@vitest/coverage-v8": "4.1.4",
148
- "@vitest/ui": "4.1.4",
148
+ "@vitest/coverage-v8": "4.1.5",
149
+ "@vitest/ui": "4.1.5",
149
150
  "chart.js": "4.5.1",
150
151
  "chartjs-plugin-annotation": "3.1.0",
151
152
  "d3": "7.9.0",
@@ -165,13 +166,13 @@
165
166
  "tsx": "4.21.0",
166
167
  "typedoc": "0.28.19",
167
168
  "typescript": "6.0.3",
168
- "vitest": "4.1.4"
169
+ "vitest": "4.1.5"
169
170
  },
170
171
  "engines": {
171
172
  "node": ">=25"
172
173
  },
173
174
  "dependencies": {
174
- "european-parliament-mcp-server": "1.2.10"
175
+ "european-parliament-mcp-server": "1.2.13"
175
176
  },
176
177
  "optionalDependencies": {
177
178
  "worldbank-mcp": "1.0.1"
@@ -191,4 +191,6 @@ export declare const FOOTER_DISCLAIMER_LABELS: LanguageMap;
191
191
  export declare const FOOTER_REPORT_ISSUES_LABELS: LanguageMap;
192
192
  /** Localized "{count} articles available" stats text used in footer About section */
193
193
  export declare const FOOTER_ARTICLES_AVAILABLE_LABELS: LanguageMap;
194
+ /** Localized "Political Intelligence" link label used in footer Quick Links section */
195
+ export declare const FOOTER_POLITICAL_INTELLIGENCE_LABELS: LanguageMap;
194
196
  //# sourceMappingURL=language-ui.d.ts.map
@@ -1849,4 +1849,21 @@ export const FOOTER_ARTICLES_AVAILABLE_LABELS = {
1849
1849
  ko: '{count}๊ฐœ ๊ธฐ์‚ฌ ์ด์šฉ ๊ฐ€๋Šฅ',
1850
1850
  zh: '{count}็ฏ‡ๆ–‡็ซ ๅฏ็”จ',
1851
1851
  };
1852
+ /** Localized "Political Intelligence" link label used in footer Quick Links section */
1853
+ export const FOOTER_POLITICAL_INTELLIGENCE_LABELS = {
1854
+ en: 'Political Intelligence',
1855
+ sv: 'Politisk underrรคttelse',
1856
+ da: 'Politisk efterretning',
1857
+ no: 'Politisk etterretning',
1858
+ fi: 'Poliittinen tiedustelu',
1859
+ de: 'Politische Aufklรคrung',
1860
+ fr: 'Intelligence politique',
1861
+ es: 'Inteligencia polรญtica',
1862
+ nl: 'Politieke intelligentie',
1863
+ ar: 'ุงู„ุงุณุชุฎุจุงุฑุงุช ุงู„ุณูŠุงุณูŠุฉ',
1864
+ he: 'ืžื•ื“ื™ืขื™ืŸ ืคื•ืœื™ื˜ื™',
1865
+ ja: 'ๆ”ฟๆฒปใ‚คใƒณใƒ†ใƒชใ‚ธใ‚งใƒณใ‚น',
1866
+ ko: '์ •์น˜ ์ •๋ณด',
1867
+ zh: 'ๆ”ฟๆฒปๆƒ…ๆŠฅ',
1868
+ };
1852
1869
  //# sourceMappingURL=language-ui.js.map
@@ -8,7 +8,7 @@
8
8
  * - **language-articles** โ€” Article-type title generators and body-text strings
9
9
  */
10
10
  export { ALL_LANGUAGES, LANGUAGE_PRESETS, LANGUAGE_FLAGS, LANGUAGE_NAMES, getLocalizedString, isSupportedLanguage, getTextDirection, } from './language-core.js';
11
- export { PAGE_TITLES, PAGE_DESCRIPTIONS, SECTION_HEADINGS, NO_ARTICLES_MESSAGES, SKIP_LINK_TEXTS, ARTICLE_TYPE_LABELS, READ_TIME_LABELS, BACK_TO_NEWS_LABELS, ARTICLE_NAV_LABELS, RELATED_ARTICLES_NAV_LABELS, BREADCRUMB_HOME_LABELS, BREADCRUMB_NEWS_LABELS, TIMELINE_HEADINGS, COMPARISON_BEFORE_LABELS, COMPARISON_AFTER_LABELS, KEY_FIGURES_HEADINGS, AI_SECTION_CONTENT, FILTER_LABELS, SOURCES_HEADING_LABELS, HEADER_SUBTITLE_LABELS, THEME_TOGGLE_LABELS, FOOTER_ABOUT_HEADING_LABELS, FOOTER_ABOUT_TEXT_LABELS, FOOTER_QUICK_LINKS_LABELS, FOOTER_BUILT_BY_LABELS, FOOTER_LANGUAGES_LABELS, FOOTER_HOME_LABELS, FOOTER_SITEMAP_LABELS, FOOTER_RSS_LABELS, FOOTER_GITHUB_REPO_LABELS, FOOTER_LICENSE_LABELS, FOOTER_EUROPARL_LABELS, FOOTER_LINKEDIN_LABELS, FOOTER_SECURITY_POLICY_LABELS, FOOTER_CONTACT_LABELS, FOOTER_DISCLAIMER_LABELS, FOOTER_REPORT_ISSUES_LABELS, FOOTER_ARTICLES_AVAILABLE_LABELS, TOC_ARIA_LABELS, RELATED_ANALYSIS_LABELS, ANALYSIS_TRANSPARENCY_LABELS, ANALYSIS_SUMMARY_LABELS, METHODOLOGY_LABELS, TRANSPARENCY_DISCLOSURE_LABELS, CLASSIFICATION_ANALYSIS_LABELS, THREAT_ASSESSMENT_LABELS, RISK_SCORING_LABELS, DEEP_ANALYSIS_LABELS, VIEW_SOURCE_LABELS, OPEN_SOURCE_NOTE_LABELS, AI_ANALYSIS_GUIDE_LABELS, SWOT_FRAMEWORK_LABELS, RISK_METHODOLOGY_LABELS, THREAT_FRAMEWORK_LABELS, CLASSIFICATION_GUIDE_LABELS, STYLE_GUIDE_LABELS, SIGNIFICANCE_CLASSIFICATION_LABELS, ACTOR_MAPPING_LABELS, FORCES_ANALYSIS_LABELS, IMPACT_MATRIX_LABELS, POLITICAL_THREAT_LANDSCAPE_LABELS, ACTOR_THREAT_PROFILING_LABELS, CONSEQUENCE_TREES_LABELS, LEGISLATIVE_DISRUPTION_LABELS, RISK_MATRIX_LABELS, QUANTITATIVE_SWOT_LABELS, POLITICAL_CAPITAL_RISK_LABELS, LEGISLATIVE_VELOCITY_RISK_LABELS, AGENT_RISK_WORKFLOW_LABELS, STAKEHOLDER_IMPACT_LABELS, COALITION_DYNAMICS_LABELS, VOTING_PATTERNS_LABELS, CROSS_SESSION_INTELLIGENCE_LABELS, SYNTHESIS_SUMMARY_LABELS, DOCUMENT_ANALYSIS_LABELS, SIGNIFICANCE_SCORING_LABELS, } from './language-ui.js';
11
+ export { PAGE_TITLES, PAGE_DESCRIPTIONS, SECTION_HEADINGS, NO_ARTICLES_MESSAGES, SKIP_LINK_TEXTS, ARTICLE_TYPE_LABELS, READ_TIME_LABELS, BACK_TO_NEWS_LABELS, ARTICLE_NAV_LABELS, RELATED_ARTICLES_NAV_LABELS, BREADCRUMB_HOME_LABELS, BREADCRUMB_NEWS_LABELS, TIMELINE_HEADINGS, COMPARISON_BEFORE_LABELS, COMPARISON_AFTER_LABELS, KEY_FIGURES_HEADINGS, AI_SECTION_CONTENT, FILTER_LABELS, SOURCES_HEADING_LABELS, HEADER_SUBTITLE_LABELS, THEME_TOGGLE_LABELS, FOOTER_ABOUT_HEADING_LABELS, FOOTER_ABOUT_TEXT_LABELS, FOOTER_QUICK_LINKS_LABELS, FOOTER_BUILT_BY_LABELS, FOOTER_LANGUAGES_LABELS, FOOTER_HOME_LABELS, FOOTER_SITEMAP_LABELS, FOOTER_RSS_LABELS, FOOTER_GITHUB_REPO_LABELS, FOOTER_LICENSE_LABELS, FOOTER_EUROPARL_LABELS, FOOTER_LINKEDIN_LABELS, FOOTER_SECURITY_POLICY_LABELS, FOOTER_CONTACT_LABELS, FOOTER_DISCLAIMER_LABELS, FOOTER_REPORT_ISSUES_LABELS, FOOTER_ARTICLES_AVAILABLE_LABELS, FOOTER_POLITICAL_INTELLIGENCE_LABELS, TOC_ARIA_LABELS, RELATED_ANALYSIS_LABELS, ANALYSIS_TRANSPARENCY_LABELS, ANALYSIS_SUMMARY_LABELS, METHODOLOGY_LABELS, TRANSPARENCY_DISCLOSURE_LABELS, CLASSIFICATION_ANALYSIS_LABELS, THREAT_ASSESSMENT_LABELS, RISK_SCORING_LABELS, DEEP_ANALYSIS_LABELS, VIEW_SOURCE_LABELS, OPEN_SOURCE_NOTE_LABELS, AI_ANALYSIS_GUIDE_LABELS, SWOT_FRAMEWORK_LABELS, RISK_METHODOLOGY_LABELS, THREAT_FRAMEWORK_LABELS, CLASSIFICATION_GUIDE_LABELS, STYLE_GUIDE_LABELS, SIGNIFICANCE_CLASSIFICATION_LABELS, ACTOR_MAPPING_LABELS, FORCES_ANALYSIS_LABELS, IMPACT_MATRIX_LABELS, POLITICAL_THREAT_LANDSCAPE_LABELS, ACTOR_THREAT_PROFILING_LABELS, CONSEQUENCE_TREES_LABELS, LEGISLATIVE_DISRUPTION_LABELS, RISK_MATRIX_LABELS, QUANTITATIVE_SWOT_LABELS, POLITICAL_CAPITAL_RISK_LABELS, LEGISLATIVE_VELOCITY_RISK_LABELS, AGENT_RISK_WORKFLOW_LABELS, STAKEHOLDER_IMPACT_LABELS, COALITION_DYNAMICS_LABELS, VOTING_PATTERNS_LABELS, CROSS_SESSION_INTELLIGENCE_LABELS, SYNTHESIS_SUMMARY_LABELS, DOCUMENT_ANALYSIS_LABELS, SIGNIFICANCE_SCORING_LABELS, } from './language-ui.js';
12
12
  export type { AISection, RelationshipLabels, RelatedAnalysisStrings } from './language-ui.js';
13
13
  export { WEEK_AHEAD_TITLES, MONTH_AHEAD_TITLES, WEEKLY_REVIEW_TITLES, MONTHLY_REVIEW_TITLES, MOTIONS_TITLES, BREAKING_NEWS_TITLES, COMMITTEE_REPORTS_TITLES, PROPOSITIONS_TITLES, PROPOSITIONS_STRINGS, EDITORIAL_STRINGS, MOTIONS_STRINGS, WEEK_AHEAD_STRINGS, WEEK_AHEAD_STAKEHOLDER_STRINGS, BREAKING_STRINGS, DEEP_ANALYSIS_STRINGS, COMMITTEE_ANALYSIS_CONTENT_STRINGS, SWOT_STRINGS, DASHBOARD_STRINGS, SWOT_BUILDER_STRINGS, DASHBOARD_BUILDER_STRINGS, LOCALIZED_KEYWORDS, MONTH_IN_REVIEW_STRINGS, ANALYSIS_QUALITY_LABELS, ANALYSIS_INSIGHTS_HEADING, } from './language-articles.js';
14
14
  //# sourceMappingURL=languages.d.ts.map
@@ -10,6 +10,6 @@
10
10
  * - **language-articles** โ€” Article-type title generators and body-text strings
11
11
  */
12
12
  export { ALL_LANGUAGES, LANGUAGE_PRESETS, LANGUAGE_FLAGS, LANGUAGE_NAMES, getLocalizedString, isSupportedLanguage, getTextDirection, } from './language-core.js';
13
- export { PAGE_TITLES, PAGE_DESCRIPTIONS, SECTION_HEADINGS, NO_ARTICLES_MESSAGES, SKIP_LINK_TEXTS, ARTICLE_TYPE_LABELS, READ_TIME_LABELS, BACK_TO_NEWS_LABELS, ARTICLE_NAV_LABELS, RELATED_ARTICLES_NAV_LABELS, BREADCRUMB_HOME_LABELS, BREADCRUMB_NEWS_LABELS, TIMELINE_HEADINGS, COMPARISON_BEFORE_LABELS, COMPARISON_AFTER_LABELS, KEY_FIGURES_HEADINGS, AI_SECTION_CONTENT, FILTER_LABELS, SOURCES_HEADING_LABELS, HEADER_SUBTITLE_LABELS, THEME_TOGGLE_LABELS, FOOTER_ABOUT_HEADING_LABELS, FOOTER_ABOUT_TEXT_LABELS, FOOTER_QUICK_LINKS_LABELS, FOOTER_BUILT_BY_LABELS, FOOTER_LANGUAGES_LABELS, FOOTER_HOME_LABELS, FOOTER_SITEMAP_LABELS, FOOTER_RSS_LABELS, FOOTER_GITHUB_REPO_LABELS, FOOTER_LICENSE_LABELS, FOOTER_EUROPARL_LABELS, FOOTER_LINKEDIN_LABELS, FOOTER_SECURITY_POLICY_LABELS, FOOTER_CONTACT_LABELS, FOOTER_DISCLAIMER_LABELS, FOOTER_REPORT_ISSUES_LABELS, FOOTER_ARTICLES_AVAILABLE_LABELS, TOC_ARIA_LABELS, RELATED_ANALYSIS_LABELS, ANALYSIS_TRANSPARENCY_LABELS, ANALYSIS_SUMMARY_LABELS, METHODOLOGY_LABELS, TRANSPARENCY_DISCLOSURE_LABELS, CLASSIFICATION_ANALYSIS_LABELS, THREAT_ASSESSMENT_LABELS, RISK_SCORING_LABELS, DEEP_ANALYSIS_LABELS, VIEW_SOURCE_LABELS, OPEN_SOURCE_NOTE_LABELS, AI_ANALYSIS_GUIDE_LABELS, SWOT_FRAMEWORK_LABELS, RISK_METHODOLOGY_LABELS, THREAT_FRAMEWORK_LABELS, CLASSIFICATION_GUIDE_LABELS, STYLE_GUIDE_LABELS, SIGNIFICANCE_CLASSIFICATION_LABELS, ACTOR_MAPPING_LABELS, FORCES_ANALYSIS_LABELS, IMPACT_MATRIX_LABELS, POLITICAL_THREAT_LANDSCAPE_LABELS, ACTOR_THREAT_PROFILING_LABELS, CONSEQUENCE_TREES_LABELS, LEGISLATIVE_DISRUPTION_LABELS, RISK_MATRIX_LABELS, QUANTITATIVE_SWOT_LABELS, POLITICAL_CAPITAL_RISK_LABELS, LEGISLATIVE_VELOCITY_RISK_LABELS, AGENT_RISK_WORKFLOW_LABELS, STAKEHOLDER_IMPACT_LABELS, COALITION_DYNAMICS_LABELS, VOTING_PATTERNS_LABELS, CROSS_SESSION_INTELLIGENCE_LABELS, SYNTHESIS_SUMMARY_LABELS, DOCUMENT_ANALYSIS_LABELS, SIGNIFICANCE_SCORING_LABELS, } from './language-ui.js';
13
+ export { PAGE_TITLES, PAGE_DESCRIPTIONS, SECTION_HEADINGS, NO_ARTICLES_MESSAGES, SKIP_LINK_TEXTS, ARTICLE_TYPE_LABELS, READ_TIME_LABELS, BACK_TO_NEWS_LABELS, ARTICLE_NAV_LABELS, RELATED_ARTICLES_NAV_LABELS, BREADCRUMB_HOME_LABELS, BREADCRUMB_NEWS_LABELS, TIMELINE_HEADINGS, COMPARISON_BEFORE_LABELS, COMPARISON_AFTER_LABELS, KEY_FIGURES_HEADINGS, AI_SECTION_CONTENT, FILTER_LABELS, SOURCES_HEADING_LABELS, HEADER_SUBTITLE_LABELS, THEME_TOGGLE_LABELS, FOOTER_ABOUT_HEADING_LABELS, FOOTER_ABOUT_TEXT_LABELS, FOOTER_QUICK_LINKS_LABELS, FOOTER_BUILT_BY_LABELS, FOOTER_LANGUAGES_LABELS, FOOTER_HOME_LABELS, FOOTER_SITEMAP_LABELS, FOOTER_RSS_LABELS, FOOTER_GITHUB_REPO_LABELS, FOOTER_LICENSE_LABELS, FOOTER_EUROPARL_LABELS, FOOTER_LINKEDIN_LABELS, FOOTER_SECURITY_POLICY_LABELS, FOOTER_CONTACT_LABELS, FOOTER_DISCLAIMER_LABELS, FOOTER_REPORT_ISSUES_LABELS, FOOTER_ARTICLES_AVAILABLE_LABELS, FOOTER_POLITICAL_INTELLIGENCE_LABELS, TOC_ARIA_LABELS, RELATED_ANALYSIS_LABELS, ANALYSIS_TRANSPARENCY_LABELS, ANALYSIS_SUMMARY_LABELS, METHODOLOGY_LABELS, TRANSPARENCY_DISCLOSURE_LABELS, CLASSIFICATION_ANALYSIS_LABELS, THREAT_ASSESSMENT_LABELS, RISK_SCORING_LABELS, DEEP_ANALYSIS_LABELS, VIEW_SOURCE_LABELS, OPEN_SOURCE_NOTE_LABELS, AI_ANALYSIS_GUIDE_LABELS, SWOT_FRAMEWORK_LABELS, RISK_METHODOLOGY_LABELS, THREAT_FRAMEWORK_LABELS, CLASSIFICATION_GUIDE_LABELS, STYLE_GUIDE_LABELS, SIGNIFICANCE_CLASSIFICATION_LABELS, ACTOR_MAPPING_LABELS, FORCES_ANALYSIS_LABELS, IMPACT_MATRIX_LABELS, POLITICAL_THREAT_LANDSCAPE_LABELS, ACTOR_THREAT_PROFILING_LABELS, CONSEQUENCE_TREES_LABELS, LEGISLATIVE_DISRUPTION_LABELS, RISK_MATRIX_LABELS, QUANTITATIVE_SWOT_LABELS, POLITICAL_CAPITAL_RISK_LABELS, LEGISLATIVE_VELOCITY_RISK_LABELS, AGENT_RISK_WORKFLOW_LABELS, STAKEHOLDER_IMPACT_LABELS, COALITION_DYNAMICS_LABELS, VOTING_PATTERNS_LABELS, CROSS_SESSION_INTELLIGENCE_LABELS, SYNTHESIS_SUMMARY_LABELS, DOCUMENT_ANALYSIS_LABELS, SIGNIFICANCE_SCORING_LABELS, } from './language-ui.js';
14
14
  export { WEEK_AHEAD_TITLES, MONTH_AHEAD_TITLES, WEEKLY_REVIEW_TITLES, MONTHLY_REVIEW_TITLES, MOTIONS_TITLES, BREAKING_NEWS_TITLES, COMMITTEE_REPORTS_TITLES, PROPOSITIONS_TITLES, PROPOSITIONS_STRINGS, EDITORIAL_STRINGS, MOTIONS_STRINGS, WEEK_AHEAD_STRINGS, WEEK_AHEAD_STAKEHOLDER_STRINGS, BREAKING_STRINGS, DEEP_ANALYSIS_STRINGS, COMMITTEE_ANALYSIS_CONTENT_STRINGS, SWOT_STRINGS, DASHBOARD_STRINGS, SWOT_BUILDER_STRINGS, DASHBOARD_BUILDER_STRINGS, LOCALIZED_KEYWORDS, MONTH_IN_REVIEW_STRINGS, ANALYSIS_QUALITY_LABELS, ANALYSIS_INSIGHTS_HEADING, } from './language-articles.js';
15
15
  //# sourceMappingURL=languages.js.map
@@ -49,7 +49,7 @@ import { ensureDirectoryExists } from '../utils/file-utils.js';
49
49
  import { initializeMCPClient, fetchEPFeedData } from './pipeline/fetch-stage.js';
50
50
  import { createStrategyRegistry, generateArticleForStrategy, setAIMetadata, } from './pipeline/generate-stage.js';
51
51
  import { writeGenerationMetadata } from './pipeline/output-stage.js';
52
- import { runAnalysisStage, ALL_ANALYSIS_METHODS, VALID_ANALYSIS_METHODS, hasSubstantiveData, deriveArticleTypeSlug, } from './pipeline/analysis-stage.js';
52
+ import { runAnalysisStage, ALL_ANALYSIS_METHODS, VALID_ANALYSIS_METHODS, hasSubstantiveData, deriveArticleTypeSlug, isResolvedAnalysisDir, } from './pipeline/analysis-stage.js';
53
53
  import { discoverAnalysisFileEntries } from '../utils/file-utils.js';
54
54
  // โ”€โ”€โ”€ Content-module imports (bounded contexts) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
55
55
  import { parsePlenarySessions, parseEPEvents, parseCommitteeMeetings, parseLegislativeDocuments, parseLegislativePipeline, parseParliamentaryQuestions, buildWeekAheadContent, buildKeywords, PLACEHOLDER_EVENTS, buildWhatToWatchSection, buildStakeholderImpactMatrix, computeWeekPoliticalTemperature, } from './week-ahead-content.js';
@@ -208,6 +208,56 @@ function parseAnalysisMethods() {
208
208
  }
209
209
  return methods;
210
210
  }
211
+ /**
212
+ * Fetch EP feed data (month-level timeframe for month-level article types,
213
+ * one-week otherwise), populate `fetchedData` with the result, and enforce
214
+ * the "substantive data required" check.
215
+ *
216
+ * Extracted from {@link maybeRunAnalysis} to keep that function's cognitive
217
+ * complexity under the repo's SonarJS limit. Mutates `fetchedData` in place
218
+ * and throws when no substantive EP data is available.
219
+ *
220
+ * @param fetchedData - Target record to populate (mutated)
221
+ * @param client - Connected MCP client or null
222
+ * @param articleTypes - Requested article types (drives timeframe selection)
223
+ */
224
+ async function fetchAndValidateEPData(fetchedData, client, articleTypes) {
225
+ const MONTH_LEVEL_TYPES = ['month-ahead', 'month-in-review', 'committee-reports', 'motions'];
226
+ const needsMonthData = articleTypes
227
+ .map((t) => t.trim())
228
+ .some((t) => MONTH_LEVEL_TYPES.includes(t));
229
+ const feedTimeframe = needsMonthData ? 'one-month' : 'one-week';
230
+ // fetchEPFeedData handles a null client gracefully (returns undefined) and
231
+ // also loads from EP_FEED_DATA_FILE when set, so we call it unconditionally.
232
+ const feedData = await fetchEPFeedData(client, feedTimeframe);
233
+ if (feedData) {
234
+ fetchedData['events'] = feedData.events ?? [];
235
+ fetchedData['documents'] = feedData.documents ?? [];
236
+ fetchedData['adoptedTexts'] = feedData.adoptedTexts ?? [];
237
+ fetchedData['procedures'] = feedData.procedures ?? [];
238
+ fetchedData['mepUpdates'] = feedData.mepUpdates ?? [];
239
+ fetchedData['plenaryDocuments'] = feedData.plenaryDocuments ?? [];
240
+ fetchedData['committeeDocuments'] = feedData.committeeDocuments ?? [];
241
+ fetchedData['plenarySessionDocuments'] = feedData.plenarySessionDocuments ?? [];
242
+ fetchedData['externalDocuments'] = feedData.externalDocuments ?? [];
243
+ fetchedData['questions'] = feedData.questions ?? [];
244
+ fetchedData['declarations'] = feedData.declarations ?? [];
245
+ fetchedData['corporateBodies'] = feedData.corporateBodies ?? [];
246
+ }
247
+ if (!fetchedData['events']) {
248
+ // No MCP or feed-data file available โ€” populate empty arrays so builders don't fail
249
+ fetchedData['events'] = [];
250
+ fetchedData['sessions'] = [];
251
+ fetchedData['documents'] = [];
252
+ }
253
+ // Agentic workflows must not proceed with empty data โ€” analysis on empty
254
+ // data produces hollow output that should never feed article generation.
255
+ if (!hasSubstantiveData(fetchedData)) {
256
+ throw new Error('โŒ Analysis aborted: no substantive EP data was fetched. ' +
257
+ 'MCP data fetch must succeed before analysis can run. ' +
258
+ 'Check MCP connection, feed data file, or EP API availability.');
259
+ }
260
+ }
211
261
  /**
212
262
  * Run the analysis discovery stage (Fetch โ†’ Discover) before article generation.
213
263
  *
@@ -248,16 +298,24 @@ async function maybeRunAnalysis(date, client) {
248
298
  console.log(` Output dir: ${analysisDirBase}/${date}`);
249
299
  console.log(` Methods: ${enabledMethods.length} enabled`);
250
300
  console.log('');
251
- // Derive the feed timeframe from the requested article types so the analysis
252
- // window matches the generation window. Month-level types need 'one-month'.
253
- const MONTH_LEVEL_TYPES = ['month-ahead', 'month-in-review', 'committee-reports', 'motions'];
254
- const normalizedArticleTypes = articleTypes.map((t) => t.trim());
255
- const needsMonthData = normalizedArticleTypes.some((t) => MONTH_LEVEL_TYPES.includes(t));
256
- const feedTimeframe = needsMonthData ? 'one-month' : 'one-week';
257
- // Fetch comprehensive EP feed data. fetchEPFeedData handles a null client
258
- // gracefully (returns undefined) and also loads from EP_FEED_DATA_FILE when
259
- // set, so we call it unconditionally.
260
- //
301
+ // Detect whether `--analysis-dir` already points at a fully-resolved
302
+ // per-run analysis directory (agentic workflows pre-populate
303
+ // `analysis/daily/<date>/<slug>-run<N>` with AI-authored artifacts and
304
+ // pass it verbatim). When so, honour the path as-is; otherwise treat it
305
+ // as a base and compose `<base>/<date>/<slug>` below.
306
+ const analysisDirIsResolved = isResolvedAnalysisDir(analysisDirBase);
307
+ if (analysisDirIsResolved) {
308
+ console.log(` Analysis dir treated as pre-resolved run directory`);
309
+ }
310
+ // Short-circuit for `--analysis-only` wrap-up on a pre-resolved dir:
311
+ // the agent has already completed Stage A (data collection) and Stage B
312
+ // (artifact authoring); runAnalysisStage with outputDirIsResolved=true
313
+ // performs pure filesystem discovery and does not need EP data. Skip
314
+ // the expensive fetchEPFeedData call โ€” a slow one-month feed pull here
315
+ // can hang the pipeline wrap-up long enough for the safeoutputs MCP
316
+ // session to expire, which then blocks PR creation (see failed run
317
+ // #24802174815 for the cascade).
318
+ const skipDataFetch = analysisOnlyArg && analysisDirIsResolved;
261
319
  // Always initialise voting-derived keys (`patterns`, `votingRecords`) to
262
320
  // empty arrays so coalition/voting/cross-session analyses never receive
263
321
  // undefined. These feeds are not yet exposed by fetchEPFeedData, so they
@@ -267,37 +325,19 @@ async function maybeRunAnalysis(date, client) {
267
325
  patterns: [],
268
326
  votingRecords: [],
269
327
  };
270
- const feedData = await fetchEPFeedData(client, feedTimeframe);
271
- if (feedData) {
272
- fetchedData['events'] = feedData.events ?? [];
273
- fetchedData['documents'] = feedData.documents ?? [];
274
- fetchedData['adoptedTexts'] = feedData.adoptedTexts ?? [];
275
- fetchedData['procedures'] = feedData.procedures ?? [];
276
- fetchedData['mepUpdates'] = feedData.mepUpdates ?? [];
277
- fetchedData['plenaryDocuments'] = feedData.plenaryDocuments ?? [];
278
- fetchedData['committeeDocuments'] = feedData.committeeDocuments ?? [];
279
- fetchedData['plenarySessionDocuments'] = feedData.plenarySessionDocuments ?? [];
280
- fetchedData['externalDocuments'] = feedData.externalDocuments ?? [];
281
- fetchedData['questions'] = feedData.questions ?? [];
282
- fetchedData['declarations'] = feedData.declarations ?? [];
283
- fetchedData['corporateBodies'] = feedData.corporateBodies ?? [];
284
- }
285
- if (!fetchedData['events']) {
286
- // No MCP or feed-data file available โ€” populate empty arrays so builders don't fail
328
+ if (skipDataFetch) {
329
+ console.log(` Skipping EP data fetch โ€” --analysis-only wrap-up on pre-resolved dir (agent owns Stage A/B)`);
330
+ // Populate empty arrays so downstream builders don't trip on undefined.
287
331
  fetchedData['events'] = [];
288
332
  fetchedData['sessions'] = [];
289
333
  fetchedData['documents'] = [];
290
334
  }
291
- // Validate that substantive EP data was actually fetched.
292
- // Agentic workflows must not proceed with empty data โ€” analysis on empty
293
- // data produces hollow output that should never feed article generation.
294
- if (!hasSubstantiveData(fetchedData)) {
295
- const msg = 'โŒ Analysis aborted: no substantive EP data was fetched. ' +
296
- 'MCP data fetch must succeed before analysis can run. ' +
297
- 'Check MCP connection, feed data file, or EP API availability.';
298
- throw new Error(msg);
335
+ else {
336
+ await fetchAndValidateEPData(fetchedData, client, articleTypes);
299
337
  }
300
- const validArticleTypes = normalizedArticleTypes.filter((t) => VALID_ARTICLE_CATEGORIES.includes(t));
338
+ const validArticleTypes = articleTypes
339
+ .map((t) => t.trim())
340
+ .filter((t) => VALID_ARTICLE_CATEGORIES.includes(t));
301
341
  // Derive a slug from the article types to scope analysis output per workflow,
302
342
  // preventing merge conflicts when multiple workflows run on the same date.
303
343
  // When a run ID is provided (e.g. GITHUB_RUN_NUMBER), append it to the slug
@@ -308,8 +348,8 @@ async function maybeRunAnalysis(date, client) {
308
348
  console.log(` Article type slug: ${slug}`);
309
349
  if (runId)
310
350
  console.log(` Run ID: ${runId}`);
311
- // Pass requireData=true so runAnalysisStage enforces data availability
312
- // and aborts when no substantive data is available โ€” discovery on empty data produces no artifacts.
351
+ // Require data only when we actually fetched it. The pre-resolved
352
+ // --analysis-only wrap-up path performs discovery-only and needs no data.
313
353
  const ctx = await runAnalysisStage(fetchedData, {
314
354
  articleTypes: validArticleTypes,
315
355
  date,
@@ -318,7 +358,8 @@ async function maybeRunAnalysis(date, client) {
318
358
  enabledMethods,
319
359
  skipCompleted: true,
320
360
  verbose: analysisVerboseArg,
321
- requireData: true,
361
+ requireData: !skipDataFetch,
362
+ outputDirIsResolved: analysisDirIsResolved,
322
363
  });
323
364
  const totalMethods = ctx.manifest.methods.length;
324
365
  const completedCount = ctx.manifest.methods.filter((method) => method.status === 'completed').length;
@@ -1,5 +1,6 @@
1
1
  import type { ArticleCategory } from '../../types/index.js';
2
2
  import type { ConfidenceLevel } from '../../types/index.js';
3
+ import { type AnalysisManifestHistoryEntry } from '../../utils/file-utils.js';
3
4
  /**
4
5
  * Union type of all recognised analysis method identifiers.
5
6
  */
@@ -34,7 +35,44 @@ export interface AnalysisStageOptions {
34
35
  * Retained for backward compatibility with agentic workflow invocations.
35
36
  */
36
37
  readonly requireData?: boolean;
38
+ /**
39
+ * When true, treat {@link AnalysisStageOptions.outputDir} as a fully
40
+ * resolved analysis directory and skip the `<outputDir>/<date>/<slug>`
41
+ * composition.
42
+ *
43
+ * This supports agentic workflows that pre-populate a per-run directory
44
+ * (e.g. `analysis/daily/<date>/<slug>-run<N>`) with AI-authored artifacts
45
+ * and then invoke `news-enhanced.ts --analysis-dir=<that dir>` for the
46
+ * discovery + article-generation phase.
47
+ */
48
+ readonly outputDirIsResolved?: boolean;
49
+ /**
50
+ * Optional stable run identifier used to record a history entry in the
51
+ * manifest when `outputDirIsResolved` is true. Enables repeated analysis
52
+ * runs against the same `analysis/daily/${DATE}/${TYPE}/` folder to
53
+ * accumulate an audit trail via `manifest.json.history[]` instead of
54
+ * triggering the `-2` suffix. When unset, a UUID-based run id is used.
55
+ */
56
+ readonly runId?: string;
57
+ /**
58
+ * Optional Stage-C gate result recorded in the manifest history entry
59
+ * written after discovery. Defaults to `'PENDING'` โ€” the Stage-C
60
+ * validator or the caller updates this later.
61
+ */
62
+ readonly gateResult?: AnalysisManifestHistoryEntry['gateResult'];
37
63
  }
64
+ /**
65
+ * Detect whether `candidate` looks like a pre-populated, fully-resolved
66
+ * analysis run directory.
67
+ *
68
+ * Returns `true` when the directory exists AND either contains a
69
+ * `manifest.json` from a prior run or at least one of the canonical
70
+ * analysis subdirectories in {@link RESOLVED_ANALYSIS_SUBDIRS}.
71
+ *
72
+ * @param candidate - Directory path to inspect.
73
+ * @returns `true` when the directory is a resolved analysis run dir.
74
+ */
75
+ export declare function isResolvedAnalysisDir(candidate: string): boolean;
38
76
  /** Status record written into the manifest for each method */
39
77
  export interface AnalysisMethodStatus {
40
78
  readonly method: AnalysisMethod;
@@ -44,15 +82,60 @@ export interface AnalysisMethodStatus {
44
82
  readonly duration: number;
45
83
  readonly summary: string;
46
84
  }
85
+ /**
86
+ * `manifest.files.*` structure โ€” artifact paths grouped by subdirectory.
87
+ *
88
+ * Mirrors the shape consumed by
89
+ * `src/utils/validate-analysis-completeness.ts:extractListedPaths` (nested
90
+ * `{ category: string[] }` variant). Each key is the first path segment of
91
+ * an artifact's relative path (e.g. `intelligence`, `classification`,
92
+ * `risk-scoring`), and the value is the list of relative paths under that
93
+ * subdir.
94
+ *
95
+ * Hyphenated keys (e.g. `risk-scoring`, `threat-assessment`) match the
96
+ * canonical on-disk subdirectory names defined in
97
+ * `scripts/resolve-analysis-dir.sh` and `DISCOVERY_SUBDIRS` /
98
+ * `RESOLVED_ANALYSIS_SUBDIRS`. They must be quoted in TypeScript because
99
+ * `-` is not a valid identifier character.
100
+ */
101
+ export interface AnalysisManifestFiles {
102
+ readonly classification?: readonly string[];
103
+ readonly 'risk-scoring'?: readonly string[];
104
+ readonly 'threat-assessment'?: readonly string[];
105
+ readonly intelligence?: readonly string[];
106
+ readonly documents?: readonly string[];
107
+ readonly existing?: readonly string[];
108
+ readonly data?: readonly string[];
109
+ readonly runs?: readonly string[];
110
+ readonly [key: string]: readonly string[] | undefined;
111
+ }
47
112
  /** Metadata record written to manifest.json for each analysis run */
48
113
  export interface AnalysisManifest {
49
114
  readonly runId: string;
50
115
  readonly date: string;
116
+ /**
117
+ * Top-level article-type slug (e.g. `committee-reports`, `breaking`).
118
+ *
119
+ * Required by the Stage-C completeness gate (see
120
+ * `03-analysis-completeness-gate.md` ยง2 item 8 and
121
+ * `src/utils/validate-analysis-completeness.ts:loadManifest`). Kept in
122
+ * addition to the legacy `articleTypeSlug` field for backward compatibility.
123
+ */
124
+ readonly articleType?: string | undefined;
51
125
  readonly articleTypeSlug?: string | undefined;
52
126
  readonly startTime: string;
53
127
  readonly endTime: string;
54
128
  readonly articleTypes: readonly ArticleCategory[];
55
129
  readonly methods: readonly AnalysisMethodStatus[];
130
+ /**
131
+ * Artifact paths grouped by subdirectory (e.g.
132
+ * `{ intelligence: ['intelligence/pestle-analysis.md', ...] }`).
133
+ *
134
+ * Required by the Stage-C completeness gate โ€” see
135
+ * `src/utils/validate-analysis-completeness.ts:loadManifest` which rejects
136
+ * any manifest without a top-level `files` object.
137
+ */
138
+ readonly files?: AnalysisManifestFiles;
56
139
  readonly overallConfidence: ConfidenceLevel;
57
140
  readonly dataSourcesUsed: readonly string[];
58
141
  readonly documentsAnalyzed?: number;
@@ -92,6 +175,12 @@ export declare function deriveArticleTypeSlug(articleTypes: readonly (ArticleCat
92
175
  * (if one doesn't already exist) so downstream consumers that check for
93
176
  * the manifest continue to work.
94
177
  *
178
+ * When `options.outputDirIsResolved` is true, `outputDir` is honoured
179
+ * verbatim and the uniqueness-suffix step is bypassed โ€” agentic workflows
180
+ * pre-populate `analysis/daily/<date>/<slug>-run<N>/` with `manifest.json`
181
+ * plus artifacts during Stage B, and discovery must consume that exact
182
+ * path rather than being redirected to a `-2` suffix.
183
+ *
95
184
  * @param fetchedData - Raw EP data (used only for the requireData check)
96
185
  * @param options - Analysis stage configuration
97
186
  * @returns Analysis context with discovered methods