opencode-skills-collection 3.1.5 → 3.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 (36) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +4 -1
  2. package/bundled-skills/007/scripts/scanners/dependency_scanner.py +2 -2
  3. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  4. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  5. package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
  6. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  7. package/bundled-skills/docs/users/bundles.md +1 -1
  8. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  9. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  10. package/bundled-skills/docs/users/getting-started.md +1 -1
  11. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  12. package/bundled-skills/docs/users/usage.md +4 -4
  13. package/bundled-skills/docs/users/visual-guide.md +4 -4
  14. package/bundled-skills/docx-official/ooxml/scripts/pack.py +12 -0
  15. package/bundled-skills/docx-official/ooxml/scripts/unpack.py +28 -7
  16. package/bundled-skills/docx-official/ooxml/scripts/validation/base.py +18 -13
  17. package/bundled-skills/docx-official/ooxml/scripts/validation/docx.py +6 -6
  18. package/bundled-skills/docx-official/ooxml/scripts/validation/pptx.py +6 -6
  19. package/bundled-skills/emil-design-eng/SKILL.md +702 -0
  20. package/bundled-skills/hugging-face-model-trainer/scripts/convert_to_gguf.py +3 -2
  21. package/bundled-skills/infinity/SKILL.md +173 -0
  22. package/bundled-skills/loki-mode/autonomy/run.sh +17 -8
  23. package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package-lock.json +17 -13
  24. package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package.json +3 -3
  25. package/bundled-skills/macos-spm-app-packaging/assets/templates/setup_dev_signing.sh +11 -8
  26. package/bundled-skills/macos-spm-app-packaging/assets/templates/sign-and-notarize.sh +10 -5
  27. package/bundled-skills/pptx-official/ooxml/scripts/pack.py +12 -0
  28. package/bundled-skills/pptx-official/ooxml/scripts/unpack.py +28 -7
  29. package/bundled-skills/pptx-official/ooxml/scripts/validation/base.py +18 -13
  30. package/bundled-skills/pptx-official/ooxml/scripts/validation/docx.py +6 -6
  31. package/bundled-skills/pptx-official/ooxml/scripts/validation/pptx.py +6 -6
  32. package/bundled-skills/review-animations/SKILL.md +139 -0
  33. package/bundled-skills/review-animations/STANDARDS.md +188 -0
  34. package/bundled-skills/skill-creator/scripts/package_skill.py +11 -1
  35. package/package.json +2 -2
  36. package/skills_index.json +66 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "updatedAt": "2026-06-24T02:01:40.611Z",
3
+ "updatedAt": "2026-06-26T02:05:23.006Z",
4
4
  "entries": [
5
5
  "00-andruia-consultant",
6
6
  "007",
@@ -620,6 +620,7 @@
620
620
  "embedding-strategies",
621
621
  "emblemai-crypto-wallet",
622
622
  "emergency-card",
623
+ "emil-design-eng",
623
624
  "emotional-arc-designer",
624
625
  "employment-contract-templates",
625
626
  "energy-procurement",
@@ -850,6 +851,7 @@
850
851
  "indexing-issue-auditor",
851
852
  "industrial-brutalist-ui",
852
853
  "infinite-gratitude",
854
+ "infinity",
853
855
  "ingest-youtube",
854
856
  "inngest",
855
857
  "instagram",
@@ -1261,6 +1263,7 @@
1261
1263
  "requesting-code-review",
1262
1264
  "returns-reverse-logistics",
1263
1265
  "reverse-engineer",
1266
+ "review-animations",
1264
1267
  "revops",
1265
1268
  "rich-elicitation",
1266
1269
  "risk-manager",
@@ -155,7 +155,7 @@ _DOCKER_COPY_SENSITIVE_RE = re.compile(
155
155
  )
156
156
 
157
157
  _DOCKER_CURL_PIPE_RE = re.compile(
158
- r"""(?:curl|wget)\s+[^|]*\|\s*(?:bash|sh|zsh|python|perl|ruby|node)""",
158
+ r"""(?:curl|wget)\s+[^|]*\|\s*(?:bash|sh|zsh|python|perl|ruby|node)""", # security-allowlist: curl-pipe-bash, wget-pipe-sh
159
159
  re.IGNORECASE,
160
160
  )
161
161
 
@@ -776,7 +776,7 @@ def analyze_dockerfile(filepath: Path, verbose: bool = False) -> dict:
776
776
  file=file_str,
777
777
  line=line_num,
778
778
  severity="CRITICAL",
779
- description="Pipe-to-shell pattern detected (curl|bash). Remote code execution risk",
779
+ description="Pipe-to-shell pattern detected (curl|bash). Remote code execution risk", # security-allowlist: curl-pipe-bash
780
780
  recommendation="Download scripts first, verify checksum, then execute",
781
781
  pattern="curl_pipe_bash",
782
782
  ))
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  title: Jetski/Cortex + Gemini Integration Guide
3
- description: "Use antigravity-awesome-skills with Jetski/Cortex without hitting context-window overflow with 1,681+ skills."
3
+ description: "Use antigravity-awesome-skills with Jetski/Cortex without hitting context-window overflow with 1,684+ skills."
4
4
  ---
