ceddcozum 0.1.5 → 0.1.7

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 (2) hide show
  1. package/dist/index.js +117 -38
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -27086,11 +27086,34 @@ var toolsMetadata = [
27086
27086
  }
27087
27087
  ];
27088
27088
 
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
+
27089
27106
  // src/formatter.ts
27090
- var bold = (s) => `\x1B[1m${s}\x1B[0m`;
27091
- var dim = (s) => `\x1B[2m${s}\x1B[0m`;
27092
- var yellow = (s) => `\x1B[33m${s}\x1B[0m`;
27093
- var cyan = (s) => `\x1B[36m${s}\x1B[0m`;
27107
+ var useColor = !process.env.NO_COLOR && !!process.stdout.isTTY;
27108
+ var bold = (s) => useColor ? `\x1B[1m${s}\x1B[0m` : s;
27109
+ var dim = (s) => useColor ? `\x1B[2m${s}\x1B[0m` : s;
27110
+ var yellow = (s) => useColor ? `\x1B[33m${s}\x1B[0m` : s;
27111
+ var cyan = (s) => useColor ? `\x1B[36m${s}\x1B[0m` : s;
27112
+ function formatPercentile2(p) {
27113
+ if (p <= 0.02) return "<P0.02";
27114
+ if (p >= 99.98) return ">P99.98";
27115
+ return `P${p.toFixed(1)}`;
27116
+ }
27094
27117
  function formatHuman(toolName, results, schema) {
27095
27118
  const lines = [];
27096
27119
  lines.push("");
@@ -27101,12 +27124,13 @@ function formatHuman(toolName, results, schema) {
27101
27124
  const warnings = results.filter((r) => r.tag === "warning" || r.tag === "validation");
27102
27125
  const info = results.filter((r) => r.tag === "info" || r.tag === "note");
27103
27126
  const altRows = results.filter((r) => r.tag === "alt" || r.tag === "sexAlt");
27127
+ const padWidth = Math.max(20, ...dataRows.map((r) => r.label.length)) + 2;
27104
27128
  for (const r of dataRows) {
27105
- const label = r.label.padEnd(30);
27129
+ const label = r.label.padEnd(padWidth);
27106
27130
  const value = r.value;
27107
27131
  const unit = r.unit ? dim(` ${r.unit}`) : "";
27108
27132
  const sds = r.sds != null ? cyan(` SDS: ${r.sds >= 0 ? "+" : ""}${r.sds.toFixed(2)}`) : "";
27109
- const pct = r.percentile != null ? dim(` (P${r.percentile.toFixed(1)})`) : "";
27133
+ const pct = r.percentile != null ? dim(` (${formatPercentile2(r.percentile)})`) : "";
27110
27134
  let sdsNote = "";
27111
27135
  if (r.sdsReason === "outOfRange") {
27112
27136
  sdsNote = yellow(" (age out of reference range)");
@@ -27131,8 +27155,9 @@ function formatHuman(toolName, results, schema) {
27131
27155
  lines.push("");
27132
27156
  for (const r of altRows) {
27133
27157
  if (r.altToolName && r.altArgs) {
27158
+ const kvParts = Object.entries(r.altArgs).map(([k, v]) => `${k}=${v}`).join(" ");
27134
27159
  lines.push(` ${dim("tip:")} ${r.value}`);
27135
- lines.push(` ${dim(" ceddcozum")} ${r.altToolName} ${dim("--args")} '${JSON.stringify(r.altArgs)}'`);
27160
+ lines.push(` ${dim(" ceddcozum")} ${r.altToolName} ${kvParts}`);
27136
27161
  } else {
27137
27162
  lines.push(` ${dim("tip:")} ${r.value}`);
27138
27163
  }
@@ -27147,28 +27172,14 @@ function formatHuman(toolName, results, schema) {
27147
27172
  const noop = () => null;
27148
27173
  globalThis.localStorage = { getItem: noop, setItem: noop, removeItem: noop, clear: noop, length: 0, key: noop };
27149
27174
  })();
27150
- var bold2 = (s) => `\x1B[1m${s}\x1B[0m`;
27151
- var dim2 = (s) => `\x1B[2m${s}\x1B[0m`;
27152
- var cyan2 = (s) => `\x1B[36m${s}\x1B[0m`;
27153
- var green = (s) => `\x1B[32m${s}\x1B[0m`;
27154
- var yellow2 = (s) => `\x1B[33m${s}\x1B[0m`;
27155
- var magenta = (s) => `\x1B[35m${s}\x1B[0m`;
27156
- var HIDDEN_TOOL_IDS = [
27157
- "steroid-tapering",
27158
- "periop-adrenal",
27159
- "preop-diabetes",
27160
- "dka-fluid",
27161
- "gri",
27162
- "basal-bolus",
27163
- "meal-bolus",
27164
- "custom-iv-fluid",
27165
- "cp-height",
27166
- "oral-phosphorus",
27167
- "corrected-calcium",
27168
- "gfr",
27169
- "leptin-sds"
27170
- ];
27171
- var VERSION = "0.1.5";
27175
+ var useColor2 = !process.env.NO_COLOR && !!process.stdout.isTTY;
27176
+ var bold2 = (s) => useColor2 ? `\x1B[1m${s}\x1B[0m` : s;
27177
+ var dim2 = (s) => useColor2 ? `\x1B[2m${s}\x1B[0m` : s;
27178
+ var cyan2 = (s) => useColor2 ? `\x1B[36m${s}\x1B[0m` : s;
27179
+ var green = (s) => useColor2 ? `\x1B[32m${s}\x1B[0m` : s;
27180
+ var yellow2 = (s) => useColor2 ? `\x1B[33m${s}\x1B[0m` : s;
27181
+ var magenta = (s) => useColor2 ? `\x1B[35m${s}\x1B[0m` : s;
27182
+ var VERSION = true ? "0.1.7" : "0.0.0-dev";
27172
27183
  var visibleSchemas = toolSchemas.filter(
27173
27184
  (s) => !HIDDEN_TOOL_IDS.includes(s.function.name)
27174
27185
  );
@@ -27205,6 +27216,56 @@ var CATEGORY_LABELS = {
27205
27216
  function getSchemaByName(name) {
27206
27217
  return visibleSchemas.find((s) => s.function.name === name);
27207
27218
  }
27219
+ function camelToKebab(s) {
27220
+ return s.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
27221
+ }
27222
+ function kebabToCamel(s) {
27223
+ return s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
27224
+ }
27225
+ function levenshtein(a, b) {
27226
+ const m = a.length, n = b.length;
27227
+ const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
27228
+ for (let i = 0; i <= m; i++) dp[i][0] = i;
27229
+ for (let j = 0; j <= n; j++) dp[0][j] = j;
27230
+ for (let i = 1; i <= m; i++) {
27231
+ for (let j = 1; j <= n; j++) {
27232
+ dp[i][j] = Math.min(
27233
+ dp[i - 1][j] + 1,
27234
+ dp[i][j - 1] + 1,
27235
+ dp[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1)
27236
+ );
27237
+ }
27238
+ }
27239
+ return dp[m][n];
27240
+ }
27241
+ function suggestTool(input) {
27242
+ let best = null;
27243
+ let bestDist = 4;
27244
+ for (const s of visibleSchemas) {
27245
+ const d = levenshtein(input.toLowerCase(), s.function.name.toLowerCase());
27246
+ if (d < bestDist) {
27247
+ bestDist = d;
27248
+ best = s.function.name;
27249
+ }
27250
+ }
27251
+ return best;
27252
+ }
27253
+ function exampleValue(key, prop) {
27254
+ if (prop.enum) return prop.enum[0];
27255
+ if (prop.type === "boolean") return "true";
27256
+ const k = key.toLowerCase();
27257
+ if (k === "age" || k.includes("age")) return "5.5";
27258
+ if (k === "height") return "110";
27259
+ if (k === "weight") return "19";
27260
+ if (k.includes("circumference") || k === "headcircumference") return "50";
27261
+ if (k === "gestationalweeks" || k.includes("gestational")) return "38";
27262
+ if (k === "birthweight") return "3200";
27263
+ if (k.includes("percent")) return "7.5";
27264
+ if (k.includes("systolic")) return "110";
27265
+ if (k.includes("diastolic")) return "70";
27266
+ if (prop.type === "number") return "10";
27267
+ return "value";
27268
+ }
27208
27269
  function printHelp() {
27209
27270
  console.log(`
27210
27271
  ${bold2("ceddcozum")} v${VERSION} ${dim2("\u2014 Pediatric clinical calculators")}
@@ -27236,7 +27297,8 @@ function printList() {
27236
27297
  const name = schema.function.name;
27237
27298
  const meta = findMeta(name);
27238
27299
  const category = getCategoryForSchema(name);
27239
- const subtitle = meta?.subtitleFallback ?? schema.function.description.slice(0, 60);
27300
+ const raw = meta?.subtitleFallback ?? schema.function.description;
27301
+ const subtitle = raw.length > 50 ? raw.slice(0, 49) + "\u2026" : raw;
27240
27302
  if (!byCategory.has(category)) byCategory.set(category, []);
27241
27303
  byCategory.get(category).push({ id: name, subtitle });
27242
27304
  }
@@ -27273,7 +27335,7 @@ ${cyan2(String(visibleSchemas.length))} tools available:
27273
27335
  console.log();
27274
27336
  }
27275
27337
  function extractUnit(description) {
27276
- const match = description.match(/\bin\s+((?:[\w/°µ²]+(?:\s+per\s+\w+)?))/i);
27338
+ const match = description.match(/\bin\s+([\w/°µ²%]+(?:\s+per\s+\w+)?)\s*[.;,)]*$/i);
27277
27339
  if (match) return match[1];
27278
27340
  const parenMatch = description.match(/\((\w+\/\w+)\)\s*$/);
27279
27341
  if (parenMatch) return parenMatch[1];
@@ -27307,9 +27369,7 @@ ${bold2("EXAMPLE")}`);
27307
27369
  const exParts = [];
27308
27370
  for (const key of required) {
27309
27371
  const prop = params.properties[key];
27310
- if (prop?.enum) exParts.push(`${key}=${prop.enum[0]}`);
27311
- else if (prop?.type === "number") exParts.push(`${key}=...`);
27312
- else exParts.push(`${key}=...`);
27372
+ exParts.push(`${key}=${exampleValue(key, prop ?? { type: "string" })}`);
27313
27373
  }
27314
27374
  console.log(` ${dim2("$")} ceddcozum ${name} ${exParts.join(" ")}`);
27315
27375
  console.log();
@@ -27363,7 +27423,9 @@ function parseKeyValueArgs(pairs, schema) {
27363
27423
  }
27364
27424
  const rawKey = pair.slice(0, eqIdx);
27365
27425
  const raw = pair.slice(eqIdx + 1);
27366
- const key = Object.keys(props).find((k) => k.toLowerCase() === rawKey.toLowerCase()) ?? rawKey;
27426
+ const key = Object.keys(props).find(
27427
+ (k) => k.toLowerCase() === rawKey.toLowerCase() || camelToKebab(k) === rawKey.toLowerCase() || k.toLowerCase() === kebabToCamel(rawKey).toLowerCase()
27428
+ ) ?? rawKey;
27367
27429
  const prop = props[key];
27368
27430
  if (prop?.type === "number") {
27369
27431
  const n = Number(raw);
@@ -27405,7 +27467,8 @@ ${yellow2("!")} Validation errors for ${bold2(name)}:`);
27405
27467
  process.exit(1);
27406
27468
  }
27407
27469
  if (format === "json") {
27408
- console.log(JSON.stringify(results, null, 2));
27470
+ const dataResults = results.filter((r) => r.tag !== "alt" && r.tag !== "sexAlt");
27471
+ console.log(JSON.stringify(dataResults, null, 2));
27409
27472
  } else {
27410
27473
  console.log(formatHuman(name, results, schema));
27411
27474
  }
@@ -27423,7 +27486,7 @@ function main() {
27423
27486
  format: { type: "string", short: "f", default: "human" }
27424
27487
  }
27425
27488
  });
27426
- if (values.help) {
27489
+ if (values.help && positionals.length === 0) {
27427
27490
  printHelp();
27428
27491
  return;
27429
27492
  }
@@ -27456,10 +27519,26 @@ function main() {
27456
27519
  const schema = getSchemaByName(toolName);
27457
27520
  if (!schema) {
27458
27521
  console.error(`${yellow2("!")} Unknown tool: ${bold2(toolName)}`);
27459
- console.error(` Run ${cyan2("'ceddcozum --list'")} to see available tools.`);
27522
+ const suggestion = suggestTool(toolName);
27523
+ if (suggestion) {
27524
+ console.error(` Did you mean ${cyan2(suggestion)}?`);
27525
+ } else {
27526
+ console.error(` Run ${cyan2("'ceddcozum --list'")} to see available tools.`);
27527
+ }
27460
27528
  process.exit(1);
27461
27529
  }
27530
+ if (values.help) {
27531
+ printToolInfo(toolName);
27532
+ return;
27533
+ }
27462
27534
  const kvPairs = positionals.slice(1).filter((p) => p.includes("="));
27535
+ const nonKvArgs = positionals.slice(1).filter((p) => !p.includes("="));
27536
+ if (nonKvArgs.length > 0 && !values.args) {
27537
+ console.error(`${yellow2("!")} Unexpected arguments: ${bold2(nonKvArgs.join(" "))}`);
27538
+ console.error(` Use ${cyan2("key=value")} format: ${dim2(`ceddcozum ${toolName} sex=male age=5.5`)}`);
27539
+ console.error(` Run ${cyan2(`'ceddcozum ${toolName}'`)} to see parameter names.`);
27540
+ process.exit(1);
27541
+ }
27463
27542
  if (values.args) {
27464
27543
  let args;
27465
27544
  try {
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "ceddcozum",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "CEDD Cozum pediatric clinical calculators - CLI interface",
5
5
  "license": "MIT",
6
6
  "author": "Bora Ulukapı",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/boraulukapı/cedd-cozum"
9
+ "url": "https://github.com/borean/ceddcozum"
10
10
  },
11
11
  "keywords": [
12
12
  "pediatrics",