heor-agent-mcp 1.6.3 → 1.9.2

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 (41) hide show
  1. package/README.md +92 -17
  2. package/dist/icf/jargon.d.ts +30 -0
  3. package/dist/icf/jargon.d.ts.map +1 -0
  4. package/dist/icf/jargon.js +233 -0
  5. package/dist/icf/jargon.js.map +1 -0
  6. package/dist/icf/readability.d.ts +18 -0
  7. package/dist/icf/readability.d.ts.map +1 -0
  8. package/dist/icf/readability.js +156 -0
  9. package/dist/icf/readability.js.map +1 -0
  10. package/dist/icf/syllables.d.ts +21 -0
  11. package/dist/icf/syllables.d.ts.map +1 -0
  12. package/dist/icf/syllables.js +100 -0
  13. package/dist/icf/syllables.js.map +1 -0
  14. package/dist/icf/types.d.ts +102 -0
  15. package/dist/icf/types.d.ts.map +1 -0
  16. package/dist/icf/types.js +8 -0
  17. package/dist/icf/types.js.map +1 -0
  18. package/dist/models/evppi.d.ts +54 -16
  19. package/dist/models/evppi.d.ts.map +1 -1
  20. package/dist/models/evppi.js +199 -54
  21. package/dist/models/evppi.js.map +1 -1
  22. package/dist/models/survivalFitting.d.ts +30 -3
  23. package/dist/models/survivalFitting.d.ts.map +1 -1
  24. package/dist/models/survivalFitting.js +359 -6
  25. package/dist/models/survivalFitting.js.map +1 -1
  26. package/dist/providers/types.d.ts +4 -0
  27. package/dist/providers/types.d.ts.map +1 -1
  28. package/dist/server.js +5 -0
  29. package/dist/server.js.map +1 -1
  30. package/dist/tools/costEffectivenessModel.d.ts.map +1 -1
  31. package/dist/tools/costEffectivenessModel.js +24 -5
  32. package/dist/tools/costEffectivenessModel.js.map +1 -1
  33. package/dist/tools/icfReadabilityCheck.d.ts +37 -0
  34. package/dist/tools/icfReadabilityCheck.d.ts.map +1 -0
  35. package/dist/tools/icfReadabilityCheck.js +220 -0
  36. package/dist/tools/icfReadabilityCheck.js.map +1 -0
  37. package/dist/tools/survivalFitting.d.ts +20 -1
  38. package/dist/tools/survivalFitting.d.ts.map +1 -1
  39. package/dist/tools/survivalFitting.js +59 -13
  40. package/dist/tools/survivalFitting.js.map +1 -1
  41. package/package.json +1 -1
package/README.md CHANGED
@@ -20,24 +20,45 @@ Built for pharmaceutical, biotech, CRO, and medical affairs teams who need rigor
20
20
 
21
21
  ---
22
22
 
23
- ## What's new in v1.1.0
23
+ ## First 60 seconds
24
24
 
25
- Pharmacovigilance + workflow orchestration:
25
+ Verify your install works **before** wiring it into Claude / Cursor / Continue. Open two terminal tabs:
26
26
 
