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.
- package/bundled-skills/.antigravity-install-manifest.json +4 -1
- package/bundled-skills/007/scripts/scanners/dependency_scanner.py +2 -2
- package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
- package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
- package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
- package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
- package/bundled-skills/docs/users/bundles.md +1 -1
- package/bundled-skills/docs/users/claude-code-skills.md +1 -1
- package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
- package/bundled-skills/docs/users/getting-started.md +1 -1
- package/bundled-skills/docs/users/kiro-integration.md +1 -1
- package/bundled-skills/docs/users/usage.md +4 -4
- package/bundled-skills/docs/users/visual-guide.md +4 -4
- package/bundled-skills/docx-official/ooxml/scripts/pack.py +12 -0
- package/bundled-skills/docx-official/ooxml/scripts/unpack.py +28 -7
- package/bundled-skills/docx-official/ooxml/scripts/validation/base.py +18 -13
- package/bundled-skills/docx-official/ooxml/scripts/validation/docx.py +6 -6
- package/bundled-skills/docx-official/ooxml/scripts/validation/pptx.py +6 -6
- package/bundled-skills/emil-design-eng/SKILL.md +702 -0
- package/bundled-skills/hugging-face-model-trainer/scripts/convert_to_gguf.py +3 -2
- package/bundled-skills/infinity/SKILL.md +173 -0
- package/bundled-skills/loki-mode/autonomy/run.sh +17 -8
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package-lock.json +17 -13
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package.json +3 -3
- package/bundled-skills/macos-spm-app-packaging/assets/templates/setup_dev_signing.sh +11 -8
- package/bundled-skills/macos-spm-app-packaging/assets/templates/sign-and-notarize.sh +10 -5
- package/bundled-skills/pptx-official/ooxml/scripts/pack.py +12 -0
- package/bundled-skills/pptx-official/ooxml/scripts/unpack.py +28 -7
- package/bundled-skills/pptx-official/ooxml/scripts/validation/base.py +18 -13
- package/bundled-skills/pptx-official/ooxml/scripts/validation/docx.py +6 -6
- package/bundled-skills/pptx-official/ooxml/scripts/validation/pptx.py +6 -6
- package/bundled-skills/review-animations/SKILL.md +139 -0
- package/bundled-skills/review-animations/STANDARDS.md +188 -0
- package/bundled-skills/skill-creator/scripts/package_skill.py +11 -1
- package/package.json +2 -2
- package/skills_index.json +66 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"updatedAt": "2026-06-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
75
|
+
- All 1,684+ skills from the skills directory
|
|
76
76
|
|
|
77
77
|
## When to Update
|
|
78
78
|
|
|
@@ -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,
|
|
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,
|
|
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
|
|
@@ -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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
261
|
+
root = parse_xml(str(rels_file)).getroot()
|
|
262
262
|
|
|
263
263
|
# Find all notesSlide relationships
|
|
264
264
|
for rel in root.findall(
|