5
5
 
6
- # Jetski/Cortex + Gemini: safe integration with 1,681+ skills
6
+ # Jetski/Cortex + Gemini: safe integration with 1,684+ skills
7
7
 
8
8
  This guide shows how to integrate the `antigravity-awesome-skills` repository with an agent based on **Jetski/Cortex + Gemini** (or similar frameworks) **without exceeding the model context window**.
9
9
 
@@ -23,7 +23,7 @@ Never do:
23
23
  - concatenate all `SKILL.md` content into a single system prompt;
24
24
  - re-inject the entire library for **every** request.
25
25
 
26
- With 1,681+ skills, this approach fills the context window before user messages are even added, causing truncation.
26
+ With 1,684+ skills, this approach fills the context window before user messages are even added, causing truncation.
27
27
 
28
28
  ---
29
29
 
@@ -21,7 +21,7 @@ This example shows one way to integrate **antigravity-awesome-skills** with a Je
21
21
  - How to enforce a **maximum number of skills per turn** via `maxSkillsPerTurn`.
22
22
  - How to choose whether to **truncate or error** when too many skills are requested via `overflowBehavior`.
23
23
 
24
- This pattern avoids context overflow when you have 1,681+ skills installed.
24
+ This pattern avoids context overflow when you have 1,684+ skills installed.
25
25
 
26
26
  Manifest contract references:
27
27
 
@@ -6,7 +6,7 @@ This document keeps the repository's GitHub-facing discovery copy aligned with t
6
6
 
7
7
  Preferred positioning:
8
8
 
9
- > Installable GitHub library of 1,681+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants.
9
+ > Installable GitHub library of 1,684+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants.
10
10
 
11
11
  Key framing:
12
12
 
@@ -20,7 +20,7 @@ Key framing:
20
20
 
21
21
  Preferred description:
22
22
 
23
- > Installable GitHub library of 1,681+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and more. Includes installer CLI, bundles, workflows, and official/community skill collections.
23
+ > Installable GitHub library of 1,684+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and more. Includes installer CLI, bundles, workflows, and official/community skill collections.
24
24
 
25
25
  Preferred homepage:
26
26
 
@@ -28,7 +28,7 @@ Preferred homepage:
28
28
 
29
29
  Preferred social preview:
30
30
 
31
- - use a clean preview image that says `1,681+ Agentic Skills`;
31
+ - use a clean preview image that says `1,684+ Agentic Skills`;
32
32
  - mention Claude Code, Cursor, Codex CLI, and Gemini CLI;
33
33
  - avoid dense text and tiny logos that disappear in social cards.
34
34
 
@@ -72,7 +72,7 @@ The update process refreshes:
72
72
  - Canonical skills index (`skills_index.json`)
73
73
  - Compatibility mirror (`data/skills_index.json`)
74
74
  - Web app skills data (`apps\web-app\public\skills.json`)
75
- - All 1,681+ skills from the skills directory
75
+ - All 1,684+ skills from the skills directory
76
76
 
77
77
  ## When to Update
78
78
 
