opencode-skills-collection 3.0.21 → 3.0.23
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 +5 -1
- package/bundled-skills/bilig-workpaper/SKILL.md +145 -0
- 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/ejentum-reasoning-harness/SKILL.md +3 -0
- package/bundled-skills/ingest-youtube/SKILL.md +6 -1
- package/bundled-skills/ingest-youtube/ingest.py +81 -31
- package/bundled-skills/linux-privilege-escalation/SKILL.md +5 -4
- package/bundled-skills/mercury-mcp/SKILL.md +126 -0
- package/bundled-skills/news-sentiment-engine/SKILL.md +9 -1
- package/bundled-skills/photopea-embedded-editor/SKILL.md +1399 -0
- package/bundled-skills/subagent-orchestrator/README.md +99 -0
- package/bundled-skills/subagent-orchestrator/SKILL.md +201 -0
- package/bundled-skills/subagent-orchestrator/examples/api-plus-frontend.md +76 -0
- package/bundled-skills/subagent-orchestrator/examples/debug-mission.md +72 -0
- package/bundled-skills/subagent-orchestrator/examples/nextjs-feature.md +87 -0
- package/bundled-skills/subagent-orchestrator/resources/mission-brief-template.md +43 -0
- package/bundled-skills/subagent-orchestrator/resources/quota-reference.md +73 -0
- package/bundled-skills/subagent-orchestrator/scripts/install.js +62 -0
- package/bundled-skills/tokenwise/SKILL.md +5 -1
- package/package.json +1 -1
- package/skills_index.json +108 -14
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"updatedAt": "2026-05-
|
|
3
|
+
"updatedAt": "2026-05-22T02:03:47.949Z",
|
|
4
4
|
"entries": [
|
|
5
5
|
"00-andruia-consultant",
|
|
6
6
|
"007",
|
|
@@ -269,6 +269,7 @@
|
|
|
269
269
|
"beautiful-prose",
|
|
270
270
|
"behavioral-modes",
|
|
271
271
|
"bevy-ecs-expert",
|
|
272
|
+
"bilig-workpaper",
|
|
272
273
|
"bill-gates",
|
|
273
274
|
"billing-automation",
|
|
274
275
|
"binary-analysis-patterns",
|
|
@@ -860,6 +861,7 @@
|
|
|
860
861
|
"memory-safety-patterns",
|
|
861
862
|
"memory-systems",
|
|
862
863
|
"mental-health-analyzer",
|
|
864
|
+
"mercury-mcp",
|
|
863
865
|
"mermaid-expert",
|
|
864
866
|
"metasploit-framework",
|
|
865
867
|
"micro-saas-launcher",
|
|
@@ -997,6 +999,7 @@
|
|
|
997
999
|
"performance-testing-review-multi-agent-review",
|
|
998
1000
|
"personal-tool-builder",
|
|
999
1001
|
"phase-gated-debugging",
|
|
1002
|
+
"photopea-embedded-editor",
|
|
1000
1003
|
"php-pro",
|
|
1001
1004
|
"pipecat-friday-agent",
|
|
1002
1005
|
"pipedrive-automation",
|
|
@@ -1266,6 +1269,7 @@
|
|
|
1266
1269
|
"stripe-automation",
|
|
1267
1270
|
"stripe-integration",
|
|
1268
1271
|
"subagent-driven-development",
|
|
1272
|
+
"subagent-orchestrator",
|
|
1269
1273
|
"subject-line-psychologist",
|
|
1270
1274
|
"supabase-automation",
|
|
1271
1275
|
"superpowers-lab",
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bilig-workpaper
|
|
3
|
+
description: "Use formula-backed WorkPaper JSON and MCP tools for agent spreadsheet tasks without driving Excel or a browser UI."
|
|
4
|
+
risk: unknown
|
|
5
|
+
source: community
|
|
6
|
+
date_added: "2026-05-21"
|
|
7
|
+
tags:
|
|
8
|
+
- spreadsheets
|
|
9
|
+
- formulas
|
|
10
|
+
- mcp
|
|
11
|
+
- xlsx
|
|
12
|
+
- typescript
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Bilig WorkPaper
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
|
|
19
|
+
Bilig WorkPaper gives agents a code-first workbook runtime for spreadsheet-style business logic. Use it when the task is easier to model as sheets and formulas, but the reliable path is to edit cells through an API, recalculate, read computed values back, and persist a JSON workbook document.
|
|
20
|
+
|
|
21
|
+
The main use case is replacing fragile spreadsheet UI automation with deterministic tool calls. It is useful for quote calculators, payout models, budget checks, import validation, and reduced XLSX formula bug reports.
|
|
22
|
+
|
|
23
|
+
## When To Use This Skill
|
|
24
|
+
|
|
25
|
+
Use this skill when the user needs to:
|
|
26
|
+
|
|
27
|
+
- work with spreadsheet formulas from a Node.js service, route, test, or agent tool;
|
|
28
|
+
- write workbook inputs and verify calculated outputs with readback proof;
|
|
29
|
+
- persist a formula workbook as reviewable WorkPaper JSON;
|
|
30
|
+
- expose a file-backed workbook through MCP tools;
|
|
31
|
+
- investigate an XLSX formula recalculation issue without automating Excel, LibreOffice, or a browser grid.
|
|
32
|
+
|
|
33
|
+
Do not use it for manual spreadsheet editing, VBA/macros, pivots, charts, COM automation, or exact desktop Excel behavior unless the user explicitly asks to compare against Excel as an oracle.
|
|
34
|
+
|
|
35
|
+
## Safer Command Pattern
|
|
36
|
+
|
|
37
|
+
Prefer argument arrays in MCP/client configuration. Do not shell-concatenate user-provided paths, sheet names, formulas, or cell addresses. Reject path or cell input containing newlines, backticks, `$(`, `;`, `&`, `|`, `<`, or `>` before using it in a command.
|
|
38
|
+
|
|
39
|
+
## Quick MCP Setup
|
|
40
|
+
|
|
41
|
+
First prove the package-owned challenge works:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"command": "npm",
|
|
46
|
+
"args": ["exec", "--package", "@bilig/workpaper", "--", "bilig-mcp-challenge"]
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Then run a writable file-backed MCP server:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"command": "npm",
|
|
55
|
+
"args": [
|
|
56
|
+
"exec",
|
|
57
|
+
"--package",
|
|
58
|
+
"@bilig/workpaper",
|
|
59
|
+
"--",
|
|
60
|
+
"bilig-workpaper-mcp",
|
|
61
|
+
"--workpaper",
|
|
62
|
+
"./pricing.workpaper.json",
|
|
63
|
+
"--init-demo-workpaper",
|
|
64
|
+
"--writable"
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Useful tools exposed by the MCP server:
|
|
70
|
+
|
|
71
|
+
- `list_sheets`
|
|
72
|
+
- `read_range`
|
|
73
|
+
- `read_cell`
|
|
74
|
+
- `set_cell_contents`
|
|
75
|
+
- `get_cell_display_value`
|
|
76
|
+
- `export_workpaper_document`
|
|
77
|
+
- `validate_formula`
|
|
78
|
+
|
|
79
|
+
After every write, read the dependent output cell and export the WorkPaper document. Do not claim success from the write call alone.
|
|
80
|
+
|
|
81
|
+
## Direct TypeScript Pattern
|
|
82
|
+
|
|
83
|
+
Use the package directly when workbook logic belongs inside application code:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import {
|
|
87
|
+
WorkPaper,
|
|
88
|
+
exportWorkPaperDocument,
|
|
89
|
+
serializeWorkPaperDocument,
|
|
90
|
+
} from "@bilig/workpaper";
|
|
91
|
+
|
|
92
|
+
const workbook = WorkPaper.buildFromSheets({
|
|
93
|
+
Inputs: [
|
|
94
|
+
["Metric", "Value"],
|
|
95
|
+
["Customers", 20],
|
|
96
|
+
["Average revenue", 1200],
|
|
97
|
+
],
|
|
98
|
+
Summary: [
|
|
99
|
+
["Metric", "Value"],
|
|
100
|
+
["Revenue", "=Inputs!B2*Inputs!B3"],
|
|
101
|
+
],
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const inputs = workbook.getSheetId("Inputs");
|
|
105
|
+
const summary = workbook.getSheetId("Summary");
|
|
106
|
+
if (inputs === undefined || summary === undefined) {
|
|
107
|
+
throw new Error("Workbook is missing required sheets");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
workbook.setCellContents({ sheet: inputs, row: 1, col: 1 }, 32);
|
|
111
|
+
const revenue = workbook.getCellDisplayValue({ sheet: summary, row: 1, col: 1 });
|
|
112
|
+
const saved = serializeWorkPaperDocument(
|
|
113
|
+
exportWorkPaperDocument(workbook, { includeConfig: true }),
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
console.log({ revenue, savedBytes: saved.length });
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Required Verification
|
|
120
|
+
|
|
121
|
+
A good agent response should include:
|
|
122
|
+
|
|
123
|
+
- exact sheet names and A1 cells edited;
|
|
124
|
+
- before values for important inputs and dependent outputs;
|
|
125
|
+
- after values read from the recalculated workbook;
|
|
126
|
+
- persistence evidence from exported or serialized WorkPaper JSON;
|
|
127
|
+
- restore or reimport proof when file boundaries matter;
|
|
128
|
+
- clear limitations for unsupported formulas or Excel-only behavior.
|
|
129
|
+
|
|
130
|
+
If any proof step fails, report the blocker instead of saying the workbook was updated.
|
|
131
|
+
|
|
132
|
+
## Limitations
|
|
133
|
+
|
|
134
|
+
- WorkPaper behavior is not a complete replacement for desktop Excel, VBA, pivots, charts, or UI automation.
|
|
135
|
+
- Formula compatibility depends on the Bilig runtime and should be verified against Excel when exact parity matters.
|
|
136
|
+
- MCP writes should remain scoped to trusted workbook paths and must be followed by readback validation.
|
|
137
|
+
|
|
138
|
+
## References
|
|
139
|
+
|
|
140
|
+
- Repository: https://github.com/proompteng/bilig
|
|
141
|
+
- Compact docs map: https://proompteng.github.io/bilig/llms.txt
|
|
142
|
+
- Agent handbook: https://proompteng.github.io/bilig/headless-workpaper-agent-handbook.html
|
|
143
|
+
- MCP server guide: https://proompteng.github.io/bilig/mcp-workpaper-tool-server.html
|
|
144
|
+
- XLSX formula clinic: https://proompteng.github.io/bilig/formula-bug-clinic.html
|
|
145
|
+
- Compatibility limits: https://proompteng.github.io/bilig/where-bilig-is-not-excel-compatible-yet.html
|
|
@@ -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,464+ skills."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Jetski/Cortex + Gemini: safe integration with 1,
|
|
6
|
+
# Jetski/Cortex + Gemini: safe integration with 1,464+ 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,464+ 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,464+ 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,464+ 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,464+ 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,464+ 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,464+ 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,464+ 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,464+ 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,464+ 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,464+ skill files** to your computer (default: `~/.gemini/antigravity/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,464+ 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,464+ 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,464+ 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,464+ 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,464+ 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,464+ 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,464+ total) │
|
|
205
205
|
└─────────────────────────────────────────┘
|
|
206
206
|
```
|
|
207
207
|
|
|
@@ -9,6 +9,9 @@ date_added: "2026-05-10"
|
|
|
9
9
|
license: "MIT"
|
|
10
10
|
license_source: "https://github.com/ejentum/ejentum-mcp/blob/main/LICENSE"
|
|
11
11
|
plugin:
|
|
12
|
+
targets:
|
|
13
|
+
codex: blocked
|
|
14
|
+
claude: blocked
|
|
12
15
|
setup:
|
|
13
16
|
type: manual
|
|
14
17
|
summary: "Install the ejentum-mcp MCP server (`npx -y ejentum-mcp`) and provide an EJENTUM_API_KEY env var (free tier: 100 calls, no card, at https://ejentum.com/pricing). Add the server to your client's mcpServers config (Claude Code, Cursor, Cline, Windsurf, Codex CLI, Gemini CLI, Antigravity, or VS Code Copilot Chat)."
|
|
@@ -9,6 +9,11 @@ date_added: "2026-05-09"
|
|
|
9
9
|
license: MIT
|
|
10
10
|
license_source: "https://github.com/adelaidasofia/ai-brain-starter/blob/main/LICENSE"
|
|
11
11
|
upstream: "https://github.com/adelaidasofia/ai-brain-starter/tree/main/skills/ingest-youtube"
|
|
12
|
+
plugin:
|
|
13
|
+
setup:
|
|
14
|
+
type: manual
|
|
15
|
+
summary: "Install yt-dlp locally before running ingest.py; the script only accepts http(s) YouTube video URLs and writes markdown into the selected vault."
|
|
16
|
+
docs: "SKILL.md"
|
|
12
17
|
---
|
|
13
18
|
|
|
14
19
|
# ingest-youtube — YouTube-to-vault connector
|
|
@@ -34,7 +39,7 @@ Do NOT use for:
|
|
|
34
39
|
|
|
35
40
|
1. Parse the input as one YouTube video URL.
|
|
36
41
|
2. Verify `yt-dlp` is installed. If not, the script exits with install instructions: `brew install yt-dlp` (macOS) or `pip3 install --user yt-dlp`.
|
|
37
|
-
3.
|
|
42
|
+
3. Validate the URL as a single http(s) YouTube video and call `yt-dlp --ignore-config --list-subs -- <url>` to enumerate available subtitles.
|
|
38
43
|
4. Subtitle priority: manual subs > auto-generated captions. Manual subs preserve creator-provided punctuation and speaker labels; auto-gen is uppercase + no punctuation.
|
|
39
44
|
5. Download the highest-priority subtitle as VTT via `yt-dlp --write-sub --sub-lang <lang> --skip-download`. Default language preference: `en,es` (English first, Spanish second).
|
|
40
45
|
6. Strip VTT timing markers and merge into clean prose paragraphs. Deduplicate repeated lines (auto-generated VTTs are line-doubled). Preserve speaker labels if the source had them.
|
|
@@ -22,6 +22,7 @@ Defaults:
|
|
|
22
22
|
from __future__ import annotations
|
|
23
23
|
|
|
24
24
|
import argparse
|
|
25
|
+
import html
|
|
25
26
|
import json
|
|
26
27
|
import os
|
|
27
28
|
import re
|
|
@@ -31,10 +32,13 @@ import sys
|
|
|
31
32
|
import tempfile
|
|
32
33
|
from datetime import datetime, timezone
|
|
33
34
|
from pathlib import Path
|
|
35
|
+
from urllib.parse import parse_qs, urlparse, urlunparse
|
|
34
36
|
|
|
35
37
|
VTT_TIMING_RE = re.compile(r"\d{2}:\d{2}:\d{2}\.\d{3} --> \d{2}:\d{2}:\d{2}\.\d{3}.*")
|
|
36
38
|
VTT_HEADER_RE = re.compile(r"^(WEBVTT|Kind:|Language:|NOTE\s|X-TIMESTAMP-MAP)", re.MULTILINE)
|
|
37
39
|
SLUG_RE = re.compile(r"[^a-z0-9]+")
|
|
40
|
+
YOUTUBE_VIDEO_ID_RE = re.compile(r"^[A-Za-z0-9_-]{11}$")
|
|
41
|
+
SUBPROCESS_TIMEOUT_SECONDS = 60
|
|
38
42
|
SEED_KEYWORDS = (
|
|
39
43
|
"decision", "framework", "model", "principle", "the lesson is",
|
|
40
44
|
"playbook", "anti-pattern", "case study", "what i learned",
|
|
@@ -42,6 +46,57 @@ SEED_KEYWORDS = (
|
|
|
42
46
|
)
|
|
43
47
|
|
|
44
48
|
|
|
49
|
+
def validate_youtube_url(raw_url: str) -> str:
|
|
50
|
+
if not raw_url or raw_url.startswith("-") or any(ord(ch) < 32 or ord(ch) == 127 for ch in raw_url):
|
|
51
|
+
raise ValueError("URL must be a valid http(s) YouTube video URL")
|
|
52
|
+
|
|
53
|
+
parsed = urlparse(raw_url)
|
|
54
|
+
if parsed.scheme not in {"http", "https"}:
|
|
55
|
+
raise ValueError("URL must use http or https")
|
|
56
|
+
|
|
57
|
+
host = parsed.hostname.lower() if parsed.hostname else ""
|
|
58
|
+
video_id = ""
|
|
59
|
+
|
|
60
|
+
if host in {"youtube.com", "www.youtube.com", "m.youtube.com", "music.youtube.com"}:
|
|
61
|
+
parts = [part for part in parsed.path.split("/") if part]
|
|
62
|
+
if parsed.path == "/watch":
|
|
63
|
+
video_id = parse_qs(parsed.query).get("v", [""])[0]
|
|
64
|
+
elif len(parts) >= 2 and parts[0] in {"shorts", "embed", "v"}:
|
|
65
|
+
video_id = parts[1]
|
|
66
|
+
elif host == "youtu.be":
|
|
67
|
+
video_id = parsed.path.lstrip("/").split("/", 1)[0]
|
|
68
|
+
|
|
69
|
+
if not YOUTUBE_VIDEO_ID_RE.fullmatch(video_id):
|
|
70
|
+
raise ValueError("URL must point to a single YouTube video")
|
|
71
|
+
|
|
72
|
+
return urlunparse(("https", "www.youtube.com", "/watch", "", f"v={video_id}", ""))
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def run_ytdlp(args: list[str]) -> subprocess.CompletedProcess[str]:
|
|
76
|
+
return subprocess.run(
|
|
77
|
+
args,
|
|
78
|
+
capture_output=True,
|
|
79
|
+
text=True,
|
|
80
|
+
check=False,
|
|
81
|
+
timeout=SUBPROCESS_TIMEOUT_SECONDS,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def yaml_scalar(value: object) -> str:
|
|
86
|
+
if isinstance(value, bool):
|
|
87
|
+
return "true" if value else "false"
|
|
88
|
+
if isinstance(value, (int, float)):
|
|
89
|
+
return str(value)
|
|
90
|
+
if value is None:
|
|
91
|
+
return '""'
|
|
92
|
+
return json.dumps(str(value), ensure_ascii=False)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def markdown_text(value: object) -> str:
|
|
96
|
+
text = html.escape(str(value), quote=False)
|
|
97
|
+
return re.sub(r"([\\`*_{}\[\]()#+.!|-])", r"\\\1", text)
|
|
98
|
+
|
|
99
|
+
|
|
45
100
|
def slugify(text: str, max_len: int = 60) -> str:
|
|
46
101
|
s = SLUG_RE.sub("-", text.lower()).strip("-")
|
|
47
102
|
return s[:max_len].rstrip("-") or "untitled"
|
|
@@ -59,10 +114,7 @@ def require_bin(name: str) -> str:
|
|
|
59
114
|
|
|
60
115
|
|
|
61
116
|
def fetch_metadata(url: str, ytdlp: str) -> dict:
|
|
62
|
-
proc =
|
|
63
|
-
[ytdlp, "--skip-download", "--print-json", "--no-warnings", url],
|
|
64
|
-
capture_output=True, text=True, check=False,
|
|
65
|
-
)
|
|
117
|
+
proc = run_ytdlp([ytdlp, "--ignore-config", "--skip-download", "--print-json", "--no-warnings", "--", url])
|
|
66
118
|
if proc.returncode != 0:
|
|
67
119
|
sys.stderr.write(f"yt-dlp metadata fetch failed:\n{proc.stderr}\n")
|
|
68
120
|
sys.exit(3)
|
|
@@ -70,10 +122,7 @@ def fetch_metadata(url: str, ytdlp: str) -> dict:
|
|
|
70
122
|
|
|
71
123
|
|
|
72
124
|
def list_subs(url: str, ytdlp: str) -> str:
|
|
73
|
-
proc =
|
|
74
|
-
[ytdlp, "--list-subs", "--skip-download", "--no-warnings", url],
|
|
75
|
-
capture_output=True, text=True, check=False,
|
|
76
|
-
)
|
|
125
|
+
proc = run_ytdlp([ytdlp, "--ignore-config", "--list-subs", "--skip-download", "--no-warnings", "--", url])
|
|
77
126
|
return proc.stdout
|
|
78
127
|
|
|
79
128
|
|
|
@@ -117,11 +166,10 @@ def pick_lang(prefs: list[str], manual: set[str], auto: set[str]) -> tuple[str,
|
|
|
117
166
|
def download_subs(url: str, lang: str, source: str, ytdlp: str, workdir: Path) -> Path:
|
|
118
167
|
flag = "--write-sub" if source == "manual" else "--write-auto-sub"
|
|
119
168
|
out_template = str(workdir / "%(id)s.%(ext)s")
|
|
120
|
-
proc =
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
)
|
|
169
|
+
proc = run_ytdlp([
|
|
170
|
+
ytdlp, "--ignore-config", flag, "--sub-lang", lang, "--skip-download",
|
|
171
|
+
"--sub-format", "vtt", "-o", out_template, "--no-warnings", "--", url,
|
|
172
|
+
])
|
|
125
173
|
if proc.returncode != 0:
|
|
126
174
|
sys.stderr.write(f"yt-dlp subtitle download failed:\n{proc.stderr}\n")
|
|
127
175
|
sys.exit(4)
|
|
@@ -171,11 +219,7 @@ def write_vault_file(
|
|
|
171
219
|
target = target_dir / f"{upload_date}-{video_slug}.md"
|
|
172
220
|
yaml_lines = ["---"]
|
|
173
221
|
for k, v in frontmatter.items():
|
|
174
|
-
|
|
175
|
-
v = v.replace('"', '\\"')
|
|
176
|
-
yaml_lines.append(f'{k}: "{v}"')
|
|
177
|
-
else:
|
|
178
|
-
yaml_lines.append(f"{k}: {v}")
|
|
222
|
+
yaml_lines.append(f"{k}: {yaml_scalar(v)}")
|
|
179
223
|
yaml_lines.append("---")
|
|
180
224
|
target.write_text("\n".join(yaml_lines) + "\n\n" + body + "\n", encoding="utf-8")
|
|
181
225
|
return target
|
|
@@ -193,14 +237,14 @@ def write_seed_stub(
|
|
|
193
237
|
"---\n"
|
|
194
238
|
"type: capture\n"
|
|
195
239
|
"source: youtube\n"
|
|
196
|
-
f"video_url: {video_url}\n"
|
|
197
|
-
f"detected_at: {datetime.now(timezone.utc).isoformat()}\n"
|
|
198
|
-
f"keywords: {', '.join(seeds)}\n"
|
|
240
|
+
f"video_url: {yaml_scalar(video_url)}\n"
|
|
241
|
+
f"detected_at: {yaml_scalar(datetime.now(timezone.utc).isoformat())}\n"
|
|
242
|
+
f"keywords: {yaml_scalar(', '.join(seeds))}\n"
|
|
199
243
|
"status: open\n"
|
|
200
244
|
"---\n\n"
|
|
201
|
-
f"# Capture seed: {video_title}\n\n"
|
|
202
|
-
f"Trigger keywords detected in transcript: {', '.join(seeds)}.\n\n"
|
|
203
|
-
f"Source: {video_url}\n\n"
|
|
245
|
+
f"# Capture seed: {markdown_text(video_title)}\n\n"
|
|
246
|
+
f"Trigger keywords detected in transcript: {markdown_text(', '.join(seeds))}.\n\n"
|
|
247
|
+
f"Source: {markdown_text(video_url)}\n\n"
|
|
204
248
|
"## Notes\n\n(fill in)\n"
|
|
205
249
|
)
|
|
206
250
|
target.write_text(body, encoding="utf-8")
|
|
@@ -220,10 +264,16 @@ def main() -> int:
|
|
|
220
264
|
sys.stderr.write(f"Vault root not a directory: {vault_root}\n")
|
|
221
265
|
return 1
|
|
222
266
|
|
|
267
|
+
try:
|
|
268
|
+
youtube_url = validate_youtube_url(args.url)
|
|
269
|
+
except ValueError as exc:
|
|
270
|
+
sys.stderr.write(f"Invalid YouTube URL: {exc}\n")
|
|
271
|
+
return 2
|
|
272
|
+
|
|
223
273
|
ytdlp = require_bin("yt-dlp")
|
|
224
274
|
prefs = [c.strip() for c in args.lang.split(",") if c.strip()]
|
|
225
275
|
|
|
226
|
-
meta = fetch_metadata(
|
|
276
|
+
meta = fetch_metadata(youtube_url, ytdlp)
|
|
227
277
|
video_id = meta.get("id", "unknown")
|
|
228
278
|
title = meta.get("title", "Untitled")
|
|
229
279
|
channel = meta.get("channel") or meta.get("uploader") or "unknown-channel"
|
|
@@ -236,7 +286,7 @@ def main() -> int:
|
|
|
236
286
|
datetime.now().strftime("%Y-%m-%d")
|
|
237
287
|
)
|
|
238
288
|
|
|
239
|
-
listing = list_subs(
|
|
289
|
+
listing = list_subs(youtube_url, ytdlp)
|
|
240
290
|
manual, auto = parse_available_subs(listing)
|
|
241
291
|
pick = pick_lang(prefs, manual, auto)
|
|
242
292
|
|
|
@@ -247,7 +297,7 @@ def main() -> int:
|
|
|
247
297
|
if pick:
|
|
248
298
|
lang_code, sub_source = pick
|
|
249
299
|
with tempfile.TemporaryDirectory() as td:
|
|
250
|
-
vtt = download_subs(
|
|
300
|
+
vtt = download_subs(youtube_url, lang_code, sub_source, ytdlp, Path(td))
|
|
251
301
|
transcript = clean_vtt(vtt)
|
|
252
302
|
elif args.whisper:
|
|
253
303
|
sys.stderr.write("Whisper fallback requested but not yet implemented in v0.1.\n")
|
|
@@ -261,18 +311,18 @@ def main() -> int:
|
|
|
261
311
|
seeds = detect_seeds(transcript) if transcript else []
|
|
262
312
|
|
|
263
313
|
body = transcript or (
|
|
264
|
-
f"# {title}\n\n"
|
|
314
|
+
f"# {markdown_text(title)}\n\n"
|
|
265
315
|
f"No subtitles or auto-captions available for this video.\n\n"
|
|
266
316
|
"To capture this transcript, add captions to the source video or transcribe the audio "
|
|
267
317
|
"with your local Whisper workflow and re-run ingest.\n\n"
|
|
268
|
-
f"Source: {
|
|
318
|
+
f"Source: {markdown_text(youtube_url)}\n"
|
|
269
319
|
)
|
|
270
320
|
|
|
271
321
|
fm = {
|
|
272
322
|
"type": "external-input",
|
|
273
323
|
"source": "youtube",
|
|
274
324
|
"video_id": video_id,
|
|
275
|
-
"url":
|
|
325
|
+
"url": youtube_url,
|
|
276
326
|
"channel": channel,
|
|
277
327
|
"channel_url": meta.get("channel_url", ""),
|
|
278
328
|
"title": title,
|
|
@@ -288,7 +338,7 @@ def main() -> int:
|
|
|
288
338
|
seed_paths: list[Path] = []
|
|
289
339
|
if seeds:
|
|
290
340
|
seed_paths.append(
|
|
291
|
-
write_seed_stub(vault_root, upload_date, channel_slug, video_id, seeds,
|
|
341
|
+
write_seed_stub(vault_root, upload_date, channel_slug, video_id, seeds, youtube_url, title)
|
|
292
342
|
)
|
|
293
343
|
|
|
294
344
|
seed_str = f" Seeds at: {', '.join(str(p) for p in seed_paths)}." if seed_paths else ""
|
|
@@ -9,8 +9,6 @@ date_added: "2026-02-27"
|
|
|
9
9
|
|
|
10
10
|
> AUTHORIZED USE ONLY: Use this skill only for authorized security assessments, defensive validation, or controlled educational environments.
|
|
11
11
|
|
|
12
|
-
<!-- security-allowlist: curl-pipe-bash -->
|
|
13
|
-
|
|
14
12
|
# Linux Privilege Escalation
|
|
15
13
|
|
|
16
14
|
## Purpose
|
|
@@ -145,8 +143,11 @@ echo $PATH
|
|
|
145
143
|
Deploy automated scripts for comprehensive enumeration:
|
|
146
144
|
|
|
147
145
|
```bash
|
|
148
|
-
# LinPEAS
|
|
149
|
-
curl -L https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh
|
|
146
|
+
# LinPEAS: download first, inspect the script, then execute only in an authorized lab
|
|
147
|
+
curl -L -o linpeas.sh https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh
|
|
148
|
+
less linpeas.sh
|
|
149
|
+
chmod +x linpeas.sh
|
|
150
|
+
./linpeas.sh
|
|
150
151
|
|
|
151
152
|
# LinEnum
|
|
152
153
|
./LinEnum.sh -t
|