@nano-step/skill-manager 5.2.0 → 5.2.2

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/dist/utils.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export declare const MANAGER_VERSION = "5.2.0";
1
+ export declare const MANAGER_VERSION = "5.2.2";
2
2
  export interface SkillManifest {
3
3
  name: string;
4
4
  version: string;
package/dist/utils.js CHANGED
@@ -13,7 +13,7 @@ exports.writeText = writeText;
13
13
  const path_1 = __importDefault(require("path"));
14
14
  const os_1 = __importDefault(require("os"));
15
15
  const fs_extra_1 = __importDefault(require("fs-extra"));
16
- exports.MANAGER_VERSION = "5.2.0";
16
+ exports.MANAGER_VERSION = "5.2.2";
17
17
  async function detectOpenCodePaths() {
18
18
  const homeConfig = path_1.default.join(os_1.default.homedir(), ".config", "opencode");
19
19
  const cwd = process.cwd();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nano-step/skill-manager",
3
- "version": "5.2.0",
3
+ "version": "5.2.2",
4
4
  "description": "CLI tool that installs and manages AI agent skills, MCP tool routing, and workflow configurations.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,303 @@
1
+ ---
2
+ name: pdf
3
+ description: "Comprehensive PDF manipulation toolkit for extracting text and tables, creating new PDFs, merging/splitting documents, and handling forms. Use when filling PDF forms or programmatically processing, generating, or analyzing PDF documents."
4
+ compatibility: "OpenCode"
5
+ metadata:
6
+ author: openclaw/skillmd
7
+ version: "1.0.0"
8
+ ---
9
+
10
+ # PDF Processing Guide
11
+
12
+ ## When This Skill Activates
13
+
14
+ Activate when the user asks to:
15
+ - Extract text or tables from PDFs
16
+ - Create, merge, split, or rotate PDFs
17
+ - Add watermarks or password protection
18
+ - OCR scanned PDFs
19
+ - Fill PDF forms
20
+ - Convert PDFs to text
21
+
22
+ ## Quick Start
23
+ ```python
24
+ from pypdf import PdfReader, PdfWriter
25
+
26
+ # Read a PDF
27
+ reader = PdfReader("document.pdf")
28
+ print(f"Pages: {len(reader.pages)}")
29
+
30
+ # Extract text
31
+ text = ""
32
+ for page in reader.pages:
33
+ text += page.extract_text()
34
+ ```
35
+
36
+ ## Python Libraries
37
+
38
+ ### pypdf - Basic Operations
39
+
40
+ #### Merge PDFs
41
+ ```python
42
+ from pypdf import PdfWriter, PdfReader
43
+
44
+ writer = PdfWriter()
45
+ for pdf_file in ["doc1.pdf", "doc2.pdf", "doc3.pdf"]:
46
+ reader = PdfReader(pdf_file)
47
+ for page in reader.pages:
48
+ writer.add_page(page)
49
+
50
+ with open("merged.pdf", "wb") as output:
51
+ writer.write(output)
52
+ ```
53
+
54
+ #### Split PDF
55
+ ```python
56
+ reader = PdfReader("input.pdf")
57
+ for i, page in enumerate(reader.pages):
58
+ writer = PdfWriter()
59
+ writer.add_page(page)
60
+ with open(f"page_{i+1}.pdf", "wb") as output:
61
+ writer.write(output)
62
+ ```
63
+
64
+ #### Rotate Pages
65
+ ```python
66
+ reader = PdfReader("input.pdf")
67
+ writer = PdfWriter()
68
+
69
+ page = reader.pages[0]
70
+ page.rotate(90) # Rotate 90 degrees clockwise
71
+ writer.add_page(page)
72
+
73
+ with open("rotated.pdf", "wb") as output:
74
+ writer.write(output)
75
+ ```
76
+
77
+ #### Extract Metadata
78
+ ```python
79
+ reader = PdfReader("document.pdf")
80
+ meta = reader.metadata
81
+ print(f"Author: {meta.author}")
82
+ print(f"Title: {meta.title}")
83
+ print(f"Subject: {meta.subject}")
84
+ print(f"Creator: {meta.creator}")
85
+ ```
86
+
87
+ ### pdfplumber - Text and Table Extraction
88
+
89
+ #### Extract Text
90
+ ```python
91
+ import pdfplumber
92
+
93
+ with pdfplumber.open("document.pdf") as pdf:
94
+ for page in pdf.pages:
95
+ text = page.extract_text()
96
+ print(text)
97
+ ```
98
+
99
+ #### Extract Tables
100
+ ```python
101
+ with pdfplumber.open("document.pdf") as pdf:
102
+ for i, page in enumerate(pdf.pages):
103
+ tables = page.extract_tables()
104
+ for j, table in enumerate(tables):
105
+ print(f"Table {j+1} on page {i+1}:")
106
+ for row in table:
107
+ print(row)
108
+ ```
109
+
110
+ #### Extract Tables to DataFrame
111
+ ```python
112
+ import pdfplumber
113
+ import pandas as pd
114
+
115
+ with pdfplumber.open("document.pdf") as pdf:
116
+ page = pdf.pages[0]
117
+ table = page.extract_table()
118
+ df = pd.DataFrame(table[1:], columns=table[0])
119
+ print(df)
120
+ ```
121
+
122
+ ### reportlab - Create PDFs
123
+
124
+ #### Simple PDF
125
+ ```python
126
+ from reportlab.lib.pagesizes import letter
127
+ from reportlab.pdfgen import canvas
128
+
129
+ c = canvas.Canvas("hello.pdf", pagesize=letter)
130
+ width, height = letter
131
+
132
+ c.drawString(100, height - 100, "Hello World!")
133
+ c.line(100, height - 140, 400, height - 140)
134
+ c.save()
135
+ ```
136
+
137
+ #### Multi-page with Platypus
138
+ ```python
139
+ from reportlab.lib.pagesizes import letter
140
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
141
+ from reportlab.lib.styles import getSampleStyleSheet
142
+
143
+ doc = SimpleDocTemplate("report.pdf", pagesize=letter)
144
+ styles = getSampleStyleSheet()
145
+ story = []
146
+
147
+ story.append(Paragraph("Report Title", styles['Title']))
148
+ story.append(Spacer(1, 12))
149
+ story.append(Paragraph("This is the body text.", styles['Normal']))
150
+
151
+ doc.build(story)
152
+ ```
153
+
154
+ ## Command-Line Tools
155
+
156
+ ### pdftotext (poppler-utils)
157
+ ```bash
158
+ # Extract text
159
+ pdftotext input.pdf output.txt
160
+
161
+ # Preserve layout
162
+ pdftotext -layout input.pdf output.txt
163
+
164
+ # Specific pages
165
+ pdftotext -f 1 -l 5 input.pdf output.txt
166
+ ```
167
+
168
+ ### qpdf
169
+ ```bash
170
+ # Merge PDFs
171
+ qpdf --empty --pages file1.pdf file2.pdf -- merged.pdf
172
+
173
+ # Split pages
174
+ qpdf input.pdf --pages . 1-5 -- pages1-5.pdf
175
+
176
+ # Rotate pages
177
+ qpdf input.pdf output.pdf --rotate=+90:1
178
+
179
+ # Remove password
180
+ qpdf --password=mypassword --decrypt encrypted.pdf decrypted.pdf
181
+
182
+ # Linearize (optimize for web)
183
+ qpdf --linearize input.pdf output.pdf
184
+ ```
185
+
186
+ ## Common Tasks
187
+
188
+ ### OCR Scanned PDFs
189
+ ```python
190
+ import pytesseract
191
+ from pdf2image import convert_from_path
192
+
193
+ images = convert_from_path('scanned.pdf')
194
+ text = ""
195
+ for i, image in enumerate(images):
196
+ text += f"Page {i+1}:\n"
197
+ text += pytesseract.image_to_string(image)
198
+ text += "\n\n"
199
+ ```
200
+
201
+ ### Add Watermark
202
+ ```python
203
+ from pypdf import PdfReader, PdfWriter
204
+
205
+ watermark = PdfReader("watermark.pdf").pages[0]
206
+ reader = PdfReader("document.pdf")
207
+ writer = PdfWriter()
208
+
209
+ for page in reader.pages:
210
+ page.merge_page(watermark)
211
+ writer.add_page(page)
212
+
213
+ with open("watermarked.pdf", "wb") as output:
214
+ writer.write(output)
215
+ ```
216
+
217
+ ### Password Protection
218
+ ```python
219
+ from pypdf import PdfReader, PdfWriter
220
+
221
+ reader = PdfReader("input.pdf")
222
+ writer = PdfWriter()
223
+
224
+ for page in reader.pages:
225
+ writer.add_page(page)
226
+
227
+ writer.encrypt("userpassword", "ownerpassword")
228
+
229
+ with open("encrypted.pdf", "wb") as output:
230
+ writer.write(output)
231
+ ```
232
+
233
+ ### Fill PDF Forms
234
+ ```python
235
+ from pypdf import PdfReader, PdfWriter
236
+
237
+ reader = PdfReader("form.pdf")
238
+ writer = PdfWriter()
239
+ writer.append(reader)
240
+
241
+ # Get form field names
242
+ fields = reader.get_fields()
243
+ for name, field in fields.items():
244
+ print(f"Field: {name}, Type: {field.get('/FT')}")
245
+
246
+ # Fill fields
247
+ writer.update_page_form_field_values(
248
+ writer.pages[0],
249
+ {"field_name": "value", "another_field": "another_value"}
250
+ )
251
+
252
+ with open("filled_form.pdf", "wb") as output:
253
+ writer.write(output)
254
+ ```
255
+
256
+ ### PDF to Images
257
+ ```python
258
+ from pdf2image import convert_from_path
259
+
260
+ # Convert all pages
261
+ images = convert_from_path('document.pdf', dpi=300)
262
+ for i, image in enumerate(images):
263
+ image.save(f'page_{i+1}.png', 'PNG')
264
+
265
+ # Convert specific pages
266
+ images = convert_from_path('document.pdf', first_page=1, last_page=3)
267
+ ```
268
+
269
+ ## Installation Commands
270
+
271
+ ```bash
272
+ # Core libraries
273
+ pip install pypdf pdfplumber reportlab
274
+
275
+ # OCR support
276
+ pip install pytesseract pdf2image
277
+ # Also needs: apt-get install tesseract-ocr poppler-utils
278
+
279
+ # CLI tools
280
+ apt-get install poppler-utils qpdf
281
+
282
+ # All at once
283
+ pip install pypdf pdfplumber reportlab pytesseract pdf2image
284
+ ```
285
+
286
+ ## Quick Reference
287
+
288
+ | Task | Best Tool | Command/Code |
289
+ |------|-----------|--------------|
290
+ | Read/extract text | pdfplumber | `page.extract_text()` |
291
+ | Extract tables | pdfplumber | `page.extract_tables()` |
292
+ | Merge PDFs | pypdf | `writer.add_page(page)` |
293
+ | Split PDFs | pypdf | One page per PdfWriter |
294
+ | Rotate pages | pypdf | `page.rotate(90)` |
295
+ | Create PDFs | reportlab | Canvas or Platypus |
296
+ | Fill forms | pypdf | `update_page_form_field_values()` |
297
+ | Add watermark | pypdf | `page.merge_page(watermark)` |
298
+ | Password protect | pypdf | `writer.encrypt()` |
299
+ | OCR scanned PDFs | pytesseract + pdf2image | Convert to image first |
300
+ | CLI text extract | poppler-utils | `pdftotext input.pdf` |
301
+ | CLI merge/split | qpdf | `qpdf --empty --pages ...` |
302
+ | PDF to images | pdf2image | `convert_from_path()` |
303
+ | Extract metadata | pypdf | `reader.metadata` |
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "pdf",
3
+ "version": "1.0.0",
4
+ "description": "PDF manipulation toolkit \u2014 extract text/tables, create, merge, split, rotate, OCR, fill forms, watermark, and password protect",
5
+ "compatibility": "OpenCode",
6
+ "agent": null,
7
+ "commands": [],
8
+ "tags": [
9
+ "pdf",
10
+ "extract",
11
+ "merge",
12
+ "ocr",
13
+ "forms",
14
+ "reportlab",
15
+ "pypdf"
16
+ ]
17
+ }
@@ -0,0 +1,118 @@
1
+ ---
2
+ name: rtk-setup
3
+ description: "One-time setup + ongoing enforcement of RTK (Rust Token Killer) across sessions and subagents. Persists RTK instructions to AGENTS.md and propagates to delegated tasks."
4
+ compatibility: "OpenCode with RTK binary installed"
5
+ metadata:
6
+ author: Sisyphus
7
+ version: "1.0.0"
8
+ ---
9
+
10
+ # RTK Setup — Persistent RTK Enforcement
11
+
12
+ This skill performs **setup** (persist RTK instructions) and **ongoing enforcement** (use rtk, propagate to subagents).
13
+
14
+ ## Phase 1: Detection (ALWAYS run first)
15
+
16
+ Run these checks before anything else:
17
+
18
+ ```bash
19
+ rtk --version
20
+ ```
21
+
22
+ - If `rtk` is NOT found → **STOP**. Tell the user: "RTK is not installed. Install it first: `cargo install --git https://github.com/rtk-ai/rtk --locked`". Do NOT proceed with setup.
23
+ - If `rtk` IS found → continue.
24
+
25
+ ### Check oh-my-opencode
26
+
27
+ ```bash
28
+ ls ~/.config/opencode/oh-my-opencode.json 2>/dev/null
29
+ ```
30
+
31
+ - If found → omo is installed. Note this for Phase 2.
32
+ - If not found → skip omo-specific steps.
33
+
34
+ ## Phase 2: Setup (persist to AGENTS.md)
35
+
36
+ Check BOTH of these files for the RTK managed block:
37
+
38
+ 1. **User-level**: `~/.config/opencode/AGENTS.md`
39
+ 2. **Project-level**: `./AGENTS.md` (current workspace)
40
+
41
+ For EACH file, check if `<!-- RTK:START -->` exists. If it does NOT exist, **append** the following managed block to the file (create the file if it doesn't exist). NEVER replace existing content — only append.
42
+
43
+ ```markdown
44
+ <!-- RTK:START -->
45
+ <!-- Managed block - do not edit manually. Updated by: rtk-setup skill -->
46
+
47
+ ## RTK Token Optimizer
48
+
49
+ This project uses **RTK** (Rust Token Killer) to reduce LLM token consumption by 60-90%.
50
+
51
+ ### Rules for ALL agents (including subagents)
52
+
53
+ **ALWAYS** prefix these commands with `rtk`:
54
+
55
+ | Command | Use instead |
56
+ |---------|-------------|
57
+ | `git status/diff/log/add/commit/push/pull` | `rtk git ...` |
58
+ | `ls`, `ls -la` | `rtk ls` |
59
+ | `cat file` | `rtk read file` |
60
+ | `grep pattern .` / `rg pattern` | `rtk grep pattern .` |
61
+ | `npm test` / `cargo test` / `pytest` | `rtk test <cmd>` |
62
+ | `npm run build` / `cargo build` | `rtk err <cmd>` |
63
+ | `gh pr list/view` | `rtk gh pr list/view` |
64
+ | `docker ps` | `rtk docker ps` |
65
+ | `eslint` / `tsc` | `rtk lint` / `rtk tsc` |
66
+
67
+ **Do NOT** prefix: `npx`, `npm install`, `pip install`, `node`, `python3`, heredocs, piped commands.
68
+
69
+ <!-- RTK:END -->
70
+ ```
71
+
72
+ ### If oh-my-opencode is installed
73
+
74
+ After updating AGENTS.md files, check if the omo config at `~/.config/opencode/oh-my-opencode.json` has `prompt_append` fields on agents. If it does, inform the user:
75
+
76
+ > "omo is installed. For maximum coverage, you can add RTK instructions to agent `prompt_append` fields in `oh-my-opencode.json`. This ensures subagents always use rtk even when AGENTS.md is not loaded. Want me to do this?"
77
+
78
+ **Only modify `oh-my-opencode.json` if the user explicitly confirms.** If they confirm, append this line to the `prompt_append` of `sisyphus-junior` agent (the primary coder):
79
+
80
+ ```
81
+ \n\nRTK: ALWAYS prefix shell commands with `rtk` when available (git, ls, grep, test, build). Example: `rtk git status` not `git status`.
82
+ ```
83
+
84
+ Do NOT modify other agents' prompt_append unless asked.
85
+
86
+ ## Phase 3: Ongoing Enforcement (EVERY time this skill is loaded)
87
+
88
+ ### For the current agent
89
+
90
+ From this point forward in this session, **you MUST**:
91
+
92
+ 1. **Use `rtk` prefix** for all supported shell commands (see table above)
93
+ 2. **Verify rtk is available** before first use: `rtk --version`
94
+ 3. If rtk is not available in the current environment, fall back to normal commands silently
95
+
96
+ ### For subagent delegation
97
+
98
+ When delegating ANY task that involves shell commands (via `task()`, `delegate_task()`, or any delegation mechanism), **ALWAYS** add this to the `MUST DO` section of the delegation prompt:
99
+
100
+ ```
101
+ - Use `rtk` prefix for shell commands when available (git, ls, grep, test, build). Run `rtk --version` first to check. If not installed, use normal commands. Examples: `rtk git status`, `rtk ls`, `rtk grep pattern .`, `rtk test npm test`.
102
+ ```
103
+
104
+ This is NON-NEGOTIABLE. Subagents do not read AGENTS.md, so RTK instructions MUST be passed explicitly in every delegation prompt.
105
+
106
+ ### Quick reference for subagent prompts
107
+
108
+ Copy-paste this one-liner into MUST DO sections:
109
+
110
+ > Use `rtk` prefix for all shell commands (git, ls, grep, test, build, docker). Example: `rtk git status` not `git status`. If `rtk` is not found, use normal commands.
111
+
112
+ ## Summary
113
+
114
+ | Phase | When | What |
115
+ |-------|------|------|
116
+ | Detection | Always first | Check rtk installed, check omo |
117
+ | Setup | Once (idempotent) | Append RTK block to AGENTS.md (user + project) |
118
+ | Enforcement | Every session | Use rtk yourself, propagate to all subagents |
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "rtk-setup",
3
+ "version": "1.0.0",
4
+ "description": "One-time setup + ongoing enforcement of RTK (Rust Token Killer) across sessions and subagents \u2014 reduces token consumption by 60-90%",
5
+ "compatibility": "OpenCode with RTK binary installed",
6
+ "agent": null,
7
+ "commands": [],
8
+ "tags": [
9
+ "rtk",
10
+ "token-saving",
11
+ "optimization",
12
+ "setup"
13
+ ]
14
+ }