@tasksai/install 0.1.27 → 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 +87 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tasksai/install",
3
- "version": "0.1.27",
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.3"
128
+ SERVER_VERSION = "2.4.5"
129
129
 
130
130
  AUTH_HEADERS = {
131
131
  "Authorization": f"Bearer {LICENSE_KEY}",
@@ -962,7 +962,7 @@ def iter_inline_markdown(text):
962
962
  """Yield (text, marks) spans for common inline Markdown."""
963
963
  text = re.sub(r"\\([\\`*_{}\[\]()#+.!-])", r"\1", text)
964
964
  token_re = re.compile(
965
- r"(`[^`]+`|\*\*[^*]+\*\*|__[^_]+__|\*[^*\s][^*]*\*|(?<!\w)_[^_\s][^_]*_(?!\w)|\[[^\]]+\]\([^)]+\))"
965
+ r"(`[^`]+`|\*\*[^*]+\*\*|(?<!_)__(?![_\s])[^_]+?(?<!\s)__(?!_)|\*[^*\s][^*]*\*|(?<!\w)_[^_\s][^_]*_(?!\w)|\[[^\]]+\]\([^)]+\))"
966
966
  )
967
967
  pos = 0
968
968
  for match in token_re.finditer(text):
@@ -1052,6 +1052,14 @@ def add_markdown_text(paragraph, text):
1052
1052
  run.font.name = "Courier New"
1053
1053
 
1054
1054
 
1055
+ def add_markdown_heading(doc, text, level):
1056
+ """Create a Word heading while parsing any inline Markdown in its text."""
1057
+
1058
+ paragraph = doc.add_heading(level=level)
1059
+ add_markdown_text(paragraph, text)
1060
+ return paragraph
1061
+
1062
+
1055
1063
  def add_horizontal_rule(paragraph):
1056
1064
  """Render a horizontal Markdown rule as a Word paragraph border."""
1057
1065
  from docx.oxml import OxmlElement
@@ -1350,16 +1358,16 @@ def write_docx(path, title, content_markdown, product_name):
1350
1358
  and re.sub(r"\W+", "", heading_text).casefold()
1351
1359
  == re.sub(r"\W+", "", title).casefold()
1352
1360
  ):
1353
- doc.add_heading(heading_text, level=min(len(heading.group(1)), 4))
1361
+ add_markdown_heading(doc, heading_text, min(len(heading.group(1)), 4))
1354
1362
  keep_checklist_together = "checklist" in heading_text.casefold()
1355
1363
  index += 1
1356
1364
  elif standalone_bold_heading:
1357
1365
  flush_paragraph()
1358
- doc.add_heading(standalone_bold_heading.group(1).strip(), level=3)
1366
+ add_markdown_heading(doc, standalone_bold_heading.group(1).strip(), 3)
1359
1367
  index += 1
1360
1368
  elif inferred_heading:
1361
1369
  flush_paragraph()
1362
- doc.add_heading(stripped, level=inferred_heading)
1370
+ add_markdown_heading(doc, stripped, inferred_heading)
1363
1371
  keep_checklist_together = "checklist" in stripped.casefold()
1364
1372
  index += 1
1365
1373
  elif fact:
@@ -1428,11 +1436,85 @@ def write_markdown(path, title, content_markdown, product_name):
1428
1436
  path.write_text("\n\n".join(part for part in parts if part), encoding="utf-8")
1429
1437
 
1430
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
+
1431
1512
  def save_document(arguments, product_name):
1432
1513
  """Save AI-produced final content to a local downloadable file."""
1433
1514
  content_markdown = (arguments.get("content_markdown") or arguments.get("content") or "").strip()
1434
1515
  if not content_markdown:
1435
1516
  raise ValueError("content_markdown is required.")
1517
+ content_markdown = reconcile_public_copy_counts(content_markdown)
1436
1518
 
1437
1519
  title = (arguments.get("title") or "").strip()
1438
1520
  skill_id = (arguments.get("skill_id") or "").strip()