@@ -1061,4 +1061,4 @@ Found a skill that should be in a bundle? Or want to create a new bundle? [Open
1061
1061
 
1062
1062
  ---
1063
1063
 
1064
- _Last updated: June 2026 | Total Skills: 1,681+ | Total Bundles: 59_
1064
+ _Last updated: June 2026 | Total Skills: 1,684+ | Total Bundles: 59_
@@ -12,7 +12,7 @@ Install the library into Claude Code, then invoke focused skills directly in the
12
12
 
13
13
  ## Why use this repo for Claude Code
14
14
 
15
- - It includes 1,681+ skills instead of a narrow single-domain starter pack.
15
+ - It includes 1,684+ skills instead of a narrow single-domain starter pack.
16
16
  - It supports the standard `.claude/skills/` path and the Claude Code plugin marketplace flow.
17
17
  - It also ships generated bundle plugins so teams can install focused packs like `Essentials` or `Security Developer` from the marketplace metadata.
18
18
  - It includes onboarding docs, bundles, and workflows so new users do not need to guess where to begin.
@@ -12,7 +12,7 @@ Install into the Gemini skills path, then ask Gemini to apply one skill at a tim
12
12
 
13
13
  - It installs directly into the expected Gemini skills path.
14
14
  - It includes both core software engineering skills and deeper agent/LLM-oriented skills.
15
- - It helps new users get started with bundles and workflows rather than forcing a cold start from 1,681+ files.
15
+ - It helps new users get started with bundles and workflows rather than forcing a cold start from 1,684+ files.
16
16
  - It is useful whether you want a broad internal skill library or a single repo to test many workflows quickly.
17
17
 
18
18
  ## Install Gemini CLI Skills
@@ -1,4 +1,4 @@
1
- # Getting Started with Antigravity Awesome Skills (V13.1.1)
1
+ # Getting Started with Antigravity Awesome Skills (V13.2.0)
2
2
 
3
3
  **New here? This guide will help you supercharge your AI Agent in 5 minutes.**
4
4
 
@@ -18,7 +18,7 @@ Kiro is AWS's agentic AI IDE that combines:
18
18
 
19
19
  Kiro's agentic capabilities are enhanced by skills that provide:
20
20
 
21
- - **Domain expertise** across 1,681+ specialized areas
21
+ - **Domain expertise** across 1,684+ specialized areas
22
22
  - **Best practices** from Anthropic, OpenAI, Google, Microsoft, and AWS
23
23
  - **Workflow automation** for common development tasks
24
24
  - **AWS-specific patterns** for serverless, infrastructure, and cloud architecture
@@ -14,7 +14,7 @@ If you came in through a **Claude Code** or **Codex** plugin instead of a full l
14
14
 
15
15
  When you ran `npx antigravity-awesome-skills` or cloned the repository, you:
16
16
 
17
- ✅ **Downloaded 1,681+ skill files** to your computer (default: `~/.agents/skills/`; or a custom path like `~/.agent/skills/` if you used `--path`)
17
+ ✅ **Downloaded 1,684+ skill files** to your computer (default: `~/.agents/skills/`; or a custom path like `~/.agent/skills/` if you used `--path`)
18
18
  ✅ **Made them available** to your AI assistant
19
19
  ❌ **Did NOT enable them all automatically** (they're just sitting there, waiting)
20
20
 
@@ -34,7 +34,7 @@ Bundles are **curated groups** of skills organized by role. They help you decide
34
34
 
35
35
  **Analogy:**
36
36
 
37
- - You installed a toolbox with 1,681+ tools (✅ done)
37
+ - You installed a toolbox with 1,684+ tools (✅ done)
38
38
  - Bundles are like **labeled organizer trays** saying: "If you're a carpenter, start with these 10 tools"
39
39
  - You can either **pick skills from the tray** or install that tray as a focused marketplace bundle plugin
40
40
 
@@ -212,7 +212,7 @@ Let's actually use a skill right now. Follow these steps:
212
212
 
213
213
  ## Step 5: Picking Your First Skills (Practical Advice)
214
214
 
215
- Don't try to use all 1,681+ skills at once. Here's a sensible approach:
215
+ Don't try to use all 1,684+ skills at once. Here's a sensible approach:
216
216
 
217
217
  If you want a tool-specific starting point before choosing skills, use:
218
218
 
@@ -343,7 +343,7 @@ Usually no, but if your AI doesn't recognize a skill:
343
343
 
344
344
  ### "Can I load all skills into the model at once?"
345
345
 
346
- No. Even though you have 1,681+ skills installed locally, you should **not** concatenate every `SKILL.md` into a single system prompt or context block.
346
+ No. Even though you have 1,684+ skills installed locally, you should **not** concatenate every `SKILL.md` into a single system prompt or context block.
347
347
 
348
348
  The intended pattern is:
349
349
 
@@ -34,7 +34,7 @@ antigravity-awesome-skills/
34
34
  ├── 📄 CONTRIBUTING.md ← Contributor workflow
35
35
  ├── 📄 CATALOG.md ← Full generated catalog
36
36
 
37
- ├── 📁 skills/ ← 1,681+ skills live here
37
+ ├── 📁 skills/ ← 1,684+ skills live here
38
38
  │ │
39
39
  │ ├── 📁 brainstorming/
40
40
  │ │ └── 📄 SKILL.md ← Skill definition
@@ -47,7 +47,7 @@ antigravity-awesome-skills/
47
47
  │ │ └── 📁 2d-games/
48
48
  │ │ └── 📄 SKILL.md ← Nested skills also supported
49
49
  │ │
50
- │ └── ... (1,681+ total)
50
+ │ └── ... (1,684+ total)
51
51
 
52
52
  ├── 📁 apps/
53
53
  │ └── 📁 web-app/ ← Interactive browser
@@ -100,7 +100,7 @@ antigravity-awesome-skills/
100
100
 
101
101
  ```
102
102
  ┌─────────────────────────┐
103
- │ 1,681+ SKILLS │
103
+ │ 1,684+ SKILLS │
104
104
  └────────────┬────────────┘
105
105
 
106
106
  ┌────────────────────────┼────────────────────────┐
@@ -201,7 +201,7 @@ If you want a workspace-style manual install instead, cloning into `.agent/skill
201
201
  │ ├── 📁 brainstorming/ │
202
202
  │ ├── 📁 stripe-integration/ │
203
203
  │ ├── 📁 react-best-practices/ │
204
- │ └── ... (1,681+ total) │
204
+ │ └── ... (1,684+ total) │
205
205
  └─────────────────────────────────────────┘
206
206
  ```
207
207
 
@@ -16,6 +16,17 @@ import zipfile
16
16
  from pathlib import Path
17
17
 
18
18
 
19
+ def validate_input_tree(input_dir: Path):
20
+ root = input_dir.resolve(strict=True)
21
+ for path in input_dir.rglob("*"):
22
+ if path.is_symlink():
23
+ raise ValueError(f"Refusing to pack symlink: {path}")
24
+ try:
25
+ path.resolve(strict=True).relative_to(root)
26
+ except (OSError, ValueError):
27
+ raise ValueError(f"Refusing to pack path outside input directory: {path}") from None
28
+
29
+
19
30
  def main():
20
31
  parser = argparse.ArgumentParser(description="Pack a directory into an Office file")
21
32
  parser.add_argument("input_directory", help="Unpacked Office document directory")
@@ -60,6 +71,7 @@ def pack_document(input_dir, output_file, validate=False):
60
71
  raise ValueError(f"{input_dir} is not a directory")
61
72
  if output_file.suffix.lower() not in {".docx", ".pptx", ".xlsx"}:
62
73
  raise ValueError(f"{output_file} must be a .docx, .pptx, or .xlsx file")
74
+ validate_input_tree(input_dir)
63
75
 
64
76
  # Work in temporary directory to avoid modifying original
65
77
  with tempfile.TemporaryDirectory() as temp_dir:
@@ -8,6 +8,11 @@ import sys
8
8
  import zipfile
9
9
  from pathlib import Path
10
10
 
11
+ MAX_ARCHIVE_MEMBERS = 5000
12
+ MAX_MEMBER_SIZE = 100 * 1024 * 1024
13
+ MAX_TOTAL_UNCOMPRESSED = 512 * 1024 * 1024
14
+ MAX_COMPRESSION_RATIO = 1000
15
+
11
16
 
12
17
  def _is_zip_symlink(member: zipfile.ZipInfo) -> bool:
13
18
  return stat.S_ISLNK(member.external_attr >> 16)
@@ -29,19 +34,35 @@ def _extract_member(archive: zipfile.ZipFile, member: zipfile.ZipInfo, output_ro
29
34
  shutil.copyfileobj(source, target)
30
35
 
31
36
 
37
+ def _validate_archive_members(archive: zipfile.ZipFile, output_root: Path):
38
+ members = archive.infolist()
39
+ if len(members) > MAX_ARCHIVE_MEMBERS:
40
+ raise ValueError("Archive contains too many entries")
41
+
42
+ total_size = 0
43
+ for member in members:
44
+ if _is_zip_symlink(member):
45
+ raise ValueError(f"Unsafe archive entry: {member.filename}")
46
+ if not _is_safe_destination(output_root, member.filename):
47
+ raise ValueError(f"Unsafe archive entry: {member.filename}")
48
+ if member.file_size > MAX_MEMBER_SIZE:
49
+ raise ValueError(f"Archive entry too large: {member.filename}")
50
+ total_size += member.file_size
51
+ if total_size > MAX_TOTAL_UNCOMPRESSED:
52
+ raise ValueError("Archive uncompressed size is too large")
53
+ if member.compress_size and member.file_size / member.compress_size > MAX_COMPRESSION_RATIO:
54
+ raise ValueError(f"Archive entry compression ratio too high: {member.filename}")
55
+
56
+ return members
57
+
58
+
32
59
  def extract_archive_safely(input_file: str | Path, output_dir: str | Path):
33
60
  output_path = Path(output_dir)
34
61
  output_path.mkdir(parents=True, exist_ok=True)
35
62
  output_root = output_path.resolve()
36
63
 
37
64
  with zipfile.ZipFile(input_file) as archive:
38
- for member in archive.infolist():
39
- if _is_zip_symlink(member):
40
- raise ValueError(f"Unsafe archive entry: {member.filename}")
41
- if not _is_safe_destination(output_root, member.filename):
42
- raise ValueError(f"Unsafe archive entry: {member.filename}")
43
-
44
- for member in archive.infolist():
65
+ for member in _validate_archive_members(archive, output_root):
45
66
  _extract_member(archive, member, output_path)
46
67
 
47
68
 
@@ -9,6 +9,14 @@ from pathlib import Path
9
9
  import lxml.etree
10
10
 
11
11
 
12
+ def hardened_xml_parser():
13
+ return lxml.etree.XMLParser(resolve_entities=False, no_network=True, load_dtd=False, huge_tree=False)
14
+
15
+
16
+ def parse_xml(source, **kwargs):
17
+ return lxml.etree.parse(source, parser=hardened_xml_parser(), **kwargs)
18
+
19
+
12
20
  def safe_extract_all(zip_ref, destination):
13
21
  """Extract a zip archive without allowing members to escape destination."""
14
22
  destination = Path(destination).resolve()
@@ -149,7 +157,7 @@ class BaseSchemaValidator:
149
157
  for xml_file in self.xml_files:
150
158
  try:
151
159
  # Try to parse the XML file
152
- lxml.etree.parse(str(xml_file))
160
+ parse_xml(str(xml_file))
153
161
  except lxml.etree.XMLSyntaxError as e:
154
162
  errors.append(
155
163
  f" {xml_file.relative_to(self.unpacked_dir)}: "
@@ -177,7 +185,7 @@ class BaseSchemaValidator:
177
185
 
178
186
  for xml_file in self.xml_files:
179
187
  try:
180
- root = lxml.etree.parse(str(xml_file)).getroot()
188
+ root = parse_xml(str(xml_file)).getroot()
181
189
  declared = set(root.nsmap.keys()) - {None} # Exclude default namespace
182
190
 
183
191
  for attr_val in [
@@ -208,7 +216,7 @@ class BaseSchemaValidator:
208
216
 
209
217
  for xml_file in self.xml_files:
210
218
  try:
211
- root = lxml.etree.parse(str(xml_file)).getroot()
219
+ root = parse_xml(str(xml_file)).getroot()
212
220
  file_ids = {} # Track IDs that must be unique within this file
213
221
 
214
222
  # Remove all mc:AlternateContent elements from the tree
@@ -328,7 +336,7 @@ class BaseSchemaValidator:
328
336
  for rels_file in rels_files:
329
337
  try:
330
338
  # Parse relationships file
331
- rels_root = lxml.etree.parse(str(rels_file)).getroot()
339
+ rels_root = parse_xml(str(rels_file)).getroot()
332
340
 
333
341
  # Get the directory where this .rels file is located
334
342
  rels_dir = rels_file.parent
@@ -429,7 +437,7 @@ class BaseSchemaValidator:
429
437
 
430
438
  try:
431
439
  # Parse the .rels file to get valid relationship IDs and their types
432
- rels_root = lxml.etree.parse(str(rels_file)).getroot()
440
+ rels_root = parse_xml(str(rels_file)).getroot()
433
441
  rid_to_type = {}
434
442
 
435
443
  for rel in rels_root.findall(
@@ -452,7 +460,7 @@ class BaseSchemaValidator:
452
460
  rid_to_type[rid] = type_name
453
461
 
454
462
  # Parse the XML file to find all r:id references
455
- xml_root = lxml.etree.parse(str(xml_file)).getroot()
463
+ xml_root = parse_xml(str(xml_file)).getroot()
456
464
 
457
465
  # Find all elements with r:id attributes
458
466
  for elem in xml_root.iter():
@@ -549,7 +557,7 @@ class BaseSchemaValidator:
549
557
 
550
558
  try:
551
559
  # Parse and get all declared parts and extensions
552
- root = lxml.etree.parse(str(content_types_file)).getroot()
560
+ root = parse_xml(str(content_types_file)).getroot()
553
561
  declared_parts = set()
554
562
  declared_extensions = set()
555
563
 
@@ -611,7 +619,7 @@ class BaseSchemaValidator:
611
619
  continue
612
620
 
613
621
  try:
614
- root_tag = lxml.etree.parse(str(xml_file)).getroot().tag
622
+ root_tag = parse_xml(str(xml_file)).getroot().tag
615
623
  root_name = root_tag.split("}")[-1] if "}" in root_tag else root_tag
616
624
 
617
625
  if root_name in declarable_roots and path_str not in declared_parts:
@@ -850,15 +858,12 @@ class BaseSchemaValidator:
850
858
  try:
851
859
  # Load schema
852
860
  with open(schema_path, "rb") as xsd_file:
853
- parser = lxml.etree.XMLParser()
854
- xsd_doc = lxml.etree.parse(
855
- xsd_file, parser=parser, base_url=str(schema_path)
856
- )
861
+ xsd_doc = parse_xml(xsd_file, base_url=str(schema_path))
857
862
  schema = lxml.etree.XMLSchema(xsd_doc)
858
863
 
859
864
  # Load and preprocess XML
860
865
  with open(xml_file, "r") as f:
861
- xml_doc = lxml.etree.parse(f)
866
+ xml_doc = parse_xml(f)
862
867
 
863
868
  xml_doc, _ = self._remove_template_tags_from_text_nodes(xml_doc)
864
869
  xml_doc = self._preprocess_for_mc_ignorable(xml_doc)
@@ -10,7 +10,7 @@ from pathlib import Path
10
10
 
11
11
  import lxml.etree
12
12
 
13
- from .base import BaseSchemaValidator
13
+ from .base import BaseSchemaValidator, parse_xml
14
14
 
15
15
 
16
16
  def safe_extract_all(zip_ref, destination):
@@ -100,7 +100,7 @@ class DOCXSchemaValidator(BaseSchemaValidator):
100
100
  continue
101
101
 
102
102
  try:
103
- root = lxml.etree.parse(str(xml_file)).getroot()
103
+ root = parse_xml(str(xml_file)).getroot()
104
104
 
105
105
  # Find all w:t elements
106
106
  for elem in root.iter(f"{{{self.WORD_2006_NAMESPACE}}}t"):
@@ -153,7 +153,7 @@ class DOCXSchemaValidator(BaseSchemaValidator):
153
153
  continue
154
154
 
155
155
  try:
156
- root = lxml.etree.parse(str(xml_file)).getroot()
156
+ root = parse_xml(str(xml_file)).getroot()
157
157
 
158
158
  # Find all w:t elements that are descendants of w:del elements
159
159
  namespaces = {"w": self.WORD_2006_NAMESPACE}
@@ -199,7 +199,7 @@ class DOCXSchemaValidator(BaseSchemaValidator):
199
199
  continue
200
200
 
201
201
  try:
202
- root = lxml.etree.parse(str(xml_file)).getroot()
202
+ root = parse_xml(str(xml_file)).getroot()
203
203
  # Count all w:p elements
204
204
  paragraphs = root.findall(f".//{{{self.WORD_2006_NAMESPACE}}}p")
205
205
  count = len(paragraphs)
@@ -221,7 +221,7 @@ class DOCXSchemaValidator(BaseSchemaValidator):
221
221
 
222
222
  # Parse document.xml
223
223
  doc_xml_path = temp_dir + "/word/document.xml"
224
- root = lxml.etree.parse(doc_xml_path).getroot()
224
+ root = parse_xml(doc_xml_path).getroot()
225
225
 
226
226
  # Count all w:p elements
227
227
  paragraphs = root.findall(f".//{{{self.WORD_2006_NAMESPACE}}}p")
@@ -244,7 +244,7 @@ class DOCXSchemaValidator(BaseSchemaValidator):
244
244
  continue
245
245
 
246
246
  try:
247
- root = lxml.etree.parse(str(xml_file)).getroot()
247
+ root = parse_xml(str(xml_file)).getroot()
248
248
  namespaces = {"w": self.WORD_2006_NAMESPACE}
249
249
 
250
250
  # Find w:delText in w:ins that are NOT within w:del
@@ -4,7 +4,7 @@ Validator for PowerPoint presentation XML files against XSD schemas.
4
4
 
5
5
  import re
6
6
 
7
- from .base import BaseSchemaValidator
7
+ from .base import BaseSchemaValidator, parse_xml
8
8
 
9
9
 
10
10
  class PPTXSchemaValidator(BaseSchemaValidator):
@@ -86,7 +86,7 @@ class PPTXSchemaValidator(BaseSchemaValidator):
86
86
 
87
87
  for xml_file in self.xml_files:
88
88
  try:
89
- root = lxml.etree.parse(str(xml_file)).getroot()
89
+ root = parse_xml(str(xml_file)).getroot()
90
90
 
91
91
  # Check all elements for ID attributes
92
92
  for elem in root.iter():
@@ -142,7 +142,7 @@ class PPTXSchemaValidator(BaseSchemaValidator):
142
142
  for slide_master in slide_masters:
143
143
  try:
144
144
  # Parse the slide master file
145
- root = lxml.etree.parse(str(slide_master)).getroot()
145
+ root = parse_xml(str(slide_master)).getroot()
146
146
 
147
147
  # Find the corresponding _rels file for this slide master
148
148
  rels_file = slide_master.parent / "_rels" / f"{slide_master.name}.rels"
@@ -155,7 +155,7 @@ class PPTXSchemaValidator(BaseSchemaValidator):
155
155
  continue
156
156
 
157
157
  # Parse the relationships file
158
- rels_root = lxml.etree.parse(str(rels_file)).getroot()
158
+ rels_root = parse_xml(str(rels_file)).getroot()
159
159
 
160
160
  # Build a set of valid relationship IDs that point to slide layouts
161
161
  valid_layout_rids = set()
@@ -209,7 +209,7 @@ class PPTXSchemaValidator(BaseSchemaValidator):
209
209
 
210
210
  for rels_file in slide_rels_files:
211
211
  try:
212
- root = lxml.etree.parse(str(rels_file)).getroot()
212
+ root = parse_xml(str(rels_file)).getroot()
213
213
 
214
214
  # Find all slideLayout relationships
215
215
  layout_rels = [
@@ -258,7 +258,7 @@ class PPTXSchemaValidator(BaseSchemaValidator):
258
258
  for rels_file in slide_rels_files:
259
259
  try:
260
260
  # Parse the relationships file
261
- root = lxml.etree.parse(str(rels_file)).getroot()
261
+ root = parse_xml(str(rels_file)).getroot()
262
262
 
263
263
  # Find all notesSlide relationships
264
264
  for rel in root.findall(