@tasksai/install 0.1.28 → 0.1.29

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/package.json +1 -1
  2. package/runtime/server.py +75 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tasksai/install",
3
- "version": "0.1.28",
3
+ "version": "0.1.29",
4
4
  "description": "Shared TasksAI MCP installer CLI",
5
5
  "type": "module",
6
6
  "bin": {
package/runtime/server.py CHANGED
@@ -125,7 +125,7 @@ if not LICENSE_KEY:
125
125
  print("Find your key in your purchase confirmation email.", file=sys.stderr, flush=True)
126
126
  sys.exit(1)
127
127
 
128
- SERVER_VERSION = "2.4.4"
128
+ SERVER_VERSION = "2.4.5"
129
129
 
130
130
  AUTH_HEADERS = {
131
131
  "Authorization": f"Bearer {LICENSE_KEY}",
@@ -1436,11 +1436,85 @@ def write_markdown(path, title, content_markdown, product_name):
1436
1436
  path.write_text("\n\n".join(part for part in parts if part), encoding="utf-8")
1437
1437
 
1438
1438
 
1439
+ def reconcile_public_copy_counts(content_markdown):
1440
+ """Replace stale public-copy counts with deterministic local counts.
1441
+
1442
+ AI hosts sometimes leave ``NOT VERIFIED`` or an estimated count beside a
1443
+ marked public-copy body. The local runtime can resolve that mechanical
1444
+ value without transmitting the document. It changes only count lines tied
1445
+ to balanced PUBLIC COPY markers and a detectable numeric maximum.
1446
+ """
1447
+ start_marker = "<!-- PUBLIC COPY START -->"
1448
+ end_marker = "<!-- PUBLIC COPY END -->"
1449
+ if content_markdown.count(start_marker) != content_markdown.count(end_marker):
1450
+ return content_markdown
1451
+
1452
+ blocks = list(
1453
+ re.finditer(
1454
+ re.escape(start_marker) + r"\n?(.*?)\n?" + re.escape(end_marker),
1455
+ content_markdown,
1456
+ re.DOTALL,
1457
+ )
1458
+ )
1459
+ if not blocks:
1460
+ return content_markdown
1461
+
1462
+ count_pattern = re.compile(
1463
+ r"(?im)^(?P<prefix>\s*(?:\*\*)?Deterministic count:(?:\*\*)?\s*)"
1464
+ r"(?P<actual>NOT VERIFIED|[\d,]+)"
1465
+ r"(?:\s*/\s*(?P<target>[\d,]+)\s*(?P<unit>words|characters))?\s*$"
1466
+ )
1467
+ limit_patterns = (
1468
+ re.compile(r"(?i)\bmaximum(?:\s+public[- ]copy\s+length)?\s+(?:is\s+)?([\d,]+)\s*(words|characters)\b"),
1469
+ re.compile(r"(?i)\bmax(?:imum)?\s*[:=]?\s*([\d,]+)\s*(words|characters)\b"),
1470
+ re.compile(r"(?i)\b([\d,]+)[ -](word|character)\s+maximum\b"),
1471
+ )
1472
+
1473
+ replacements = []
1474
+ count_matches = list(count_pattern.finditer(content_markdown))
1475
+ for block in blocks:
1476
+ following = [match for match in count_matches if match.start() >= block.end()]
1477
+ if not following:
1478
+ continue
1479
+ count_match = min(following, key=lambda match: match.start() - block.end())
1480
+ if count_match.start() - block.end() > 2000:
1481
+ continue
1482
+
1483
+ target_text = count_match.group("target")
1484
+ unit = (count_match.group("unit") or "").lower()
1485
+ if not target_text or not unit:
1486
+ limit_match = None
1487
+ for pattern in limit_patterns:
1488
+ limit_match = pattern.search(content_markdown)
1489
+ if limit_match:
1490
+ break
1491
+ if not limit_match:
1492
+ continue
1493
+ target_text = limit_match.group(1)
1494
+ unit = limit_match.group(2).lower()
1495
+ if unit == "word":
1496
+ unit = "words"
1497
+ elif unit == "character":
1498
+ unit = "characters"
1499
+
1500
+ target = int(target_text.replace(",", ""))
1501
+ body = block.group(1)
1502
+ actual = len(body) if unit == "characters" else len(re.findall(r"\S+", body))
1503
+ replacement = f"{count_match.group('prefix')}{actual} / {target} {unit}"
1504
+ replacements.append((count_match.start(), count_match.end(), replacement))
1505
+
1506
+ corrected = content_markdown
1507
+ for start, end, replacement in reversed(replacements):
1508
+ corrected = corrected[:start] + replacement + corrected[end:]
1509
+ return corrected
1510
+
1511
+
1439
1512
  def save_document(arguments, product_name):
1440
1513
  """Save AI-produced final content to a local downloadable file."""
1441
1514
  content_markdown = (arguments.get("content_markdown") or arguments.get("content") or "").strip()
1442
1515
  if not content_markdown:
1443
1516
  raise ValueError("content_markdown is required.")
1517
+ content_markdown = reconcile_public_copy_counts(content_markdown)
1444
1518
 
1445
1519
  title = (arguments.get("title") or "").strip()
1446
1520
  skill_id = (arguments.get("skill_id") or "").strip()