27
- - **`pv_classify` tool** classifies a planned study into its EMA pharmacovigilance regulatory category (PASS imposed/voluntary, PAES, RMP Annex 4, DUS, active surveillance registry, pregnancy registry, spontaneous reporting, ICH E2E plan). Returns the matching GVP module (V/VI/VIII/VIII Addendum I), ENCePP protocol template ID, RMP implications, FDA analogue, and submission obligations. Pure decision-tree per EMA GVP rev 4 + EU Regulation 1235/2010 Article 107a. <200ms response.
28
- - **`hta_dossier` Pharmacovigilance Plan section** — pass `pv_classification` from `pv_classify` to `hta_dossier` and the dossier output now includes a PV Plan section between RoB and CEA. Without it, a one-line "PV plan not provided" note flags the gap so reviewers see what's missing.
29
- - **`maic_workflow` orchestrator** *(v1.0.6)* — runs the full MAIC discovery+screening pipeline (ITC feasibility + parallel literature_search + screening + RoB + network) in one MCP call. Built for ChatGPT-5.3 surfaces where chaining 5+ tool calls in parallel is unreliable; works equally well from Claude.
30
- - **`examples` tool** *(v1.0.5)* — pre-filled JSON inputs for heavy-schema tools (CEA, BIA, survival, MAIC, Bucher) plus a `maic_workflow_recipe` multi-step prompt template for ChatGPT users.
31
- - **CMS IRA awareness** — when `pv_classify` is called with US jurisdiction, output explicitly notes that CMS IRA Medicare price-negotiation calculations exclude PV cost data — track those obligations in the regulatory budget, not the HEOR cost-effectiveness model.
32
- - **GRADE I²-based inconsistency, GRADE upgrading (Guyatt 2011), Bucher consistency check, EQ-5D 5L baseline-utility-aware impact** *(v1.0.4)* — see [CHANGELOG.md](./CHANGELOG.md).
33
- - **ChatGPT Custom GPT support** *(v1.0.4)* — OpenAPI 3.1 adapter at `/api/openapi` lets you build a Custom GPT in 5 minutes. See [ChatGPT Custom GPT](#chatgpt-custom-gpt) below.
34
- - **Surface-tagged analytics** *(v1.0.4)* — every `tool_call` PostHog event carries a `surface` property (`claude_anthropic_web` / `chatgpt_adapter` / `claude_desktop` / `direct_mcp`).
27
+ **Tab 1start the server in HTTP mode:**
35
28
 
36
- See [CHANGELOG.md](./CHANGELOG.md) for the full diff.
29
+ ```bash
30
+ MCP_HTTP_PORT=8080 npx heor-agent-mcp@latest
31
+ ```
32
+
33
+ You should see:
34
+
35
+ ```
36
+ HEORAgent MCP server running on HTTP port 8080
37
+ ```
38
+
39
+ **Tab 2 — confirm it responds:**
40
+
41
+ ```bash
42
+ curl -s http://localhost:8080/health
43
+ ```
44
+
45
+ Expected output:
46
+
47
+ ```json
48
+ {"status":"ok","server":"heor-agent-mcp","version":"1.9.2"}
49
+ ```
50
+
51
+ ✅ If you see the JSON above, the npm package works on your machine. Any further issues are in your MCP client config (Claude Desktop / Cursor / Continue), not the server.
52
+
53
+ ❌ If you see `command not found`, run `node --version` — you need Node ≥20. If you see a different error, file a quick issue at https://github.com/neptun2000/heor-agent-mcp/issues with the output.
54
+
55
+ Now stop Tab 1 (`Ctrl+C`) and pick your client below — you don't need the HTTP mode for the actual integration; Claude / Cursor / Continue all use stdio.
37
56
 
38
57
  ---
39
58
 
40
- ## Quick Start
59
+ ## Quick Start (per client)
60
+
61
+ Pick your MCP host:
41
62
 
42
63
  ### Claude Code
43
64
 
@@ -47,9 +68,9 @@ claude mcp add heor-agent -- npx heor-agent-mcp
47
68
 
48
69
  Then restart Claude Code.
49
70
 
50
- ### Claude Desktop / claude.ai
71
+ ### Claude Desktop / claude.ai Desktop
51
72
 
52
- Add to your MCP configuration:
73
+ Edit your MCP config file (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS) and add:
53
74
 
54
75
  ```json
55
76
  {
@@ -62,15 +83,69 @@ Add to your MCP configuration:
62
83
  }
63
84
  ```
64
85
 
65
- ### Verify
86
+ Then restart Claude Desktop.
87
+
88
+ ### Cursor / Continue / Cline
89
+
90
+ Same config shape as Claude Desktop above; the file path differs by client:
91
+ - **Cursor:** `Settings → MCP → Add new MCP server`
92
+ - **Continue:** `~/.continue/config.json` under the `mcpServers` key
93
+ - **Cline:** `Settings → MCP Servers → Edit MCP Settings`
94
+
95
+ ### Hosted (no install)
96
+
97
+ - **ChatGPT (Plus / Team):** [HEORAgent on ChatGPT](https://chatgpt.com/g/g-69f651f588f48191b1d69d54409857ec-heoragent) — type `/heor` to use it; works on any conversation.
98
+ - **Web UI (Claude, BYOK):** [web-michael-ns-projects.vercel.app](https://web-michael-ns-projects.vercel.app) — bring your Anthropic API key; runs the full v1.6.3 toolset.
99
+
100
+ ---
101
+
102
+ ## Your first prompt
103
+
104
+ Once your MCP host is configured, paste any of these to verify end-to-end:
105
+
106
+ ```
107
+ Run a literature search for semaglutide cost-effectiveness in T2D
108
+ using PubMed, NICE TAs, and ICER reports. Set runs=2.
109
+ ```
66
110
 
67
111
  ```
68
- > Run a literature search for semaglutide cost-effectiveness in T2D using PubMed and NICE TAs
112
+ Run irb_review for an industry-funded interventional Phase 2 trial in
113
+ relapsed MM — multi-site US+EU, pseudonymized data, greater-than-minimal
114
+ risk. I need the review tier, GDPR/HIPAA DMP, SAE framework, and the
115
+ ready-to-paste cover letter.
69
116
  ```
70
117
 
118
+ ```
119
+ Run jca_pico_scope for osimertinib in EGFR-mutant 2L NSCLC across
120
+ DE/FR/IT/ES/NL. Then prepare an EU JCA dossier draft using the picos.
121
+ ```
122
+
123
+ The first prompt exercises `literature_search` + `validate_links` (free, no API keys needed). The second exercises `irb_review` (pure decision tree, instant). The third exercises `jca_pico_scope` → `hta_dossier` pipeline.
124
+
125
+ ---
126
+
127
+ ## What's new
128
+
129
+ See [CHANGELOG.md](./CHANGELOG.md) for full version history. Current: **v1.9.2** (27 tools, 44 data sources).
130
+
131
+ ### v1.0.4 highlights (still in v1.6.3)
132
+
133
+ Pharmacovigilance + workflow orchestration:
134
+
135
+ - **`pv_classify` tool** — classifies a planned study into its EMA pharmacovigilance regulatory category (PASS imposed/voluntary, PAES, RMP Annex 4, DUS, active surveillance registry, pregnancy registry, spontaneous reporting, ICH E2E plan). Returns the matching GVP module (V/VI/VIII/VIII Addendum I), ENCePP protocol template ID, RMP implications, FDA analogue, and submission obligations. Pure decision-tree per EMA GVP rev 4 + EU Regulation 1235/2010 Article 107a. <200ms response.
136
+ - **`hta_dossier` Pharmacovigilance Plan section** — pass `pv_classification` from `pv_classify` to `hta_dossier` and the dossier output now includes a PV Plan section between RoB and CEA. Without it, a one-line "PV plan not provided" note flags the gap so reviewers see what's missing.
137
+ - **`maic_workflow` orchestrator** *(v1.0.6)* — runs the full MAIC discovery+screening pipeline (ITC feasibility + parallel literature_search + screening + RoB + network) in one MCP call. Built for ChatGPT-5.3 surfaces where chaining 5+ tool calls in parallel is unreliable; works equally well from Claude.
138
+ - **`examples` tool** *(v1.0.5)* — pre-filled JSON inputs for heavy-schema tools (CEA, BIA, survival, MAIC, Bucher) plus a `maic_workflow_recipe` multi-step prompt template for ChatGPT users.
139
+ - **CMS IRA awareness** — when `pv_classify` is called with US jurisdiction, output explicitly notes that CMS IRA Medicare price-negotiation calculations exclude PV cost data — track those obligations in the regulatory budget, not the HEOR cost-effectiveness model.
140
+ - **GRADE I²-based inconsistency, GRADE upgrading (Guyatt 2011), Bucher consistency check, EQ-5D 5L baseline-utility-aware impact** *(v1.0.4)* — see [CHANGELOG.md](./CHANGELOG.md).
141
+ - **ChatGPT Custom GPT support** *(v1.0.4)* — OpenAPI 3.1 adapter at `/api/openapi` lets you build a Custom GPT in 5 minutes. See [ChatGPT Custom GPT](#chatgpt-custom-gpt) below.
142
+ - **Surface-tagged analytics** *(v1.0.4)* — every `tool_call` PostHog event carries a `surface` property (`claude_anthropic_web` / `chatgpt_adapter` / `claude_desktop` / `direct_mcp`).
143
+
144
+ See [CHANGELOG.md](./CHANGELOG.md) for the full diff.
145
+
71
146
  ---
72
147
 
73
- ## Tools (22)
148
+ ## Tools (27)
74
149
 
75
150
  | Tool | Purpose |
76
151
  |------|---------|
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Curated medical-jargon dictionary for ICF readability checking.
3
+ *
4
+ * Built from common patterns flagged in NIH Plain Language ICF
5
+ * guidance, FDA's "Communicating Risks and Benefits" guidance, and
6
+ * the most-frequently-cited offenders in IRB consent-form audits.
7
+ *
8
+ * Each entry maps a clinical-trial term to a plain-language alternative
9
+ * and (optionally) a one-line rationale. Matching is case-insensitive
10
+ * with whole-word boundaries (so "co-administered" doesn't match
11
+ * "co-author").
12
+ *
13
+ * v1 covers ~80 high-frequency terms. Future revs can grow this without
14
+ * any code change.
15
+ */
16
+ import type { JargonHit } from "./types.js";
17
+ export interface JargonEntry {
18
+ term: string;
19
+ plain_language: string;
20
+ rationale?: string;
21
+ }
22
+ export declare const JARGON_DICTIONARY: ReadonlyArray<JargonEntry>;
23
+ /**
24
+ * Find all jargon hits in a body of text. Whole-word, case-insensitive.
25
+ * Returns one JargonHit per dictionary entry that matches at least once,
26
+ * with up to 5 occurrence offsets per term (capped to keep the output
27
+ * compact for repeated terms).
28
+ */
29
+ export declare function findJargon(text: string): JargonHit[];
30
+ //# sourceMappingURL=jargon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jargon.d.ts","sourceRoot":"","sources":["../../src/icf/jargon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,iBAAiB,EAAE,aAAa,CAAC,WAAW,CAsLxD,CAAC;AASF;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,CAgCpD"}
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Curated medical-jargon dictionary for ICF readability checking.
3
+ *
4
+ * Built from common patterns flagged in NIH Plain Language ICF
5
+ * guidance, FDA's "Communicating Risks and Benefits" guidance, and
6
+ * the most-frequently-cited offenders in IRB consent-form audits.
7
+ *
8
+ * Each entry maps a clinical-trial term to a plain-language alternative
9
+ * and (optionally) a one-line rationale. Matching is case-insensitive
10
+ * with whole-word boundaries (so "co-administered" doesn't match
11
+ * "co-author").
12
+ *
13
+ * v1 covers ~80 high-frequency terms. Future revs can grow this without
14
+ * any code change.
15
+ */
16
+ export const JARGON_DICTIONARY = [
17
+ // Trial-design terminology
18
+ { term: "randomized", plain_language: "assigned by chance" },
19
+ { term: "randomised", plain_language: "assigned by chance" },
20
+ {
21
+ term: "double-blind",
22
+ plain_language: "neither you nor your study doctor will know which group you are in",
23
+ },
24
+ {
25
+ term: "single-blind",
26
+ plain_language: "you will not know which group you are in",
27
+ },
28
+ {
29
+ term: "placebo",
30
+ plain_language: "an inactive substance with no medicine in it",
31
+ },
32
+ {
33
+ term: "open-label",
34
+ plain_language: "you and the study doctor will both know which medicine you are taking",
35
+ },
36
+ {
37
+ term: "crossover",
38
+ plain_language: "you will switch from one treatment to the other partway through",
39
+ },
40
+ { term: "stratified", plain_language: "grouped by certain characteristics" },
41
+ { term: "cohort", plain_language: "group of people in the study" },
42
+ { term: "arm", plain_language: "treatment group" },
43
+ { term: "intervention", plain_language: "the treatment being studied" },
44
+ { term: "comparator", plain_language: "the treatment being compared" },
45
+ // Adverse events / safety
46
+ {
47
+ term: "adverse event",
48
+ plain_language: "side effect or unwanted health problem",
49
+ },
50
+ { term: "adverse reaction", plain_language: "side effect" },
51
+ {
52
+ term: "serious adverse event",
53
+ plain_language: "serious side effect (one that is life-threatening, requires hospital care, or causes lasting harm)",
54
+ },
55
+ { term: "SAE", plain_language: "serious side effect" },
56
+ {
57
+ term: "SUSAR",
58
+ plain_language: "an unexpected serious side effect that may be related to the study drug",
59
+ },
60
+ { term: "untoward", plain_language: "unwanted or unexpected" },
61
+ // Pharmacology
62
+ {
63
+ term: "pharmacokinetics",
64
+ plain_language: "how the medicine moves through your body",
65
+ },
66
+ {
67
+ term: "pharmacodynamics",
68
+ plain_language: "how the medicine works in your body",
69
+ },
70
+ {
71
+ term: "bioavailability",
72
+ plain_language: "how much of the medicine your body absorbs",
73
+ },
74
+ {
75
+ term: "half-life",
76
+ plain_language: "how long the medicine stays in your body",
77
+ },
78
+ {
79
+ term: "metabolite",
80
+ plain_language: "a substance your body makes from the medicine",
81
+ },
82
+ {
83
+ term: "metabolism",
84
+ plain_language: "how your body breaks down the medicine",
85
+ },
86
+ // Disease / clinical
87
+ { term: "etiology", plain_language: "cause" },
88
+ { term: "aetiology", plain_language: "cause" },
89
+ { term: "prognosis", plain_language: "likely course of the disease" },
90
+ {
91
+ term: "comorbidity",
92
+ plain_language: "another health condition you have",
93
+ },
94
+ { term: "comorbidities", plain_language: "other health conditions" },
95
+ { term: "concomitant", plain_language: "at the same time" },
96
+ { term: "exacerbation", plain_language: "flare-up or worsening" },
97
+ { term: "remission", plain_language: "the disease is under control" },
98
+ {
99
+ term: "refractory",
100
+ plain_language: "not responding to standard treatment",
101
+ },
102
+ { term: "neoplasm", plain_language: "tumor" },
103
+ { term: "malignant", plain_language: "cancerous" },
104
+ { term: "benign", plain_language: "not cancerous" },
105
+ {
106
+ term: "metastasis",
107
+ plain_language: "the cancer has spread to another part of the body",
108
+ },
109
+ { term: "metastatic", plain_language: "cancer that has spread" },
110
+ {
111
+ term: "in situ",
112
+ plain_language: "in its original place; has not spread",
113
+ },
114
+ // Statistical / measurement
115
+ {
116
+ term: "biomarker",
117
+ plain_language: "a measurement we use to track disease or treatment",
118
+ },
119
+ { term: "endpoint", plain_language: "outcome we are measuring" },
120
+ {
121
+ term: "primary endpoint",
122
+ plain_language: "the main outcome we are measuring",
123
+ },
124
+ {
125
+ term: "secondary endpoint",
126
+ plain_language: "additional outcome we are measuring",
127
+ },
128
+ {
129
+ term: "efficacy",
130
+ plain_language: "how well the treatment works under controlled conditions",
131
+ },
132
+ // v1.9.1 fix: removed an "effectiveness" entry here. NIH Plain Language
133
+ // ICF guidance and FDA Communicating Risks and Benefits recommend
134
+ // "effectiveness" AS the plain-language replacement for "efficacy" —
135
+ // flagging it as jargon contradicts the cited authority and would tell
136
+ // investigators who did the right thing to undo it.
137
+ { term: "incidence", plain_language: "how often new cases occur" },
138
+ {
139
+ term: "prevalence",
140
+ plain_language: "how many people have the condition right now",
141
+ },
142
+ {
143
+ term: "statistical significance",
144
+ plain_language: "the difference is unlikely to be due to chance",
145
+ },
146
+ {
147
+ term: "confidence interval",
148
+ plain_language: "the likely range of the true value",
149
+ },
150
+ // Procedures
151
+ {
152
+ term: "venipuncture",
153
+ plain_language: "blood draw with a needle",
154
+ },
155
+ { term: "phlebotomy", plain_language: "drawing blood" },
156
+ { term: "lumbar puncture", plain_language: "spinal tap" },
157
+ { term: "biopsy", plain_language: "taking a small tissue sample" },
158
+ { term: "intravenous", plain_language: "given through a vein" },
159
+ { term: "subcutaneous", plain_language: "given under the skin" },
160
+ { term: "intramuscular", plain_language: "given into a muscle" },
161
+ { term: "oral", plain_language: "by mouth" },
162
+ { term: "topical", plain_language: "applied to the skin" },
163
+ { term: "ophthalmic", plain_language: "for the eye" },
164
+ { term: "ocular", plain_language: "of the eye" },
165
+ // Regulatory / consent-specific
166
+ {
167
+ term: "informed consent",
168
+ plain_language: "your decision to take part after learning about the study",
169
+ },
170
+ { term: "withdrawal", plain_language: "leaving the study" },
171
+ { term: "discontinuation", plain_language: "stopping" },
172
+ { term: "compensation", plain_language: "payment" },
173
+ { term: "reimbursement", plain_language: "money back for your costs" },
174
+ { term: "remuneration", plain_language: "payment for your time" },
175
+ { term: "ascertain", plain_language: "find out" },
176
+ { term: "elucidate", plain_language: "explain" },
177
+ { term: "facilitate", plain_language: "help" },
178
+ { term: "utilize", plain_language: "use" },
179
+ { term: "utilise", plain_language: "use" },
180
+ { term: "demonstrate", plain_language: "show" },
181
+ { term: "subsequent", plain_language: "later" },
182
+ { term: "prior to", plain_language: "before" },
183
+ { term: "in the event that", plain_language: "if" },
184
+ { term: "in conjunction with", plain_language: "with" },
185
+ { term: "due to the fact that", plain_language: "because" },
186
+ { term: "at this point in time", plain_language: "now" },
187
+ ];
188
+ /**
189
+ * Escape regex metacharacters for safe inclusion in a constructed pattern.
190
+ */
191
+ function escapeRegex(s) {
192
+ return s.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
193
+ }
194
+ /**
195
+ * Find all jargon hits in a body of text. Whole-word, case-insensitive.
196
+ * Returns one JargonHit per dictionary entry that matches at least once,
197
+ * with up to 5 occurrence offsets per term (capped to keep the output
198
+ * compact for repeated terms).
199
+ */
200
+ export function findJargon(text) {
201
+ if (!text)
202
+ return [];
203
+ const lower = text.toLowerCase();
204
+ const hits = [];
205
+ for (const entry of JARGON_DICTIONARY) {
206
+ const term = entry.term.toLowerCase();
207
+ // Optional trailing "s" handles English plurals: "adverse event"
208
+ // matches "adverse events", "biomarker" matches "biomarkers", etc.
209
+ // Harmless on terms with no plural form (e.g., "in situ"). Doesn't
210
+ // cover irregular plurals like "metastasis → metastases" — those
211
+ // get separate dictionary entries when frequent.
212
+ const pattern = new RegExp(`\\b${escapeRegex(term)}s?\\b`, "gi");
213
+ const matches = Array.from(lower.matchAll(pattern));
214
+ if (matches.length === 0)
215
+ continue;
216
+ const occurrences = [];
217
+ for (const m of matches.slice(0, 5)) {
218
+ const idx = m.index ?? 0;
219
+ occurrences.push({
220
+ index: idx,
221
+ matched_form: text.slice(idx, idx + m[0].length),
222
+ });
223
+ }
224
+ hits.push({
225
+ term: entry.term,
226
+ occurrences,
227
+ plain_language: entry.plain_language,
228
+ rationale: entry.rationale,
229
+ });
230
+ }
231
+ return hits;
232
+ }
233
+ //# sourceMappingURL=jargon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jargon.js","sourceRoot":"","sources":["../../src/icf/jargon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { DocumentStats, ReadabilityScores } from "./types.js";
2
+ export declare function splitSentences(text: string): string[];
3
+ export declare function tokenizeWords(sentence: string): string[];
4
+ export declare function computeStats(text: string): {
5
+ stats: DocumentStats;
6
+ sentenceTokens: Array<{
7
+ raw: string;
8
+ words: string[];
9
+ syllables: number;
10
+ complex: number;
11
+ }>;
12
+ };
13
+ export declare function fleschKincaidGrade(stats: DocumentStats): number;
14
+ export declare function fleschReadingEase(stats: DocumentStats): number;
15
+ export declare function gunningFog(stats: DocumentStats): number;
16
+ export declare function smog(stats: DocumentStats): number;
17
+ export declare function computeReadabilityScores(stats: DocumentStats): ReadabilityScores;
18
+ //# sourceMappingURL=readability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readability.d.ts","sourceRoot":"","sources":["../../src/icf/readability.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AA6BnE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAwCrD;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAMxD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG;IAC1C,KAAK,EAAE,aAAa,CAAC;IACrB,cAAc,EAAE,KAAK,CAAC;QACpB,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;CACJ,CAkCA;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAO/D;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAO9D;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAOvD;AAED,wBAAgB,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CASjD;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,aAAa,GACnB,iBAAiB,CAOnB"}
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Readability formulas: Flesch-Kincaid Grade Level, Flesch Reading
3
+ * Ease, Gunning Fog Index, SMOG Grade.
4
+ *
5
+ * All four standard formulas in one place so the tool can report a
6
+ * cross-validated readability picture (no single score is perfect;
7
+ * disagreements between FKGL and SMOG often signal text with unusual
8
+ * sentence-vs-word complexity tradeoffs).
9
+ *
10
+ * References:
11
+ * - Kincaid, Fishburne, Rogers, Chissom (1975). FKGL.
12
+ * - Flesch (1948). Reading Ease.
13
+ * - Gunning (1952). Fog Index.
14
+ * - McLaughlin (1969). SMOG.
15
+ */
16
+ import { countSyllables, countComplexWord } from "./syllables.js";
17
+ /**
18
+ * Split into sentences using punctuation as terminators. Handles common
19
+ * abbreviations (Dr., Mr., e.g., i.e.) by NOT splitting on them.
20
+ */
21
+ const ABBREV = new Set([
22
+ "dr",
23
+ "mr",
24
+ "mrs",
25
+ "ms",
26
+ "prof",
27
+ "st",
28
+ "co",
29
+ "ltd",
30
+ "inc",
31
+ "corp",
32
+ "vs",
33
+ "etc",
34
+ "e.g",
35
+ "i.e",
36
+ "fig",
37
+ "vol",
38
+ "pp",
39
+ "no",
40
+ "ed",
41
+ "eds",
42
+ ]);
43
+ export function splitSentences(text) {
44
+ if (!text || text.trim().length === 0)
45
+ return [];
46
+ // Normalise newlines and bullets to sentence terminators.
47
+ const normalised = text.replace(/\s+/g, " ").trim();
48
+ const candidates = [];
49
+ let start = 0;
50
+ for (let i = 0; i < normalised.length; i++) {
51
+ const ch = normalised[i];
52
+ if (ch === "." || ch === "!" || ch === "?") {
53
+ // Look-back for known abbreviation immediately before the period.
54
+ const prevWord = normalised
55
+ .slice(Math.max(0, i - 8), i)
56
+ .toLowerCase()
57
+ .match(/[a-z.]+$/)?.[0];
58
+ if (ch === "." &&
59
+ prevWord &&
60
+ ABBREV.has(prevWord.replace(/\.$/, ""))) {
61
+ continue; // skip — abbreviation, not a sentence end
62
+ }
63
+ // Look-ahead — must be followed by whitespace+capital or EOS.
64
+ const next = normalised[i + 1];
65
+ const next2 = normalised[i + 2];
66
+ if (i === normalised.length - 1 ||
67
+ (next === " " &&
68
+ next2 &&
69
+ (next2 === next2.toUpperCase() || /[0-9"(]/.test(next2)))) {
70
+ candidates.push(normalised.slice(start, i + 1).trim());
71
+ start = i + 1;
72
+ }
73
+ }
74
+ }
75
+ if (start < normalised.length) {
76
+ const tail = normalised.slice(start).trim();
77
+ if (tail.length > 0)
78
+ candidates.push(tail);
79
+ }
80
+ return candidates.filter((s) => s.length > 0);
81
+ }
82
+ export function tokenizeWords(sentence) {
83
+ return sentence
84
+ .replace(/[^a-zA-Z\s'-]/g, " ")
85
+ .split(/\s+/)
86
+ .map((w) => w.trim())
87
+ .filter((w) => w.length > 0 && /[a-zA-Z]/.test(w));
88
+ }
89
+ export function computeStats(text) {
90
+ const sentences = splitSentences(text);
91
+ const sentenceTokens = sentences.map((raw) => {
92
+ const words = tokenizeWords(raw);
93
+ const syllables = words.reduce((sum, w) => sum + countSyllables(w), 0);
94
+ const complex = words.reduce((sum, w) => sum + (countComplexWord(w) ? 1 : 0), 0);
95
+ return { raw, words, syllables, complex };
96
+ });
97
+ const totalSentences = sentenceTokens.length;
98
+ const totalWords = sentenceTokens.reduce((s, t) => s + t.words.length, 0);
99
+ const totalSyllables = sentenceTokens.reduce((s, t) => s + t.syllables, 0);
100
+ const complexWords = sentenceTokens.reduce((s, t) => s + t.complex, 0);
101
+ const avgSentenceLength = totalSentences > 0 ? totalWords / totalSentences : 0;
102
+ const avgSyllablesPerWord = totalWords > 0 ? totalSyllables / totalWords : 0;
103
+ return {
104
+ stats: {
105
+ total_sentences: totalSentences,
106
+ total_words: totalWords,
107
+ total_syllables: totalSyllables,
108
+ complex_words: complexWords,
109
+ avg_sentence_length: avgSentenceLength,
110
+ avg_syllables_per_word: avgSyllablesPerWord,
111
+ },
112
+ sentenceTokens,
113
+ };
114
+ }
115
+ export function fleschKincaidGrade(stats) {
116
+ if (stats.total_sentences === 0 || stats.total_words === 0)
117
+ return 0;
118
+ return (0.39 * stats.avg_sentence_length +
119
+ 11.8 * stats.avg_syllables_per_word -
120
+ 15.59);
121
+ }
122
+ export function fleschReadingEase(stats) {
123
+ if (stats.total_sentences === 0 || stats.total_words === 0)
124
+ return 0;
125
+ return (206.835 -
126
+ 1.015 * stats.avg_sentence_length -
127
+ 84.6 * stats.avg_syllables_per_word);
128
+ }
129
+ export function gunningFog(stats) {
130
+ if (stats.total_sentences === 0 || stats.total_words === 0)
131
+ return 0;
132
+ return (0.4 *
133
+ (stats.avg_sentence_length +
134
+ 100 * (stats.complex_words / stats.total_words)));
135
+ }
136
+ export function smog(stats) {
137
+ // SMOG was designed for ≥30 sentences; for shorter text we still
138
+ // apply the formula but note in the markdown that SMOG is unreliable
139
+ // below 30 sentences.
140
+ if (stats.total_sentences === 0)
141
+ return 0;
142
+ return (1.043 * Math.sqrt((stats.complex_words * 30) / stats.total_sentences) +
143
+ 3.1291);
144
+ }
145
+ export function computeReadabilityScores(stats) {
146
+ return {
147
+ flesch_kincaid_grade: round1(fleschKincaidGrade(stats)),
148
+ flesch_reading_ease: round1(fleschReadingEase(stats)),
149
+ gunning_fog: round1(gunningFog(stats)),
150
+ smog: round1(smog(stats)),
151
+ };
152
+ }
153
+ function round1(x) {
154
+ return Math.round(x * 10) / 10;
155
+ }
156
+ //# sourceMappingURL=readability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readability.js","sourceRoot":"","sources":["../../src/icf/readability.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Syllable counting heuristic.
3
+ *
4
+ * No dependency on a phonetic dictionary (CMU Pronouncing Dict etc.) —
5
+ * a deterministic rule-based counter that gets within ±1 of the truth
6
+ * for ~95% of English words. Sufficient for readability scoring where
7
+ * aggregate stats matter more than per-word precision.
8
+ *
9
+ * Algorithm (lowercase the word first):
10
+ * 1. Remove leading/trailing non-alpha, drop possessive.
11
+ * 2. Count vowel groups (one or more contiguous a/e/i/o/u/y).
12
+ * 3. Subtract one for a silent terminal "e" (but not "le" — table = 2).
13
+ * 4. Floor at 1 syllable per non-empty word.
14
+ *
15
+ * Special cases handled: words ending in "-le" preceded by a consonant
16
+ * count an extra syllable ("table"=2, "simple"=2). Words ending in "-ed"
17
+ * after a non-d/t consonant are silent ("walked"=1, but "added"=2).
18
+ */
19
+ export declare function countSyllables(rawWord: string): number;
20
+ export declare function countComplexWord(word: string): boolean;
21
+ //# sourceMappingURL=syllables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syllables.d.ts","sourceRoot":"","sources":["../../src/icf/syllables.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAwCtD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAkCtD"}