ceddcozum 0.1.8 → 0.2.0

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 (3) hide show
  1. package/README.md +22 -6
  2. package/dist/index.js +419 -170
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,9 +1,15 @@
1
- # ceddcozum
1
+ # ceddcozum / childmetrics
2
2
 
3
3
  Pediatric clinical calculators from the terminal. 32 tools for growth, neonatal, bone mineral, diabetes, puberty, organ size, blood pressure, and more.
4
4
 
5
5
  All calculations run locally — no API keys, no network requests, no patient data transmitted.
6
6
 
7
+ Also available as `childmetrics`:
8
+
9
+ ```bash
10
+ npx childmetrics --list
11
+ ```
12
+
7
13
  ## Install
8
14
 
9
15
  ```bash
@@ -12,17 +18,22 @@ npx ceddcozum --list
12
18
 
13
19
  # Or install globally
14
20
  npm install -g ceddcozum
21
+ # or
22
+ npm install -g childmetrics
15
23
  ```
16
24
 
17
25
  ## Usage
18
26
 
19
27
  ```bash
20
- # Run a calculator
21
- ceddcozum auxology --args '{"sex":"male","age":5.5,"height":110,"weight":19}'
28
+ # Simple key=value syntax
29
+ ceddcozum auxology sex=male age=5.5 height=110 weight=19
22
30
 
23
- # JSON output (for piping / LLM agents)
31
+ # JSON args (for LLM agents)
24
32
  ceddcozum auxology --args '{"sex":"male","age":5.5,"height":110}' --format json
25
33
 
34
+ # Growth hormone with brand name
35
+ ceddcozum growth-hormone weight=25 'usagePattern=her gün 2 mg' concentrationIU=30
36
+
26
37
  # View tool schema (OpenAI function format)
27
38
  ceddcozum auxology --schema
28
39
 
@@ -35,7 +46,8 @@ ceddcozum --schemas
35
46
  | Command | Description |
36
47
  |---------|-------------|
37
48
  | `ceddcozum --list` | List all 32 available tools |
38
- | `ceddcozum <tool> --args '{...}'` | Run a calculator with JSON args |
49
+ | `ceddcozum <tool> key=value ...` | Run a calculator |
50
+ | `ceddcozum <tool> --args '{...}'` | Run with JSON args |
39
51
  | `ceddcozum <tool> --schema` | Show tool schema (OpenAI format) |
40
52
  | `ceddcozum --schemas` | Dump all schemas |
41
53
  | `--format human` | Human-readable output (default) |
@@ -67,4 +79,8 @@ interface ToolResult {
67
79
 
68
80
  ## License
69
81
 
70
- MIT
82
+ Apache License 2.0 — see [LICENSE](./LICENSE).
83
+
84
+ Copyright 2026 ÇEDD (Çocuk Endokrinolojisi ve Diyabet Derneği) / TSPED (Turkish Society for Pediatric Endocrinology and Diabetes).
85
+
86
+ Attribution is required when using or redistributing this software. See [NOTICE](./NOTICE) for details.
package/dist/index.js CHANGED
@@ -12800,8 +12800,8 @@ var neonatalDatasetRegistry = {
12800
12800
  var syndromeDatasetRegistry = {
12801
12801
  [downDataset.id]: downDataset,
12802
12802
  ...turnerDatasets,
12803
- [noonanWittDataset.id]: noonanWittDataset,
12804
12803
  [noonanMalaquiasDataset.id]: noonanMalaquiasDataset,
12804
+ [noonanWittDataset.id]: noonanWittDataset,
12805
12805
  [noonanRankeDataset.id]: noonanRankeDataset,
12806
12806
  [praderWilliDataset.id]: praderWilliDataset,
12807
12807
  [praderWilliButlerDataset.id]: praderWilliButlerDataset,
@@ -13783,7 +13783,8 @@ var SUPPORTED_MEASUREMENTS = {
13783
13783
  ["williams" /* williams */]: ["height"],
13784
13784
  ["xlh" /* xlh */]: ["height"],
13785
13785
  ["osteogenesisImperfecta" /* osteogenesisImperfecta */]: ["height", "weight"],
13786
- ["mucopolysaccharidosis" /* mucopolysaccharidosis */]: ["height", "weight"]
13786
+ ["mucopolysaccharidosis" /* mucopolysaccharidosis */]: ["height", "weight"],
13787
+ ["klinefelter" /* klinefelter */]: ["height"]
13787
13788
  };
13788
13789
  function getDatasetDefinition(datasetId) {
13789
13790
  return syndromeDatasetRegistry[datasetId];
@@ -15499,22 +15500,6 @@ var igfbp3GuvenSds = [
15499
15500
  { ageRange: "17-18", sex: "f", values: [], mean: 5.19, sd: 1.3 }
15500
15501
  ];
15501
15502
 
15502
- // ../src/data/igf/tanner.ts
15503
- var femaleTannerData = [
15504
- { stage: "I", igf1: { low: 49, median: 159, high: 342 }, igfbp3: { low: 1.2, median: 3.6, high: 6.4, isIGFBP3: true } },
15505
- { stage: "II", igf1: { low: 115, median: 269, high: 428 }, igfbp3: { low: 2.8, median: 4.5, high: 6.9, isIGFBP3: true } },
15506
- { stage: "III", igf1: { low: 145, median: 412, high: 760 }, igfbp3: { low: 3.9, median: 5.3, high: 9.4, isIGFBP3: true } },
15507
- { stage: "IV", igf1: { low: 244, median: 504, high: 787 }, igfbp3: { low: 3.3, median: 5.9, high: 8.1, isIGFBP3: true } },
15508
- { stage: "V", igf1: { low: 143, median: 408, high: 859 }, igfbp3: { low: 2.7, median: 5.6, high: 9.1, isIGFBP3: true } }
15509
- ];
15510
- var maleTannerData = [
15511
- { stage: "I", igf1: { low: 63, median: 152, high: 279 }, igfbp3: { low: 1.4, median: 3.6, high: 5.2, isIGFBP3: true } },
15512
- { stage: "II", igf1: { low: 75, median: 190, high: 420 }, igfbp3: { low: 2.3, median: 3.9, high: 6.3, isIGFBP3: true } },
15513
- { stage: "III", igf1: { low: 94, median: 406, high: 765 }, igfbp3: { low: 3.1, median: 5.4, high: 8.9, isIGFBP3: true } },
15514
- { stage: "IV", igf1: { low: 192, median: 577, high: 861 }, igfbp3: { low: 3.7, median: 6.5, high: 8.7, isIGFBP3: true } },
15515
- { stage: "V", igf1: { low: 171, median: 422, high: 814 }, igfbp3: { low: 2.6, median: 5.2, high: 8.6, isIGFBP3: true } }
15516
- ];
15517
-
15518
15503
  // ../src/data/igf/igf1EcliaLms.ts
15519
15504
  var maleEcliaLms = [
15520
15505
  { age: 0.25, L: 0.3, M: 39.4, S: 0.5076 },
@@ -15569,8 +15554,6 @@ var femaleEcliaLms = [
15569
15554
  var igfSdsData = igf1ElmlingerSds;
15570
15555
  var igfbp3SdsDataElmlinger = igfbp3ElmlingerSds;
15571
15556
  var igfbp3SdsDataRiaBc = igfbp3RiaBcSds;
15572
- var femaleTannerData2 = femaleTannerData;
15573
- var maleTannerData2 = maleTannerData;
15574
15557
  var femaleLmsParams = femaleEcliaLms;
15575
15558
  var maleLmsParams = maleEcliaLms;
15576
15559
  function calculateIGFSds(input) {
@@ -15580,7 +15563,6 @@ function calculateIGFSds(input) {
15580
15563
  if (!input.igf1Value && !input.igfbp3Value) {
15581
15564
  return null;
15582
15565
  }
15583
- const tannerStage = input.tannerStage || "I";
15584
15566
  const result = {
15585
15567
  dataset: {
15586
15568
  igf1: input.igf1Database,
@@ -15594,22 +15576,14 @@ function calculateIGFSds(input) {
15594
15576
  if (input.igf1Value !== void 0 && input.igf1Value > 0) {
15595
15577
  const igf1Result = calculateIGF1SDS(input.age, input.sex, input.igf1Value, input.igf1Database);
15596
15578
  if (igf1Result) {
15597
- result.igf1 = {
15598
- ...igf1Result,
15599
- // Only Elmlinger dataset has Tanner stage data
15600
- tannerPercentile: input.igf1Database === "elmlinger" ? getTannerPercentile(input.sex, tannerStage, input.igf1Value, "igf1") : void 0
15601
- };
15579
+ result.igf1 = igf1Result;
15602
15580
  }
15603
15581
  }
15604
15582
  if (input.igfbp3Value !== void 0 && input.igfbp3Value > 0) {
15605
15583
  const convertedValue = input.igfbp3Unit === "ngPerML" ? input.igfbp3Value / 1e3 : input.igfbp3Value;
15606
15584
  const igfbp3Result = calculateIGFBP3SDS(input.age, input.sex, convertedValue, input.igfbp3Database);
15607
15585
  if (igfbp3Result) {
15608
- result.igfbp3 = {
15609
- ...igfbp3Result,
15610
- // Only Elmlinger dataset has Tanner stage data
15611
- tannerPercentile: input.igfbp3Database === "elmlinger" ? getTannerPercentile(input.sex, tannerStage, convertedValue, "igfbp3", input.igfbp3Database) : void 0
15612
- };
15586
+ result.igfbp3 = igfbp3Result;
15613
15587
  }
15614
15588
  }
15615
15589
  return result;
@@ -15674,7 +15648,9 @@ function calculateElmlingerSDS(age, sex, value, data) {
15674
15648
  return {
15675
15649
  sds,
15676
15650
  percentile,
15677
- interpretation: getInterpretation(sds)
15651
+ interpretation: getInterpretation(sds),
15652
+ median: values[2] ?? null
15653
+ // index 2 = Mean (50th percentile)
15678
15654
  };
15679
15655
  }
15680
15656
  function calculateECLIASDS(age, sex, value) {
@@ -15683,7 +15659,8 @@ function calculateECLIASDS(age, sex, value) {
15683
15659
  return {
15684
15660
  sds: null,
15685
15661
  percentile: null,
15686
- interpretation: null
15662
+ interpretation: null,
15663
+ median: null
15687
15664
  };
15688
15665
  }
15689
15666
  const sds = calculateLMSSDS(value, params.L, params.M, params.S);
@@ -15691,7 +15668,9 @@ function calculateECLIASDS(age, sex, value) {
15691
15668
  return {
15692
15669
  sds,
15693
15670
  percentile,
15694
- interpretation: getInterpretation(sds)
15671
+ interpretation: getInterpretation(sds),
15672
+ median: params.M
15673
+ // M parameter = 50th percentile in LMS
15695
15674
  };
15696
15675
  }
15697
15676
  function calculateRiaBcSDS(age, sex, value) {
@@ -15707,7 +15686,9 @@ function calculateRiaBcSDS(age, sex, value) {
15707
15686
  return {
15708
15687
  sds,
15709
15688
  percentile,
15710
- interpretation: getInterpretation(sds)
15689
+ interpretation: getInterpretation(sds),
15690
+ median: group.mean
15691
+ // mean ≈ 50th percentile for Mean-SD method
15711
15692
  };
15712
15693
  }
15713
15694
  function calculateGuvenSDS(age, sex, value, data) {
@@ -15723,7 +15704,9 @@ function calculateGuvenSDS(age, sex, value, data) {
15723
15704
  return {
15724
15705
  sds,
15725
15706
  percentile,
15726
- interpretation: getInterpretation(sds)
15707
+ interpretation: getInterpretation(sds),
15708
+ median: group.mean
15709
+ // mean ≈ 50th percentile for Mean-SD method
15727
15710
  };
15728
15711
  }
15729
15712
  function getLMSForAge(age, sex) {
@@ -15760,41 +15743,6 @@ function getLMSForAge(age, sex) {
15760
15743
  };
15761
15744
  }
15762
15745
  var calculateLMSSDS = (measurement, L, M, S) => calculateLMSBasedSDS(measurement, L, M, S, false);
15763
- function getTannerPercentile(sex, stage, value, type, database) {
15764
- const tannerData = sex === "female" ? femaleTannerData2 : maleTannerData2;
15765
- const stageData = tannerData.find((data) => data.stage === stage);
15766
- if (!stageData) {
15767
- return "N/A";
15768
- }
15769
- const range = type === "igf1" ? stageData.igf1 : stageData.igfbp3;
15770
- if (type === "igfbp3" && (database === "riaBc" || database === "guven")) {
15771
- return "N/A";
15772
- }
15773
- if (type === "igfbp3") {
15774
- const convertedValue = value * 1e3;
15775
- const convertedRange = {
15776
- low: range.low * 1e3,
15777
- median: range.median * 1e3,
15778
- high: range.high * 1e3
15779
- };
15780
- return calculateTannerPercentile(convertedRange, convertedValue);
15781
- }
15782
- return calculateTannerPercentile(range, value);
15783
- }
15784
- function calculateTannerPercentile(range, value) {
15785
- if (value < range.low) return "<2.5p";
15786
- if (value > range.high) return ">97.5p";
15787
- if (value === range.low) return "2.5p";
15788
- if (value === range.high) return "97.5p";
15789
- if (value === range.median) return "50p";
15790
- if (value > range.low && value < range.median) {
15791
- return "2.5-50p";
15792
- }
15793
- if (value > range.median && value < range.high) {
15794
- return "50-97.5p";
15795
- }
15796
- return "Unknown";
15797
- }
15798
15746
  function isAgeInRange(age, range) {
15799
15747
  const components = range.split(/[–-]/).map((s) => s.trim());
15800
15748
  const startAge = parseFloat(components[0]);
@@ -16943,7 +16891,9 @@ function calculatePhosphateSds(input) {
16943
16891
  sds: null,
16944
16892
  percentile: null,
16945
16893
  phosphateMmol,
16946
- phosphateMgDl
16894
+ phosphateMgDl,
16895
+ p2_5Mmol: null,
16896
+ p97_5Mmol: null
16947
16897
  };
16948
16898
  }
16949
16899
  const sds = calculateLMSBasedSDS(
@@ -16955,11 +16905,17 @@ function calculatePhosphateSds(input) {
16955
16905
  // no SD23 correction needed for phosphate
16956
16906
  );
16957
16907
  const percentile = sdsToPercentile(sds);
16908
+ const Z_2_5 = -1.96;
16909
+ const Z_97_5 = 1.96;
16910
+ const p2_5Mmol = lmsToValue(lmsParams.L, lmsParams.M, lmsParams.S, Z_2_5);
16911
+ const p97_5Mmol = lmsToValue(lmsParams.L, lmsParams.M, lmsParams.S, Z_97_5);
16958
16912
  return {
16959
16913
  sds,
16960
16914
  percentile,
16961
16915
  phosphateMmol,
16962
- phosphateMgDl
16916
+ phosphateMgDl,
16917
+ p2_5Mmol,
16918
+ p97_5Mmol
16963
16919
  };
16964
16920
  }
16965
16921
 
@@ -17224,6 +17180,8 @@ function getInterpretation2(ratio) {
17224
17180
  };
17225
17181
  }
17226
17182
  }
17183
+ var INSULIN_UNITS = ["uIU/mL", "mIU/L", "pmol/L"];
17184
+ var CPEPTIDE_UNITS = ["ng/mL", "nmol/L", "pmol/L"];
17227
17185
 
17228
17186
  // ../src/lib/calculators/hcg-test.ts
17229
17187
  var CONVERSION_FACTORS = {
@@ -24248,6 +24206,170 @@ function calculateOsmolality(input) {
24248
24206
  }
24249
24207
  }
24250
24208
 
24209
+ // ../src/lib/processors/DateAgeConverter.ts
24210
+ function calculateAge2(input) {
24211
+ const errors = [];
24212
+ let birthDate = null;
24213
+ if (input.birthDate instanceof Date) {
24214
+ birthDate = input.birthDate;
24215
+ } else if (typeof input.birthDate === "string") {
24216
+ birthDate = parseDate(input.birthDate);
24217
+ }
24218
+ if (!birthDate || isNaN(birthDate.getTime())) {
24219
+ errors.push("Invalid birth date");
24220
+ }
24221
+ const measurementDateInput = input.measurementDate || input.targetDate || /* @__PURE__ */ new Date();
24222
+ let measurementDate = null;
24223
+ if (measurementDateInput instanceof Date) {
24224
+ measurementDate = measurementDateInput;
24225
+ } else if (typeof measurementDateInput === "string") {
24226
+ measurementDate = parseDate(measurementDateInput);
24227
+ }
24228
+ if (!measurementDate || isNaN(measurementDate.getTime())) {
24229
+ errors.push("Invalid measurement date");
24230
+ }
24231
+ if (birthDate && measurementDate) {
24232
+ if (measurementDate < birthDate) {
24233
+ errors.push("Measurement date cannot be before birth date");
24234
+ }
24235
+ if (birthDate.getFullYear() < 1900) {
24236
+ errors.push("Birth date is too far in the past");
24237
+ }
24238
+ if (measurementDate.getFullYear() > (/* @__PURE__ */ new Date()).getFullYear() + 1) {
24239
+ errors.push("Measurement date is too far in the future");
24240
+ }
24241
+ }
24242
+ if (errors.length === 0 && birthDate && measurementDate) {
24243
+ const age = calculateAgeBreakdownFromDates(birthDate, measurementDate);
24244
+ return {
24245
+ isValid: true,
24246
+ age,
24247
+ errors: [],
24248
+ parsedBirthDate: birthDate,
24249
+ parsedMeasurementDate: measurementDate
24250
+ };
24251
+ }
24252
+ return {
24253
+ isValid: false,
24254
+ age: null,
24255
+ errors,
24256
+ parsedBirthDate: birthDate,
24257
+ parsedMeasurementDate: measurementDate
24258
+ };
24259
+ }
24260
+ function calculateAgeBreakdownFromDates(birthDate, measurementDate) {
24261
+ const totalDays = Math.floor((measurementDate.getTime() - birthDate.getTime()) / (1e3 * 60 * 60 * 24));
24262
+ if (totalDays < 0) {
24263
+ const decimalYears2 = totalDays / 365.25;
24264
+ return {
24265
+ years: 0,
24266
+ months: 0,
24267
+ days: totalDays,
24268
+ totalDays,
24269
+ decimalYears: decimalYears2,
24270
+ decimalMonths: decimalYears2 * 12
24271
+ };
24272
+ }
24273
+ const { years, months, days } = calculateCalendarAge(birthDate, measurementDate);
24274
+ const decimalYears = convertYMDToDecimalYears(years, months, days);
24275
+ const decimalMonths = decimalYears * 12;
24276
+ return {
24277
+ years,
24278
+ months,
24279
+ days,
24280
+ totalDays,
24281
+ decimalYears,
24282
+ decimalMonths
24283
+ };
24284
+ }
24285
+ function calculateCalendarAge(startDate, endDate) {
24286
+ let years = endDate.getFullYear() - startDate.getFullYear();
24287
+ let months = endDate.getMonth() - startDate.getMonth();
24288
+ if (endDate.getDate() < startDate.getDate()) {
24289
+ months--;
24290
+ }
24291
+ if (months < 0) {
24292
+ years--;
24293
+ months += 12;
24294
+ }
24295
+ const refDate = new Date(startDate.getFullYear() + years, startDate.getMonth() + months, 1);
24296
+ const daysInRefMonth = new Date(refDate.getFullYear(), refDate.getMonth() + 1, 0).getDate();
24297
+ const clampedDay = Math.min(startDate.getDate(), daysInRefMonth);
24298
+ refDate.setDate(clampedDay);
24299
+ const days = Math.round((endDate.getTime() - refDate.getTime()) / (1e3 * 60 * 60 * 24));
24300
+ return { years, months, days };
24301
+ }
24302
+ function convertYMDToDecimalYears(years, months, days) {
24303
+ const monthsInYears = months / 12;
24304
+ const daysInYears = days / 365.25;
24305
+ return years + monthsInYears + daysInYears;
24306
+ }
24307
+ function parseDate(dateString) {
24308
+ if (!dateString || dateString.trim() === "") {
24309
+ return null;
24310
+ }
24311
+ const cleaned = dateString.trim();
24312
+ let date = new Date(cleaned);
24313
+ if (!isNaN(date.getTime())) {
24314
+ return date;
24315
+ }
24316
+ const formats = [
24317
+ // DD.MM.YYYY (Turkish)
24318
+ /^(\d{1,2})\.(\d{1,2})\.(\d{4})$/,
24319
+ // DD/MM/YYYY (European)
24320
+ /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/,
24321
+ // DD-MM-YYYY
24322
+ /^(\d{1,2})-(\d{1,2})-(\d{4})$/,
24323
+ // MM/DD/YYYY (American)
24324
+ /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/,
24325
+ // MM-DD-YYYY
24326
+ /^(\d{1,2})-(\d{1,2})-(\d{4})$/
24327
+ ];
24328
+ const ddmmMatch = cleaned.match(/^(\d{1,2})\.(\d{1,2})\.(\d{4})$/);
24329
+ if (ddmmMatch) {
24330
+ const [, day, month, year] = ddmmMatch;
24331
+ date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
24332
+ if (!isNaN(date.getTime())) return date;
24333
+ }
24334
+ const ddmmSlashMatch = cleaned.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
24335
+ if (ddmmSlashMatch) {
24336
+ const [, day, month, year] = ddmmSlashMatch;
24337
+ date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
24338
+ if (!isNaN(date.getTime())) return date;
24339
+ }
24340
+ return null;
24341
+ }
24342
+
24343
+ // ../src/lib/calculators/cnpAnalogue.ts
24344
+ var VOSORITIDE_DOSE_TABLE = [
24345
+ { minWeight: 3, maxWeight: 3, vialMg: 0.4, doseMg: 0.096, volumeMl: 0.12, reconcentration: 0.8 },
24346
+ { minWeight: 4, maxWeight: 4, vialMg: 0.4, doseMg: 0.12, volumeMl: 0.15, reconcentration: 0.8 },
24347
+ { minWeight: 5, maxWeight: 5, vialMg: 0.4, doseMg: 0.16, volumeMl: 0.2, reconcentration: 0.8 },
24348
+ { minWeight: 6, maxWeight: 7, vialMg: 0.4, doseMg: 0.2, volumeMl: 0.25, reconcentration: 0.8 },
24349
+ { minWeight: 8, maxWeight: 11, vialMg: 0.4, doseMg: 0.24, volumeMl: 0.3, reconcentration: 0.8 },
24350
+ { minWeight: 12, maxWeight: 16, vialMg: 0.56, doseMg: 0.28, volumeMl: 0.35, reconcentration: 0.8 },
24351
+ { minWeight: 17, maxWeight: 21, vialMg: 0.56, doseMg: 0.32, volumeMl: 0.4, reconcentration: 0.8 },
24352
+ { minWeight: 22, maxWeight: 32, vialMg: 0.56, doseMg: 0.4, volumeMl: 0.5, reconcentration: 0.8 },
24353
+ { minWeight: 33, maxWeight: 43, vialMg: 1.2, doseMg: 0.5, volumeMl: 0.25, reconcentration: 2 },
24354
+ { minWeight: 44, maxWeight: 59, vialMg: 1.2, doseMg: 0.6, volumeMl: 0.3, reconcentration: 2 },
24355
+ { minWeight: 60, maxWeight: 89, vialMg: 1.2, doseMg: 0.7, volumeMl: 0.35, reconcentration: 2 },
24356
+ { minWeight: 90, maxWeight: Infinity, vialMg: 1.2, doseMg: 0.8, volumeMl: 0.4, reconcentration: 2 }
24357
+ ];
24358
+ var NAVEPEGRITIDE_DOSE_MCG_KG_WEEK = 100;
24359
+ function lookupVosoritideDose(weightKg) {
24360
+ const rounded = Math.round(weightKg);
24361
+ if (rounded < 3) return null;
24362
+ return VOSORITIDE_DOSE_TABLE.find((e) => rounded >= e.minWeight && rounded <= e.maxWeight) ?? null;
24363
+ }
24364
+ function mcgPerKgDayVosoritide(entry, weightKgRounded) {
24365
+ return entry.doseMg * 1e3 / weightKgRounded;
24366
+ }
24367
+ function computeNavepegritideWeeklyDose(weightKg) {
24368
+ if (!weightKg || !isFinite(weightKg) || weightKg <= 0) return null;
24369
+ const doseMcg = NAVEPEGRITIDE_DOSE_MCG_KG_WEEK * weightKg;
24370
+ return { doseMcg, doseMg: doseMcg / 1e3 };
24371
+ }
24372
+
24251
24373
  // ../src/lib/chat/toolExecutor.ts
24252
24374
  function severity(sds) {
24253
24375
  if (sds === null) return "normal";
@@ -24370,6 +24492,11 @@ function buildInputEcho(name, args) {
24370
24492
  if (gw != null) parts.push(`${gw}w${gd ? `+${gd}d` : ""}`);
24371
24493
  return { label: "", value: parts.join(" \xB7 "), tag: "echo" };
24372
24494
  }
24495
+ case "cnp-analogue": {
24496
+ if (args.mode) parts.push(String(args.mode));
24497
+ if (args.weightKg != null) parts.push(`${args.weightKg} kg`);
24498
+ return { label: "", value: parts.join(" \xB7 "), tag: "echo" };
24499
+ }
24373
24500
  }
24374
24501
  const age = args.age != null ? Number(args.age) : args.ageYears != null ? Number(args.ageYears) : NaN;
24375
24502
  if (!isNaN(age)) {
@@ -24416,18 +24543,6 @@ function formatDateTR(d) {
24416
24543
  const mm = String(d.getMonth() + 1).padStart(2, "0");
24417
24544
  return `${dd}/${mm}/${d.getFullYear()}`;
24418
24545
  }
24419
- function ageBreakdown(birth, ref) {
24420
- let years = ref.getFullYear() - birth.getFullYear();
24421
- let months = ref.getMonth() - birth.getMonth();
24422
- if (ref.getDate() < birth.getDate()) months--;
24423
- if (months < 0) {
24424
- years--;
24425
- months += 12;
24426
- }
24427
- const diffMs = ref.getTime() - birth.getTime();
24428
- const totalDecimal = diffMs / (365.25 * 24 * 60 * 60 * 1e3);
24429
- return { years, months, totalDecimal };
24430
- }
24431
24546
  function executeDecimalAge(args) {
24432
24547
  const birthDatesStr = String(args.birthDates || "");
24433
24548
  const refDateStr = args.referenceDate ? String(args.referenceDate) : (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -24456,8 +24571,13 @@ function executeDecimalAge(args) {
24456
24571
  results.push({ label: formatDateTR(bd), value: "Hen\xFCz do\u011Fmad\u0131", severity: "severe" });
24457
24572
  continue;
24458
24573
  }
24459
- const { years, months, totalDecimal } = ageBreakdown(bd, refDate);
24460
- const decimalStr = totalDecimal.toFixed(2).replace(".", ",");
24574
+ const ageResult = calculateAge2({ birthDate: bd, measurementDate: refDate });
24575
+ if (!ageResult.isValid || !ageResult.age) {
24576
+ results.push({ label: `#${i + 1}`, value: `Hesaplanamad\u0131: ${birthDates[i]}`, severity: "severe" });
24577
+ continue;
24578
+ }
24579
+ const { years, months, decimalYears } = ageResult.age;
24580
+ const decimalStr = decimalYears.toFixed(2).replace(".", ",");
24461
24581
  if (isBatch) {
24462
24582
  results.push({
24463
24583
  label: formatDateTR(bd),
@@ -24467,6 +24587,14 @@ function executeDecimalAge(args) {
24467
24587
  results.push({ label: "Do\u011Fum tarihi", value: formatDateTR(bd) });
24468
24588
  results.push({ label: "Ya\u015F", value: `${years} y\u0131l ${months} ay` });
24469
24589
  results.push({ label: "Desimal ya\u015F", value: `${decimalStr} y\u0131l` });
24590
+ const roundedAge = parseFloat(decimalYears.toFixed(2));
24591
+ results.push({
24592
+ label: "",
24593
+ value: "Calculate growth SDS with this age",
24594
+ tag: "alt",
24595
+ altToolName: "auxology",
24596
+ altArgs: { age: roundedAge, ...sex ? { sex } : {} }
24597
+ });
24470
24598
  }
24471
24599
  }
24472
24600
  return results;
@@ -24502,6 +24630,9 @@ function executeTool(name, args) {
24502
24630
  case "body-ratios":
24503
24631
  results = executeBodyRatios(args);
24504
24632
  break;
24633
+ case "cnp-analogue":
24634
+ results = executeCnpAnalogue(args);
24635
+ break;
24505
24636
  case "growth-hormone":
24506
24637
  results = executeGrowthHormone(args);
24507
24638
  break;
@@ -24591,7 +24722,7 @@ function executeTool(name, args) {
24591
24722
  return [{ label: "Info", value: `Calculator "${name}" is not available in the chat. Please use it from the Tools panel.` }];
24592
24723
  }
24593
24724
  if (results.length > 0 && results[0].label === "Error") return results;
24594
- const ECHO_TOOLS = /* @__PURE__ */ new Set(["growth-hormone"]);
24725
+ const ECHO_TOOLS = /* @__PURE__ */ new Set(["growth-hormone", "cnp-analogue"]);
24595
24726
  if (ECHO_TOOLS.has(name)) {
24596
24727
  const echo = buildInputEcho(name, args);
24597
24728
  if (echo.value && echo.value !== "\u2014") {
@@ -25022,9 +25153,6 @@ function executeIGFSds(args) {
25022
25153
  percentile: result.igf1.percentile,
25023
25154
  severity: result.igf1.sds != null ? severity(result.igf1.sds) : "normal"
25024
25155
  });
25025
- if (result.igf1.tannerPercentile) {
25026
- results.push({ label: "IGF-1 Tanner", value: result.igf1.tannerPercentile });
25027
- }
25028
25156
  }
25029
25157
  if (result.igfbp3) {
25030
25158
  results.push({
@@ -25116,6 +25244,44 @@ function executeBodyRatios(args) {
25116
25244
  }
25117
25245
  return results.length > 1 ? results : [patientRow({ age, sex }), { label: "Note", value: "Provide sittingHeight or armSpan" }];
25118
25246
  }
25247
+ function executeCnpAnalogue(args) {
25248
+ const mode = args.mode === "weekly" || args.mode === "daily" ? args.mode : null;
25249
+ const weightKg = Number(args.weightKg);
25250
+ if (!mode || !isFinite(weightKg) || weightKg <= 0) {
25251
+ return [{ label: "Error", value: 'Provide mode ("daily" or "weekly") and weightKg > 0.', severity: "severe" }];
25252
+ }
25253
+ if (mode === "weekly") {
25254
+ const wk = computeNavepegritideWeeklyDose(weightKg);
25255
+ if (!wk) {
25256
+ return [{ label: "Error", value: "Invalid weight for weekly dose.", severity: "severe" }];
25257
+ }
25258
+ return [
25259
+ { label: "Agent", value: "Navepegritide (weekly)" },
25260
+ { label: "Weekly dose", value: wk.doseMg.toFixed(3), unit: "mg" },
25261
+ { label: "Dose intensity", value: String(NAVEPEGRITIDE_DOSE_MCG_KG_WEEK), unit: "mcg/kg/week" }
25262
+ ];
25263
+ }
25264
+ const rounded = Math.round(weightKg);
25265
+ const row = lookupVosoritideDose(weightKg);
25266
+ if (!row) {
25267
+ return [
25268
+ {
25269
+ label: "Error",
25270
+ value: "Weight out of range for vosoritide table (minimum 3 kg after rounding to nearest kg).",
25271
+ severity: "severe"
25272
+ }
25273
+ ];
25274
+ }
25275
+ const mcgKg = mcgPerKgDayVosoritide(row, rounded);
25276
+ return [
25277
+ { label: "Agent", value: "Vosoritide (daily)" },
25278
+ { label: "Dose", value: row.doseMg.toFixed(3), unit: "mg" },
25279
+ { label: "Approx. intensity", value: mcgKg.toFixed(1), unit: "mcg/kg/day" },
25280
+ { label: "Vial", value: String(row.vialMg), unit: "mg" },
25281
+ { label: "Concentration", value: row.reconcentration.toFixed(1), unit: "mg/mL" },
25282
+ { label: "Injection volume", value: row.volumeMl.toFixed(2), unit: "mL" }
25283
+ ];
25284
+ }
25119
25285
  function parseUsagePattern(raw) {
25120
25286
  const compactNormalized = raw.replace(/:(\d+),(\d+)/g, ":$1.$2");
25121
25287
  const compactParts = compactNormalized.split(/[,;]/).map((p) => p.trim());
@@ -25152,6 +25318,14 @@ function parseUsagePattern(raw) {
25152
25318
  }
25153
25319
  }
25154
25320
  if (revEntries.length > 0) return revEntries;
25321
+ const dailyRegex = /(?:her\s*g[üu]n(?:de)?|herg[üu]n|g[üu]n(?:de|l[üu]k)|daily|every\s*day)\s*:?\s*([\d]+[.,]?\d*)\s*(?:mg)?/i;
25322
+ const dailyMatch = raw.match(dailyRegex);
25323
+ if (dailyMatch) {
25324
+ const doseMg = parseFloat(dailyMatch[1].replace(",", "."));
25325
+ if (!Number.isNaN(doseMg) && doseMg >= 0) {
25326
+ return [{ days: 7, doseMg }];
25327
+ }
25328
+ }
25155
25329
  return [];
25156
25330
  }
25157
25331
  function executeGrowthHormone(args) {
@@ -25277,12 +25451,25 @@ function executeCorrectedAge(args) {
25277
25451
  return rows;
25278
25452
  }
25279
25453
  function executeBmdSds(args) {
25454
+ const siteRaw = String(args.site);
25455
+ const validSites = ["spine", "neck", "tblh"];
25456
+ if (!validSites.includes(siteRaw)) {
25457
+ return [
25458
+ {
25459
+ label: "Error",
25460
+ value: `BMD SDS supports sites: ${validSites.join(", ")}. Retry with one of those.`,
25461
+ severity: "severe"
25462
+ }
25463
+ ];
25464
+ }
25465
+ const isTblh = siteRaw === "tblh";
25280
25466
  const result = calculateBmdSds({
25281
25467
  value: Number(args.value),
25282
25468
  age: Number(args.age),
25283
25469
  sex: String(args.sex),
25284
- site: String(args.site),
25285
- measure: "bmd"
25470
+ site: isTblh ? "spine" : siteRaw,
25471
+ measure: "aBMD",
25472
+ ...isTblh && { dataset: "tblh" }
25286
25473
  });
25287
25474
  const ageVal = Number(args.age);
25288
25475
  const sexVal = String(args.sex);
@@ -26031,6 +26218,44 @@ function executeCorrectedSodium(args) {
26031
26218
  ];
26032
26219
  }
26033
26220
 
26221
+ // ../src/lib/chat/chatToolIdMapping.ts
26222
+ var CHAT_FUNCTION_NAME_ALIASES = {
26223
+ hba1c: "hba1c-estimation",
26224
+ "igf-lagh-adjustment": "igf-sds-lagh-adjustment"
26225
+ };
26226
+ var CHAT_NAME_CATEGORY_OVERRIDE = {
26227
+ "decimal-age": "misc"
26228
+ };
26229
+ function chatFunctionToMetadataId(chatFunctionName) {
26230
+ return CHAT_FUNCTION_NAME_ALIASES[chatFunctionName] ?? chatFunctionName;
26231
+ }
26232
+ function chatFunctionCategoryOverride(chatFunctionName) {
26233
+ return CHAT_NAME_CATEGORY_OVERRIDE[chatFunctionName];
26234
+ }
26235
+
26236
+ // ../src/lib/hiddenToolIds.ts
26237
+ var HIDDEN_TOOL_IDS = [
26238
+ "steroid-tapering",
26239
+ "periop-adrenal",
26240
+ "preop-diabetes",
26241
+ "dka-fluid",
26242
+ "gri",
26243
+ "basal-bolus",
26244
+ "meal-bolus",
26245
+ "custom-iv-fluid",
26246
+ "cp-height",
26247
+ "oral-phosphorus",
26248
+ "corrected-calcium",
26249
+ "gfr",
26250
+ "leptin-sds"
26251
+ ];
26252
+ function isToolVisible(toolId, isUnlocked) {
26253
+ if (!HIDDEN_TOOL_IDS.includes(toolId)) {
26254
+ return true;
26255
+ }
26256
+ return isUnlocked;
26257
+ }
26258
+
26034
26259
  // ../src/lib/chat/toolSchemas.ts
26035
26260
  var toolSchemas = [
26036
26261
  // ╔══════════════════════════════════════════╗
@@ -26040,7 +26265,7 @@ var toolSchemas = [
26040
26265
  type: "function",
26041
26266
  function: {
26042
26267
  name: "decimal-age",
26043
- description: "Calculate decimal age(s) from birth date(s). Accepts one or multiple comma-separated birth dates. Returns decimal age in years for each. Use when user ONLY wants age calculation without growth parameters.",
26268
+ description: "Calculate decimal age(s) from birth date(s). Accepts one or multiple comma-separated birth dates. Returns decimal age in years for each.",
26044
26269
  parameters: {
26045
26270
  type: "object",
26046
26271
  properties: {
@@ -26068,8 +26293,8 @@ var toolSchemas = [
26068
26293
  height: { type: "number", description: "Height in cm [optional]" },
26069
26294
  weight: { type: "number", description: "Weight in kg [optional]" },
26070
26295
  headCircumference: { type: "number", description: "Head circumference in cm [optional]" },
26071
- dataset: { type: "string", description: 'Standard dataset. Use exact IDs from auxology dropdown: neyzi, who, cdc-extended-2022. "CDC" means cdc-extended-2022. [default: neyzi]', enum: ["neyzi", "who", "cdc-extended-2022"] },
26072
- syndrome: { type: "string", description: 'Condition dataset ID. Turner: turner-darendeliler, turner-ranke, turner-lyon, turner-rongen, turner-isojima. Use "none" for standard growth. When user says "standart neyzi" or "standard neyzi", use syndrome: none and dataset: neyzi.', enum: ["none", ...auxologySyndromeDatasetIds] }
26296
+ dataset: { type: "string", description: "Standard dataset [default: neyzi]", enum: ["neyzi", "who", "cdc-extended-2022"] },
26297
+ syndrome: { type: "string", description: 'Condition dataset ID. Use "none" for standard growth.', enum: ["none", ...auxologySyndromeDatasetIds] }
26073
26298
  },
26074
26299
  required: ["sex", "age"]
26075
26300
  }
@@ -26084,13 +26309,13 @@ var toolSchemas = [
26084
26309
  type: "object",
26085
26310
  properties: {
26086
26311
  sex: { type: "string", description: "Sex", enum: ["male", "female"] },
26087
- currentAge: { type: "number", description: "Age at last measurement in years (REQUIRED \u2014 do not assume from interval)" },
26312
+ currentAge: { type: "number", description: "Age at last measurement in years" },
26088
26313
  previousAge: { type: "number", description: "Previous age in years [optional if intervalMonths given]" },
26089
26314
  currentHeight: { type: "number", description: "Current height in cm [optional if heightChange given]" },
26090
26315
  previousHeight: { type: "number", description: "Previous height in cm [optional if heightChange given]" },
26091
26316
  heightChange: { type: "number", description: "Height gained in cm [optional, alternative to two heights]" },
26092
26317
  intervalMonths: { type: "number", description: "Interval between measurements in months [optional, alternative to two ages]" },
26093
- dataset: { type: "string", description: "Height-velocity dataset (different from auxology): who (age <5), neyzi (8\u201315 boys), kelly (5.5\u201318), baumgartner (0\u201318), tanner (0\u201319), achondroplasia. [default: who for age<5, neyzi for older]", enum: ["neyzi", "kelly", "baumgartner", "tanner", "who", "achondroplasia"] }
26318
+ dataset: { type: "string", description: "Reference dataset [default: auto by age]", enum: ["neyzi", "kelly", "baumgartner", "tanner", "who", "achondroplasia"] }
26094
26319
  },
26095
26320
  required: ["sex", "currentAge"]
26096
26321
  }
@@ -26100,15 +26325,15 @@ var toolSchemas = [
26100
26325
  type: "function",
26101
26326
  function: {
26102
26327
  name: "predicted-height",
26103
- description: 'Three modes: (1) TARGET HEIGHT from parental heights: motherHeight + fatherHeight + sex. (2) Bayley-Pinneau (default): height + boneAge + sex. (3) RWT (Roche-Wainer-Thissen): height + weight + boneAge + chronologicalAge + motherHeight + fatherHeight + sex. User says "rwt/RWT" \u2192 method=rwt.',
26328
+ description: "Predicted adult height. Three modes: (1) Target height from parental heights. (2) Bayley-Pinneau (default): height + bone age. (3) RWT (Roche-Wainer-Thissen): height + weight + bone age + parents.",
26104
26329
  parameters: {
26105
26330
  type: "object",
26106
26331
  properties: {
26107
26332
  sex: { type: "string", description: "Sex", enum: ["male", "female"] },
26108
- method: { type: "string", description: "PAH method. bayley-pinneau (default, aka BP/PAH/\xD6EB/pah). rwt = Roche-Wainer-Thissen (needs weight + parents + chronologicalAge).", enum: ["bayley-pinneau", "rwt"] },
26109
- height: { type: "number", description: "Current height in cm [for PAH methods, NOT for target height from parents]" },
26110
- weight: { type: "number", description: "Weight in kg [required for RWT only]" },
26111
- boneAge: { type: "number", description: "Bone age in years [for PAH methods]" },
26333
+ method: { type: "string", description: "PAH method [default: bayley-pinneau]", enum: ["bayley-pinneau", "rwt"] },
26334
+ height: { type: "number", description: "Current height in cm [optional]" },
26335
+ weight: { type: "number", description: "Weight in kg [optional, required for RWT]" },
26336
+ boneAge: { type: "number", description: "Bone age in years [optional]" },
26112
26337
  chronologicalAge: { type: "number", description: "Chronological age in years" },
26113
26338
  motherHeight: { type: "number", description: "Mother height in cm" },
26114
26339
  fatherHeight: { type: "number", description: "Father height in cm" }
@@ -26130,8 +26355,8 @@ var toolSchemas = [
26130
26355
  igf1Value: { type: "number", description: "IGF-1 concentration in ng/mL [optional]" },
26131
26356
  igfbp3Value: { type: "number", description: "IGFBP-3 concentration [optional]" },
26132
26357
  igfbp3Unit: { type: "string", description: "IGFBP-3 unit [default: mcg/mL]", enum: ["mcg/mL", "ng/mL"] },
26133
- igf1Database: { type: "string", description: "IGF-1 reference kit. elmlinger = CLIA / IMMULITE / Siemens Immulite (default). eclia = ECLIA / Roche / Elecsys / Cobas. guven = G\xFCven (Turkish reference). When user mentions a kit/assay name, match to the correct enum.", enum: ["elmlinger", "eclia", "guven"] },
26134
- igfbp3Database: { type: "string", description: "IGFBP-3 reference kit. elmlinger = CLIA / IMMULITE / Siemens Immulite (default). ria-bc = RIA / Beckman Coulter / RIA-BC. guven = G\xFCven (Turkish reference). When user mentions a kit/assay name, match to the correct enum.", enum: ["elmlinger", "ria-bc", "guven"] }
26358
+ igf1Database: { type: "string", description: "IGF-1 reference kit. elmlinger = CLIA/IMMULITE (default). eclia = ECLIA/Roche. guven = G\xFCven.", enum: ["elmlinger", "eclia", "guven"] },
26359
+ igfbp3Database: { type: "string", description: "IGFBP-3 reference kit. elmlinger = CLIA/IMMULITE (default). ria-bc = RIA/Beckman Coulter. guven = G\xFCven.", enum: ["elmlinger", "ria-bc", "guven"] }
26135
26360
  },
26136
26361
  required: ["age", "sex"]
26137
26362
  }
@@ -26148,24 +26373,39 @@ var toolSchemas = [
26148
26373
  sex: { type: "string", description: "Sex", enum: ["male", "female"] },
26149
26374
  age: { type: "number", description: "Age in years" },
26150
26375
  height: { type: "number", description: "Standing height in cm" },
26151
- sittingHeight: { type: "number", description: "Sitting height in cm [optional]. Turkish: ob, oturma boyu. NOT kula\xE7/kulac (that is armSpan)." },
26152
- armSpan: { type: "number", description: "Arm span in cm [optional]. Turkish: kulac, kulac boyu, kula\xE7 boyu = arm span." }
26376
+ sittingHeight: { type: "number", description: "Sitting height in cm [optional]" },
26377
+ armSpan: { type: "number", description: "Arm span in cm [optional]" }
26153
26378
  },
26154
26379
  required: ["sex", "age", "height"]
26155
26380
  }
26156
26381
  }
26157
26382
  },
26383
+ {
26384
+ type: "function",
26385
+ function: {
26386
+ name: "cnp-analogue",
26387
+ description: "CNP analogue dosing for achondroplasia. daily = vosoritide (label weight-band table, ~15 mcg/kg/day). weekly = navepegritide 100 mcg/kg/week SC.",
26388
+ parameters: {
26389
+ type: "object",
26390
+ properties: {
26391
+ mode: { type: "string", description: "daily (vosoritide) or weekly (navepegritide)", enum: ["daily", "weekly"] },
26392
+ weightKg: { type: "number", description: "Body weight in kg (daily table uses weight rounded to nearest kg)" }
26393
+ },
26394
+ required: ["mode", "weightKg"]
26395
+ }
26396
+ }
26397
+ },
26158
26398
  {
26159
26399
  type: "function",
26160
26400
  function: {
26161
26401
  name: "growth-hormone",
26162
- description: 'Growth hormone dosing analysis. Extract weight, weekly schedule as "days:doseMg,days:doseMg" (days 1-7, supports off-day schedules), and optional concentration in IU/ml.',
26402
+ description: 'Growth hormone dosing analysis. Weekly schedule as "days:doseMg,days:doseMg" (days 1-7, supports off-day schedules).',
26163
26403
  parameters: {
26164
26404
  type: "object",
26165
26405
  properties: {
26166
- weight: { type: "number", description: "Weight in kg (extract from user text)" },
26167
- usagePattern: { type: "string", description: 'Compact format "days:doseMg,days:doseMg". Extract from user text and convert. Days can sum to 1-7 (supports off-day schedules like 6 days on / 1 day off).' },
26168
- concentrationIU: { type: "number", description: 'Preparation concentration in IU per ml [optional]. Extract from "15 IU/ml", "Genotropin 5 mg", "Norditropin 10 mg" (30), "Omnitrope 6 mg" (18). Common: 15, 16, 18, 30, 36, 45, 60, 72, 90.' }
26406
+ weight: { type: "number", description: "Weight in kg" },
26407
+ usagePattern: { type: "string", description: 'Compact format "days:doseMg,days:doseMg". Days can sum to 1-7 (supports off-day schedules).' },
26408
+ concentrationIU: { type: "number", description: "Preparation concentration in IU (mg\xD73) [optional, for box calculation]" }
26169
26409
  },
26170
26410
  required: ["weight", "usagePattern"]
26171
26411
  }
@@ -26175,7 +26415,7 @@ var toolSchemas = [
26175
26415
  type: "function",
26176
26416
  function: {
26177
26417
  name: "igf-lagh-adjustment",
26178
- description: "Adjust IGF-1 value for long-acting growth hormone (somatrogon). Corrects measured IGF-1 based on hours after dose. NOT for growth hormone dose calculation \u2014 use growth-hormone tool for that.",
26418
+ description: "Adjust IGF-1 value for long-acting growth hormone (somatrogon). Corrects measured IGF-1 based on hours after dose.",
26179
26419
  parameters: {
26180
26420
  type: "object",
26181
26421
  properties: {
@@ -26193,7 +26433,7 @@ var toolSchemas = [
26193
26433
  type: "function",
26194
26434
  function: {
26195
26435
  name: "neonatal-parameters",
26196
- description: "Calculate SDS/percentile for neonatal birth parameters (weight, length, head circumference) by gestational week. Requires sex \u2014 do NOT assume male if user omits it; ask. Uses Fenton (2025) or Kurto\u011Flu (2012) reference data.",
26436
+ description: "Calculate SDS/percentile for neonatal birth parameters (weight, length, head circumference) by gestational week. Uses Fenton (2025) or Kurto\u011Flu (2012) reference data.",
26197
26437
  parameters: {
26198
26438
  type: "object",
26199
26439
  properties: {
@@ -26240,7 +26480,7 @@ var toolSchemas = [
26240
26480
  sex: { type: "string", description: "Sex", enum: ["male", "female"] },
26241
26481
  age: { type: "number", description: "Age in years" },
26242
26482
  value: { type: "number", description: "BMD value in g/cm\xB2" },
26243
- site: { type: "string", description: "Measurement site", enum: ["spine", "hip", "femoral-neck", "total-body", "forearm"] }
26483
+ site: { type: "string", description: "DXA measurement site: spine (L1-L4), neck (femoral neck), or tblh (total body less head)", enum: ["spine", "neck", "tblh"] }
26244
26484
  },
26245
26485
  required: ["sex", "age", "value", "site"]
26246
26486
  }
@@ -26378,9 +26618,9 @@ var toolSchemas = [
26378
26618
  type: "object",
26379
26619
  properties: {
26380
26620
  insulin: { type: "number", description: "Insulin value" },
26381
- insulinUnit: { type: "string", description: "Insulin unit", enum: ["uIU/mL", "mIU/L", "pmol/L"] },
26621
+ insulinUnit: { type: "string", description: "Insulin unit", enum: [...INSULIN_UNITS] },
26382
26622
  cPeptide: { type: "number", description: "C-Peptide value" },
26383
- cPeptideUnit: { type: "string", description: "C-Peptide unit", enum: ["ng/mL", "nmol/L", "pmol/L"] }
26623
+ cPeptideUnit: { type: "string", description: "C-Peptide unit", enum: [...CPEPTIDE_UNITS] }
26384
26624
  },
26385
26625
  required: ["insulin", "insulinUnit", "cPeptide", "cPeptideUnit"]
26386
26626
  }
@@ -26393,7 +26633,7 @@ var toolSchemas = [
26393
26633
  type: "function",
26394
26634
  function: {
26395
26635
  name: "steroid-conversion",
26396
- description: "Convert equivalent doses between glucocorticoids. Enter a dose of one steroid and get equivalents for all others. Ratios: Hydrocortisone 1x, Prednisolone 4x, Methylprednisolone 5x, Dexamethasone 30x.",
26636
+ description: "Convert equivalent doses between glucocorticoids (Hydrocortisone, Prednisolone, Methylprednisolone, Dexamethasone).",
26397
26637
  parameters: {
26398
26638
  type: "object",
26399
26639
  properties: {
@@ -26579,7 +26819,7 @@ var toolSchemas = [
26579
26819
  parameters: {
26580
26820
  type: "object",
26581
26821
  properties: {
26582
- substance: { type: "string", description: "Substance to convert. Examples: testosterone, cortisol, estradiol, dht, dheas, glucose, calcium, phosphate, creatinine, t4, freeT4, t3, freeT3, insulin, igf1, prolactin, acth, vitamin25D, pth, aldosterone, androstenedione, ohProgesterone, progesterone, dhea, etc." },
26822
+ substance: { type: "string", description: "Substance to convert (e.g. testosterone, cortisol, estradiol, glucose, calcium, insulin, igf1, prolactin, etc.)" },
26583
26823
  value: { type: "number", description: "Numeric value to convert" },
26584
26824
  fromUnit: { type: "string", description: "Source unit (e.g. ng/dL, nmol/L, mg/dL, mmol/L, \xB5g/dL, pg/mL, pmol/L, ng/mL, \xB5U/mL, mIU/L)" }
26585
26825
  },
@@ -26591,7 +26831,7 @@ var toolSchemas = [
26591
26831
  type: "function",
26592
26832
  function: {
26593
26833
  name: "osmolality",
26594
- description: "Calculate serum or urine osmolality. Serum: 2\xD7Na + Glucose/18 + BUN/2.8. Urine: 2\xD7(Na+K) + Glucose/18 + UUN/2.8. If params are missing, the tool returns what to ask for \u2014 then ask the user.",
26834
+ description: "Calculate serum or urine osmolality. Serum: 2\xD7Na + Glucose/18 + BUN/2.8. Urine: 2\xD7(Na+K) + Glucose/18 + UUN/2.8.",
26595
26835
  parameters: {
26596
26836
  type: "object",
26597
26837
  properties: {
@@ -26623,6 +26863,12 @@ var toolSchemas = [
26623
26863
  }
26624
26864
  }
26625
26865
  ];
26866
+ function isChatCatalogSchema(s) {
26867
+ const name = s.function.name;
26868
+ const metaId = chatFunctionToMetadataId(name);
26869
+ return isToolVisible(metaId, false);
26870
+ }
26871
+ var chatToolSchemas = toolSchemas.filter(isChatCatalogSchema);
26626
26872
 
26627
26873
  // ../src/data/toolMetadata.ts
26628
26874
  var toolsMetadata = [
@@ -26667,16 +26913,6 @@ var toolsMetadata = [
26667
26913
  subtitleFallback: "Growth hormone dose calculator",
26668
26914
  searchKeywords: ["GH", "b\xFCy\xFCme hormonu", "somatropin", "Norditropin", "Genotropin", "Nutropin", "doz", "dose", "IU", "mg"]
26669
26915
  },
26670
- {
26671
- id: "cnp-analogue",
26672
- category: "growth",
26673
- type: "calcs",
26674
- titleKey: "tools.cnp-analogue.title",
26675
- subtitleKey: "tools.cnp-analogue.subtitle",
26676
- titleFallback: "CNP Analogue",
26677
- subtitleFallback: "Vosoritide & navepegritide dosing",
26678
- searchKeywords: ["vosoritide", "CNP", "achondroplasia", "akondroplazi", "navepegritide", "natriuretic peptide", "doz", "dose", "c\xFCcelik", "dwarfism", "skeletal dysplasia", "cnp analogue", "cnp analog"]
26679
- },
26680
26916
  {
26681
26917
  id: "igf-sds",
26682
26918
  category: "growth",
@@ -26684,8 +26920,8 @@ var toolsMetadata = [
26684
26920
  titleKey: "tools.igf-sds.title",
26685
26921
  subtitleKey: "tools.igf-sds.subtitle",
26686
26922
  titleFallback: "IGF-1 & IGFBP-3 SDS",
26687
- subtitleFallback: "Age and Tanner stage-based",
26688
- searchKeywords: ["IGF-1", "IGFBP-3", "somatomedin", "Tanner", "evre", "stage", "SDS"]
26923
+ subtitleFallback: "Age-based IGF-1 & IGFBP-3 SDS calculation",
26924
+ searchKeywords: ["IGF-1", "IGFBP-3", "somatomedin", "SDS"]
26689
26925
  },
26690
26926
  {
26691
26927
  id: "igf-sds-lagh-adjustment",
@@ -26727,6 +26963,16 @@ var toolsMetadata = [
26727
26963
  subtitleFallback: "Height estimation from segmental measurements",
26728
26964
  searchKeywords: ["serebral palsi", "cerebral palsy", "CP", "segmental", "segment", "Stevenson", "boy tahmini"]
26729
26965
  },
26966
+ {
26967
+ id: "cnp-analogue",
26968
+ category: "growth",
26969
+ type: "calcs",
26970
+ titleKey: "tools.cnp-analogue.title",
26971
+ subtitleKey: "tools.cnp-analogue.subtitle",
26972
+ titleFallback: "CNP Analogue",
26973
+ subtitleFallback: "Vosoritide & navepegritide dosing",
26974
+ searchKeywords: ["vosoritide", "CNP", "achondroplasia", "akondroplazi", "navepegritide", "natriuretic peptide", "doz", "dose", "c\xFCcelik", "dwarfism", "skeletal dysplasia", "cnp analogue", "cnp analog"]
26975
+ },
26730
26976
  // BONE CATEGORY
26731
26977
  {
26732
26978
  id: "bmd-sds",
@@ -27000,7 +27246,26 @@ var toolsMetadata = [
27000
27246
  subtitleKey: "tools.thyroid-volume.subtitle",
27001
27247
  titleFallback: "Thyroid",
27002
27248
  subtitleFallback: "Thyroid volume SDS",
27003
- searchKeywords: ["tiroid", "thyroid", "guatr", "goiter", "hacim", "volume", "USG", "ultrason", "SDS"]
27249
+ searchKeywords: [
27250
+ "tiroid",
27251
+ "thyroid",
27252
+ "guatr",
27253
+ "goiter",
27254
+ "hacim",
27255
+ "volume",
27256
+ "USG",
27257
+ "ultrason",
27258
+ "SDS",
27259
+ "neonatal thyroid",
27260
+ "yenido\u011Fan tiroid",
27261
+ "newborn thyroid",
27262
+ "gestational",
27263
+ "gestasyon",
27264
+ "TSH",
27265
+ "Kurto\u011Flu",
27266
+ "Mutlu",
27267
+ "Tuzcu"
27268
+ ]
27004
27269
  },
27005
27270
  {
27006
27271
  id: "pituitary-height",
@@ -27086,23 +27351,6 @@ var toolsMetadata = [
27086
27351
  }
27087
27352
  ];
27088
27353
 
27089
- // ../src/lib/hiddenToolIds.ts
27090
- var HIDDEN_TOOL_IDS = [
27091
- "steroid-tapering",
27092
- "periop-adrenal",
27093
- "preop-diabetes",
27094
- "dka-fluid",
27095
- "gri",
27096
- "basal-bolus",
27097
- "meal-bolus",
27098
- "custom-iv-fluid",
27099
- "cp-height",
27100
- "oral-phosphorus",
27101
- "corrected-calcium",
27102
- "gfr",
27103
- "leptin-sds"
27104
- ];
27105
-
27106
27354
  // src/formatter.ts
27107
27355
  var useColor = !process.env.NO_COLOR && !!process.stdout.isTTY;
27108
27356
  var bold = (s) => useColor ? `\x1B[1m${s}\x1B[0m` : s;
@@ -27179,26 +27427,15 @@ var cyan2 = (s) => useColor2 ? `\x1B[36m${s}\x1B[0m` : s;
27179
27427
  var green = (s) => useColor2 ? `\x1B[32m${s}\x1B[0m` : s;
27180
27428
  var yellow2 = (s) => useColor2 ? `\x1B[33m${s}\x1B[0m` : s;
27181
27429
  var magenta = (s) => useColor2 ? `\x1B[35m${s}\x1B[0m` : s;
27182
- var VERSION = true ? "0.1.8" : "0.0.0-dev";
27183
- var visibleSchemas = toolSchemas.filter(
27184
- (s) => !HIDDEN_TOOL_IDS.includes(s.function.name)
27185
- );
27186
- var SCHEMA_TO_META = {
27187
- "hba1c": "hba1c-estimation",
27188
- "igf-lagh-adjustment": "igf-sds-lagh-adjustment",
27189
- "decimal-age": "decimal-age"
27190
- // no metadata entry — handled by fallback category
27191
- };
27192
- var CATEGORY_OVERRIDE = {
27193
- "igf-lagh-adjustment": "growth",
27194
- "decimal-age": "misc"
27195
- };
27430
+ var VERSION = true ? "0.2.0" : "0.0.0-dev";
27431
+ var visibleSchemas = chatToolSchemas;
27196
27432
  function findMeta(schemaId) {
27197
- const mappedId = SCHEMA_TO_META[schemaId] ?? schemaId;
27433
+ const mappedId = chatFunctionToMetadataId(schemaId);
27198
27434
  return toolsMetadata.find((m) => m.id === mappedId);
27199
27435
  }
27200
27436
  function getCategoryForSchema(schemaId) {
27201
- if (CATEGORY_OVERRIDE[schemaId]) return CATEGORY_OVERRIDE[schemaId];
27437
+ const override = chatFunctionCategoryOverride(schemaId);
27438
+ if (override) return override;
27202
27439
  const meta = findMeta(schemaId);
27203
27440
  return meta?.category ?? "misc";
27204
27441
  }
@@ -27280,6 +27517,7 @@ ${bold2("USAGE")}
27280
27517
 
27281
27518
  ${bold2("OPTIONS")}
27282
27519
  --format human|json ${dim2("Output format (default: human)")}
27520
+ --pipe ${dim2("Output only decimal age(s) for command chaining")}
27283
27521
  --help ${dim2("Show this help")}
27284
27522
  --version ${dim2("Show version")}
27285
27523
 
@@ -27447,7 +27685,7 @@ function parseKeyValueArgs(pairs, schema) {
27447
27685
  }
27448
27686
  return args;
27449
27687
  }
27450
- function runToolWithArgs(name, args, format) {
27688
+ function runToolWithArgs(name, args, format, pipe) {
27451
27689
  const schema = getSchemaByName(name);
27452
27690
  const errors = validateArgs(name, args);
27453
27691
  if (errors.length > 0) {
@@ -27466,7 +27704,17 @@ ${yellow2("!")} Validation errors for ${bold2(name)}:`);
27466
27704
  console.error(`${yellow2("!")} Error executing ${bold2(name)}:`, err instanceof Error ? err.message : err);
27467
27705
  process.exit(1);
27468
27706
  }
27469
- if (format === "json") {
27707
+ if (pipe && name === "decimal-age") {
27708
+ for (const r of results) {
27709
+ if (r.label === "Desimal ya\u015F") {
27710
+ const match = r.value.match(/([\d,]+)\s*yıl/);
27711
+ if (match) console.log(match[1].replace(",", "."));
27712
+ } else if (r.tag !== "echo" && r.value.includes("y\u0131l") && r.label.match(/^\d{2}\/\d{2}\/\d{4}$/)) {
27713
+ const match = r.value.match(/\(([\d,]+)\s*yıl\)/);
27714
+ if (match) console.log(match[1].replace(",", "."));
27715
+ }
27716
+ }
27717
+ } else if (format === "json") {
27470
27718
  const dataResults = results.filter((r) => r.tag !== "alt" && r.tag !== "sexAlt");
27471
27719
  console.log(JSON.stringify(dataResults, null, 2));
27472
27720
  } else {
@@ -27483,7 +27731,8 @@ function main() {
27483
27731
  schemas: { type: "boolean" },
27484
27732
  schema: { type: "boolean" },
27485
27733
  args: { type: "string", short: "a" },
27486
- format: { type: "string", short: "f", default: "human" }
27734
+ format: { type: "string", short: "f", default: "human" },
27735
+ pipe: { type: "boolean", short: "p" }
27487
27736
  }
27488
27737
  });
27489
27738
  if (values.help && positionals.length === 0) {
@@ -27548,10 +27797,10 @@ Apache-2.0 \xA9 \xC7EDD / TSPED`);
27548
27797
  console.error(`${yellow2("!")} Invalid JSON in --args: ${values.args}`);
27549
27798
  process.exit(1);
27550
27799
  }
27551
- runToolWithArgs(toolName, args, format);
27800
+ runToolWithArgs(toolName, args, format, values.pipe);
27552
27801
  } else if (kvPairs.length > 0) {
27553
27802
  const args = parseKeyValueArgs(kvPairs, schema);
27554
- runToolWithArgs(toolName, args, format);
27803
+ runToolWithArgs(toolName, args, format, values.pipe);
27555
27804
  } else {
27556
27805
  printToolInfo(toolName);
27557
27806
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ceddcozum",
3
- "version": "0.1.8",
3
+ "version": "0.2.0",
4
4
  "description": "ÇEDD Çözüm pediatric clinical calculators - CLI interface",
5
5
  "license": "Apache-2.0",
6
6
  "author": "ÇEDD / TSPED",