opencode-skills-collection 3.1.14 → 3.1.16

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 (164) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +36 -1
  2. package/bundled-skills/007/scripts/full_audit.py +34 -22
  3. package/bundled-skills/007/scripts/quick_scan.py +24 -15
  4. package/bundled-skills/007/scripts/scanners/dependency_scanner.py +31 -19
  5. package/bundled-skills/007/scripts/scanners/injection_scanner.py +24 -14
  6. package/bundled-skills/007/scripts/scanners/secrets_scanner.py +24 -14
  7. package/bundled-skills/007/scripts/score_calculator.py +25 -13
  8. package/bundled-skills/2slides-ppt-generator/scripts/download_slides_pages_voices.py +15 -1
  9. package/bundled-skills/aegisops-ai/SKILL.md +1 -1
  10. package/bundled-skills/agent-self-scheduling/SKILL.md +88 -0
  11. package/bundled-skills/alpha-vantage/SKILL.md +3 -1
  12. package/bundled-skills/anti-sleep/SKILL.md +90 -0
  13. package/bundled-skills/api-security-best-practices/SKILL.md +1 -1
  14. package/bundled-skills/app-store-optimization/SKILL.md +7 -0
  15. package/bundled-skills/audio-transcriber/scripts/transcribe.py +17 -4
  16. package/bundled-skills/azure-mgmt-botservice-py/SKILL.md +2 -1
  17. package/bundled-skills/azure-resource-manager-mysql-dotnet/SKILL.md +2 -1
  18. package/bundled-skills/azure-resource-manager-postgresql-dotnet/SKILL.md +2 -1
  19. package/bundled-skills/azure-resource-manager-sql-dotnet/SKILL.md +2 -1
  20. package/bundled-skills/biopython/SKILL.md +2 -1
  21. package/bundled-skills/brain-to-docs/SKILL.md +54 -0
  22. package/bundled-skills/browser-harness/SKILL.md +220 -0
  23. package/bundled-skills/browser-harness/references/install.md +132 -0
  24. package/bundled-skills/bumblebee/scripts/render_report.py +16 -3
  25. package/bundled-skills/bun-development/SKILL.md +1 -1
  26. package/bundled-skills/cc-skill-security-review/SKILL.md +2 -2
  27. package/bundled-skills/claude-monitor/scripts/monitor.py +15 -2
  28. package/bundled-skills/cmux/SKILL.md +250 -0
  29. package/bundled-skills/code-polish/SKILL.md +192 -0
  30. package/bundled-skills/code-review-checklist/SKILL.md +1 -1
  31. package/bundled-skills/codex-subagent/SKILL.md +117 -0
  32. package/bundled-skills/competitor-analysis/scripts/capture_screenshots.mjs +24 -3
  33. package/bundled-skills/competitor-analysis/scripts/compile_report.mjs +13 -2
  34. package/bundled-skills/competitor-analysis/scripts/extract_vs_names.mjs +23 -2
  35. package/bundled-skills/competitor-analysis/scripts/gate_candidates.mjs +21 -0
  36. package/bundled-skills/competitor-analysis/scripts/list_urls.mjs +23 -2
  37. package/bundled-skills/competitor-analysis/scripts/merge_partials.mjs +24 -3
  38. package/bundled-skills/container-security-hardening/SKILL.md +1 -1
  39. package/bundled-skills/content-creator/scripts/brand_voice_analyzer.py +15 -1
  40. package/bundled-skills/content-creator/scripts/seo_optimizer.py +15 -1
  41. package/bundled-skills/context-kit/SKILL.md +135 -0
  42. package/bundled-skills/cyber-audit/SKILL.md +138 -0
  43. package/bundled-skills/deepapi/SKILL.md +649 -0
  44. package/bundled-skills/delegating-to-agents/SKILL.md +64 -0
  45. package/bundled-skills/developer-signup-flow/SKILL.md +1 -1
  46. package/bundled-skills/diary/scripts/fetch_diaries.py +14 -1
  47. package/bundled-skills/diary/scripts/prepare_context.py +15 -2
  48. package/bundled-skills/diary/scripts/sync_to_notion.py +14 -1
  49. package/bundled-skills/dispatch/SKILL.md +19 -7
  50. package/bundled-skills/distribute-skill-to-all-agents/SKILL.md +85 -0
  51. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  52. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  53. package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
  54. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  55. package/bundled-skills/docs/users/bundles.md +1 -1
  56. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  57. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  58. package/bundled-skills/docs/users/getting-started.md +1 -1
  59. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  60. package/bundled-skills/docs/users/usage.md +4 -4
  61. package/bundled-skills/docs/users/visual-guide.md +4 -4
  62. package/bundled-skills/docx-official/ooxml/scripts/pack.py +30 -3
  63. package/bundled-skills/docx-official/ooxml/scripts/unpack.py +16 -3
  64. package/bundled-skills/drizzle-migration-conflict/scripts/check_drizzle_migrations.py +27 -17
  65. package/bundled-skills/ecl-harness-engineer/agents/creator-config.md +17 -3
  66. package/bundled-skills/ecl-harness-engineer/references/environment-detection-guide.md +9 -5
  67. package/bundled-skills/effective-agent-skills/SKILL.md +333 -0
  68. package/bundled-skills/electron-development/SKILL.md +6 -2
  69. package/bundled-skills/environment-setup-guide/SKILL.md +2 -5
  70. package/bundled-skills/expo-ui/scripts/list-components.js +5 -1
  71. package/bundled-skills/fable-safe-prompt/SKILL.md +79 -0
  72. package/bundled-skills/folder-specific-claude-and-agents-md/SKILL.md +96 -0
  73. package/bundled-skills/frontend-slides/scripts/extract-pptx.py +18 -4
  74. package/bundled-skills/gcp-cloud-run/SKILL.md +2 -1
  75. package/bundled-skills/gemini-api-integration/SKILL.md +3 -1
  76. package/bundled-skills/gemini-live-api-dev/SKILL.md +2 -1
  77. package/bundled-skills/gemini-omni-flash-api/scripts/video/generate_video.py +49 -14
  78. package/bundled-skills/goal-loop/SKILL.md +175 -0
  79. package/bundled-skills/hugging-face-evaluation/SKILL.md +1 -1
  80. package/bundled-skills/hugging-face-jobs/SKILL.md +1 -1
  81. package/bundled-skills/hugging-face-jobs/scripts/finepdfs-stats.py +14 -1
  82. package/bundled-skills/hugging-face-model-trainer/scripts/convert_to_gguf.py +10 -1
  83. package/bundled-skills/hugo-to-markdown/scripts/inventory_hugo_rules.py +14 -1
  84. package/bundled-skills/image-generator/SKILL.md +3 -1
  85. package/bundled-skills/instagram/scripts/export.py +18 -7
  86. package/bundled-skills/interview-style-doc-building/SKILL.md +86 -0
  87. package/bundled-skills/junta-leiloeiros/scripts/export.py +19 -10
  88. package/bundled-skills/landing-page-generator/scripts/landing_page_scaffolder.py +15 -1
  89. package/bundled-skills/linear-claude-skill/SKILL.md +4 -2
  90. package/bundled-skills/linkedin-post-writer/SKILL.md +159 -0
  91. package/bundled-skills/linkedin-post-writer/references/hook-formulas.md +482 -0
  92. package/bundled-skills/llm-council/SKILL.md +4 -2
  93. package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package-lock.json +5 -6
  94. package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package.json +2 -3
  95. package/bundled-skills/markdown-rendering/SKILL.md +72 -0
  96. package/bundled-skills/matematico-tao/scripts/complexity_analyzer.py +14 -1
  97. package/bundled-skills/matematico-tao/scripts/dependency_graph.py +15 -2
  98. package/bundled-skills/mobile-design/scripts/mobile_audit.py +20 -6
  99. package/bundled-skills/monte-carlo-validation-notebook/scripts/resolve_dbt_schema.py +15 -2
  100. package/bundled-skills/multi-agent-architect/SKILL.md +1 -1
  101. package/bundled-skills/odoo-rpc-api/SKILL.md +2 -1
  102. package/bundled-skills/pci-compliance/SKILL.md +3 -1
  103. package/bundled-skills/pdf-official/scripts/create_validation_image.py +17 -3
  104. package/bundled-skills/pdf-official/scripts/extract_form_field_info.py +1 -1
  105. package/bundled-skills/personal-tool-builder/SKILL.md +1 -1
  106. package/bundled-skills/pi-custom-model/SKILL.md +68 -0
  107. package/bundled-skills/pi-web-search/SKILL.md +74 -0
  108. package/bundled-skills/playwright-skill/run.js +19 -1
  109. package/bundled-skills/pptx-official/ooxml/scripts/pack.py +30 -3
  110. package/bundled-skills/pptx-official/ooxml/scripts/unpack.py +16 -3
  111. package/bundled-skills/pptx-official/scripts/inventory.py +17 -4
  112. package/bundled-skills/pptx-official/scripts/rearrange.py +15 -2
  113. package/bundled-skills/pptx-official/scripts/replace.py +16 -3
  114. package/bundled-skills/product-manager-toolkit/scripts/customer_interview_analyzer.py +15 -1
  115. package/bundled-skills/production-code-audit/SKILL.md +1 -1
  116. package/bundled-skills/push-skill-to-github/SKILL.md +61 -0
  117. package/bundled-skills/rclone-cli/references/commands/rclone_completion_powershell.md +3 -2
  118. package/bundled-skills/read-all-adrs/SKILL.md +35 -0
  119. package/bundled-skills/remote-gpu-trainer/scripts/verify_local.py +14 -1
  120. package/bundled-skills/research-prompt/SKILL.md +63 -0
  121. package/bundled-skills/run-deep-swe/SKILL.md +120 -0
  122. package/bundled-skills/senior-architect/scripts/architecture_diagram_generator.py +15 -2
  123. package/bundled-skills/senior-architect/scripts/dependency_analyzer.py +15 -2
  124. package/bundled-skills/senior-architect/scripts/project_architect.py +15 -2
  125. package/bundled-skills/senior-frontend/scripts/bundle_analyzer.py +14 -1
  126. package/bundled-skills/senior-frontend/scripts/frontend_scaffolder.py +14 -1
  127. package/bundled-skills/senior-fullstack/scripts/code_quality_analyzer.py +15 -2
  128. package/bundled-skills/senior-fullstack/scripts/fullstack_scaffolder.py +15 -2
  129. package/bundled-skills/senior-fullstack/scripts/project_scaffolder.py +15 -2
  130. package/bundled-skills/setup-help/SKILL.md +50 -0
  131. package/bundled-skills/shodan-reconnaissance/SKILL.md +2 -1
  132. package/bundled-skills/short/SKILL.md +28 -0
  133. package/bundled-skills/skill-creator/scripts/init_skill.py +15 -2
  134. package/bundled-skills/skill-creator/scripts/package_skill.py +29 -12
  135. package/bundled-skills/skill-creator/scripts/quick_validate.py +14 -2
  136. package/bundled-skills/skill-creator-ms/SKILL.md +1 -1
  137. package/bundled-skills/skill-installer/scripts/install_skill.py +42 -8
  138. package/bundled-skills/skill-installer/scripts/package_skill.py +29 -17
  139. package/bundled-skills/skill-installer/scripts/validate_skill.py +37 -22
  140. package/bundled-skills/stripe-integration/SKILL.md +7 -3
  141. package/bundled-skills/swiftui-expert-skill/scripts/instruments_parser/xctrace.py +7 -1
  142. package/bundled-skills/swiftui-expert-skill/scripts/instruments_parser/xml_utils.py +7 -1
  143. package/bundled-skills/taisly-social-media-posting/SKILL.md +79 -0
  144. package/bundled-skills/technical-tutorials/SKILL.md +1 -1
  145. package/bundled-skills/telegram/scripts/setup_project.py +17 -2
  146. package/bundled-skills/time-ledger/SKILL.md +118 -0
  147. package/bundled-skills/trading-ledger/SKILL.md +121 -0
  148. package/bundled-skills/videodb/scripts/ws_listener.py +17 -4
  149. package/bundled-skills/voice-ai-development/SKILL.md +2 -1
  150. package/bundled-skills/vps-server-management/SKILL.md +72 -0
  151. package/bundled-skills/weaviate/scripts/weaviate_conn.py +20 -18
  152. package/bundled-skills/wgm/SKILL.md +137 -0
  153. package/bundled-skills/whatsapp-cloud-api/scripts/setup_project.py +16 -1
  154. package/bundled-skills/workorai/SKILL.md +137 -0
  155. package/bundled-skills/wp-site-health-auditor/SKILL.md +341 -0
  156. package/bundled-skills/wp-site-health-auditor/references/catalog.md +135 -0
  157. package/bundled-skills/writing-skills/render-graphs.js +13 -2
  158. package/bundled-skills/x-twitter-scraper/SKILL.md +3 -1
  159. package/bundled-skills/youtube-notetaker/scripts/serve.py +41 -31
  160. package/bundled-skills/youtube-notetaker/scripts/vtt_to_transcript.py +16 -2
  161. package/bundled-skills/youtube-notetaker/scripts/write_library_item.py +15 -1
  162. package/bundled-skills/youtube-transcript/SKILL.md +120 -0
  163. package/package.json +5 -1
  164. package/skills_index.json +786 -6
@@ -17,6 +17,19 @@ import sys
17
17
  from datetime import datetime, timezone
18
18
  from pathlib import Path
19
19
 
20
+
21
+ def safe_user_path(path_value, base_dir="."):
22
+ """Resolve a CLI path under the current workspace."""
23
+ if base_dir != ".":
24
+ raise ValueError("Custom base directories are not supported for CLI paths")
25
+ base_path = Path.cwd().resolve()
26
+ resolved_path = Path(path_value).expanduser().resolve()
27
+ try:
28
+ resolved_path.relative_to(base_path)
29
+ except ValueError as exc:
30
+ raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
31
+ return resolved_path
32
+
20
33
  sys.path.insert(0, str(Path(__file__).parent))
21
34
 
22
35
  _db = None
@@ -55,11 +68,9 @@ def export_json(records: list, output_dir: Path, name: str) -> Path:
55
68
  output_dir.mkdir(parents=True, exist_ok=True)
56
69
  ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
57
70
  path = output_dir / f"instagram_{name}_{ts}.json"
58
- with open(path, "w", encoding="utf-8") as f:
59
- json.dump(
60
- {"exported_at": datetime.now(timezone.utc).isoformat(), "total": len(records), "data": records},
61
- f, ensure_ascii=False, indent=2,
62
- )
71
+ payload = {"exported_at": datetime.now(timezone.utc).isoformat(), "total": len(records), "data": records}
72
+ with safe_user_path(path).open("w", encoding="utf-8") as f:
73
+ f.write(json.dumps(payload, ensure_ascii=False, indent=2))
63
74
  print(f"[JSON] {len(records)} registros ->{path}")
64
75
  return path
65
76
 
@@ -68,7 +79,7 @@ def export_jsonl(records: list, output_dir: Path, name: str) -> Path:
68
79
  output_dir.mkdir(parents=True, exist_ok=True)
69
80
  ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
70
81
  path = output_dir / f"instagram_{name}_{ts}.jsonl"
71
- with open(path, "w", encoding="utf-8") as f:
82
+ with safe_user_path(path).open("w", encoding="utf-8") as f:
72
83
  for rec in records:
73
84
  f.write(json.dumps(rec, ensure_ascii=False) + "\n")
74
85
  print(f"[JSONL] {len(records)} registros ->{path}")
@@ -82,7 +93,7 @@ def export_csv_file(records: list, output_dir: Path, name: str) -> Path:
82
93
  output_dir.mkdir(parents=True, exist_ok=True)
83
94
  ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
84
95
  path = output_dir / f"instagram_{name}_{ts}.csv"
85
- with open(path, "w", newline="", encoding="utf-8-sig") as f:
96
+ with safe_user_path(path).open("w", newline="", encoding="utf-8-sig") as f:
86
97
  writer = csv.DictWriter(f, fieldnames=list(records[0].keys()), extrasaction="ignore")
87
98
  writer.writeheader()
88
99
  writer.writerows(records)
@@ -0,0 +1,86 @@
1
+ ---
2
+ name: interview-style-doc-building
3
+ description: "Build structured strategy documents by asking one question at a time and patching the file."
4
+ category: productivity
5
+ risk: critical
6
+ source: community
7
+ source_repo: davidondrej/skills
8
+ source_type: community
9
+ date_added: "2026-07-07"
10
+ author: davidondrej
11
+ tags: [documentation, interview, planning]
12
+ tools: [claude, codex]
13
+ license: "MIT"
14
+ license_source: "https://github.com/davidondrej/skills/blob/main/LICENSE"
15
+ ---
16
+
17
+ # Interview-Style Doc Building
18
+
19
+ The user's preferred mode for creating durable strategic docs. AI does NOT propose content — AI asks one question, the user answers, AI patches the file, AI asks the next question. The file IS the conversation's output, updated incrementally.
20
+
21
+ ## When to Use
22
+
23
+ - Building a new SSOT file (life priorities, life vision, principles, frameworks, ranked lists).
24
+ - Filling out a structured doc the user explicitly wants to author themselves.
25
+ - Quarterly/annual reviews where the user's words go into the file.
26
+
27
+ **NOT for:** day planning (use `day-plan`), task triage (`organize-tasks`), or anything where AI proposes content first.
28
+
29
+ ## The Loop
30
+
31
+ 1. **Create the file** with a skeleton (header, sections, "to be filled in" placeholders). Single `write_file` for the new file. After this, NEVER overwrite — only `patch`.
32
+ 2. **Ask ONE question.** Concise. Specific. Single-faceted. Open-ended where possible.
33
+ 3. **Wait for the answer.** Don't ask the next question yet.
34
+ 4. **Patch the file** with the user's answer in the correct section.
35
+ 5. **Re-ask** — next question, or follow-up if the answer was incomplete.
36
+ 6. Repeat until the file is complete.
37
+
38
+ ## Hard Rules
39
+
40
+ - **One question at a time.** Never dump multiple questions in a single message. The user has flagged this.
41
+ - **Patch, don't overwrite.** After the initial skeleton, use `patch` for every update. Never `write_file` to an existing doc.
42
+ - **Update the file BEFORE asking the next question.** Order: receive answer → patch file → ask next question. Not the reverse.
43
+ - **Lists from the user are UNORDERED SETS.** When the user lists items in response to "which X should we cover?" or "what are the Ys?", that is a SET, not a ranking. Never infer rank, priority, or sequence from the order they typed them. If you need ordering, ask explicitly: "Which of these is #1?"
44
+ - **Ask dynamics, not names.** When the user references a person, don't ask "who is X?" — ask about the role/dynamic.
45
+ - **No snark, no attitude, no filler.** Concise questions, concise acknowledgments.
46
+ - **No speculative additions.** Don't invent sections, edge cases, or "anything else?" prompts unless the user asks.
47
+
48
+ ## Question Design
49
+
50
+ - **Domain-discovery, not confirmation.** "What wins against everything else?" — not "Is Business #1?"
51
+ - **Surface new reality.** Each question should pull out info AI doesn't already have.
52
+ - **Engine-move framing where applicable.** "What's the thing that, if true, makes the rest obvious?"
53
+ - **Concrete over abstract.** "What's #2 — the domain that wins against everything except #1?" beats "Tell me about your second priority."
54
+
55
+ ## File Patching Pattern
56
+
57
+ After each answer:
58
+ 1. Read the relevant section (if not already in context).
59
+ 2. `patch` with `old_string` = placeholder or previous entry, `new_string` = updated content with the user's words preserved.
60
+ 3. Confirm the diff. Move on.
61
+
62
+ For ranked lists, append one rank at a time:
63
+ ```
64
+ 1. **Business** — Q2 #1 goal: ...
65
+ 2. **Health** — get below 81.0 kg, sleep 9h/day, ...
66
+ ```
67
+ Each rank gets patched in as the user confirms it.
68
+
69
+ ## Common Pitfalls
70
+
71
+ - **Assuming order from a set.** The user lists "A, B, C, D" → AI writes "1. A, 2. B, 3. C, 4. D" → the user flags it. ALWAYS confirm rank explicitly.
72
+ - **Asking too many questions at once.** Even bundling 2 violates the rule.
73
+ - **Overwriting the file** instead of patching specific sections — destroys prior content.
74
+ - **Adding AI-generated content** to fill out sections. Sections stay empty until the user provides the content.
75
+ - **Skipping the file update** between Q&A pairs — the doc falls out of sync.
76
+
77
+ ## Pairing with Other Skills
78
+
79
+ - `day-plan` — different pattern (task triage), not interview-style.
80
+ - `organize-tasks` — Todoist-specific.
81
+ - `memory-management` — separate from this; persona/preferences go to memory.
82
+
83
+ ## Limitations
84
+
85
+ - Adapted from `davidondrej/skills`; verify local paths, tools, credentials, and agent features before acting.
86
+ - For commands, remote access, scheduling, browser automation, or file-changing workflows, get explicit user approval and confirm the target environment first.
@@ -18,6 +18,19 @@ import json
18
18
  import sys
19
19
  from datetime import datetime, timezone
20
20
  from pathlib import Path
21
+
22
+
23
+ def safe_user_path(path_value, base_dir="."):
24
+ """Resolve a CLI path under the current workspace."""
25
+ if base_dir != ".":
26
+ raise ValueError("Custom base directories are not supported for CLI paths")
27
+ base_path = Path.cwd().resolve()
28
+ resolved_path = Path(path_value).expanduser().resolve()
29
+ try:
30
+ resolved_path.relative_to(base_path)
31
+ except ValueError as exc:
32
+ raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
33
+ return resolved_path
21
34
  from typing import List, Optional
22
35
 
23
36
  sys.path.insert(0, str(Path(__file__).parent))
@@ -31,13 +44,9 @@ def export_json(records: list, output_dir: Path, suffix: str = "") -> Path:
31
44
  output_dir.mkdir(parents=True, exist_ok=True)
32
45
  ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
33
46
  path = output_dir / f"leiloeiros{suffix}_{ts}.json"
34
- with open(path, "w", encoding="utf-8") as f:
35
- json.dump(
36
- {"exported_at": datetime.now(timezone.utc).isoformat(), "total": len(records), "data": records},
37
- f,
38
- ensure_ascii=False,
39
- indent=2,
40
- )
47
+ payload = {"exported_at": datetime.now(timezone.utc).isoformat(), "total": len(records), "data": records}
48
+ with safe_user_path(path).open("w", encoding="utf-8") as f:
49
+ f.write(json.dumps(payload, ensure_ascii=False, indent=2))
41
50
  print(f"[JSON] {len(records)} registros → {path}")
42
51
  return path
43
52
 
@@ -46,7 +55,7 @@ def export_jsonl(records: list, output_dir: Path, suffix: str = "") -> Path:
46
55
  output_dir.mkdir(parents=True, exist_ok=True)
47
56
  ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
48
57
  path = output_dir / f"leiloeiros{suffix}_{ts}.jsonl"
49
- with open(path, "w", encoding="utf-8") as f:
58
+ with safe_user_path(path).open("w", encoding="utf-8") as f:
50
59
  for rec in records:
51
60
  f.write(json.dumps(rec, ensure_ascii=False) + "\n")
52
61
  print(f"[JSONL] {len(records)} registros → {path}")
@@ -62,7 +71,7 @@ def export_csv(records: list, output_dir: Path, suffix: str = "") -> Path:
62
71
  ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
63
72
  path = output_dir / f"leiloeiros{suffix}_{ts}.csv"
64
73
 
65
- with open(path, "w", newline="", encoding="utf-8-sig") as f:
74
+ with safe_user_path(path).open("w", newline="", encoding="utf-8-sig") as f:
66
75
  writer = csv.DictWriter(f, fieldnames=list(records[0].keys()), extrasaction="ignore")
67
76
  writer.writeheader()
68
77
  writer.writerows(records)
@@ -106,7 +115,7 @@ def main():
106
115
  db = Database()
107
116
  db.init()
108
117
 
109
- output_dir = Path(args.output)
118
+ output_dir = safe_user_path(args.output)
110
119
  estados = [e.upper() for e in args.estado] if args.estado else None
111
120
 
112
121
  if estados:
@@ -16,6 +16,20 @@ import sys
16
16
  from typing import Dict, List, Any, Optional
17
17
  from datetime import datetime
18
18
  import html as html_module
19
+ from pathlib import Path
20
+
21
+
22
+ def safe_user_path(path_value, base_dir="."):
23
+ """Resolve a CLI path under the current workspace."""
24
+ if base_dir != ".":
25
+ raise ValueError("Custom base directories are not supported for CLI paths")
26
+ base_path = Path.cwd().resolve()
27
+ resolved_path = Path(path_value).expanduser().resolve()
28
+ try:
29
+ resolved_path.relative_to(base_path)
30
+ except ValueError as exc:
31
+ raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
32
+ return resolved_path
19
33
 
20
34
 
21
35
  def escape(text: str) -> str:
@@ -557,7 +571,7 @@ def main():
557
571
  output = generate_html(config)
558
572
 
559
573
  if args.output:
560
- with open(args.output, "w") as f:
574
+ with safe_user_path(args.output).open("w") as f:
561
575
  f.write(output)
562
576
  print(f"Landing page written to {args.output}")
563
577
  else:
@@ -127,10 +127,12 @@ If setup reports a missing API key:
127
127
 
128
128
  ```bash
129
129
  # Option A: Add to shell profile (~/.zshrc or ~/.bashrc)
130
- export LINEAR_API_KEY="lin_api_your_key_here"
130
+ read -rsp "Linear API key: " LINEAR_API_KEY
131
+ echo
132
+ export LINEAR_API_KEY
131
133
 
132
134
  # Option B: Add to Claude Code environment
133
- echo 'LINEAR_API_KEY=lin_api_your_key_here' >> ~/.claude/.env
135
+ printf 'LINEAR_API_KEY=%s\n' "$LINEAR_API_KEY" >> ~/.claude/.env
134
136
 
135
137
  # Then reload your shell or restart Claude Code
136
138
  ```
@@ -0,0 +1,159 @@
1
+ ---
2
+ name: linkedin-post-writer
3
+ description: "Draft LinkedIn posts from 16 tested hook formulas mapped to engagement goals (comments, reposts, likes, saves), with 2026 algorithm formatting rules and an AI-tell scrub pass before publishing."
4
+ category: marketing
5
+ risk: none
6
+ source: community
7
+ source_repo: sergebulaev/linkedin-skills
8
+ source_type: community
9
+ date_added: "2026-07-06"
10
+ author: sergebulaev
11
+ tags: [linkedin, copywriting, hooks, social-media, personal-brand, content-marketing]
12
+ tools: [claude, codex, cursor, gemini]
13
+ license: "MIT"
14
+ license_source: "https://github.com/sergebulaev/linkedin-skills/blob/main/LICENSE"
15
+ ---
16
+
17
+ # LinkedIn Post Writer
18
+
19
+ ## Overview
20
+
21
+ Drafts long-form LinkedIn posts using 16 hook formulas that were reverse-engineered from posts that outperformed their authors' baselines in 2025-2026, each with a reference engagement number. Instead of asking "what should I write", the workflow asks "what should this post earn" (comments, reposts, likes, or saves), shortlists 2-3 matching formulas, fills the chosen skeleton with the user's voice, then scrubs the draft for AI tells before it ships.
22
+
23
+ This is the flagship skill from [sergebulaev/linkedin-skills](https://github.com/sergebulaev/linkedin-skills), a 10-skill LinkedIn bundle (writer, humanizer, pre-publish audit, comment drafter, reply handler, hook extractor, content planner, profile optimizer, engager analytics, thread monitor) installable as a Claude Code or Codex plugin. This standalone version covers the drafting workflow; scheduling and publishing automation live in the full bundle.
24
+
25
+ ## When to Use This Skill
26
+
27
+ - Use when the user says "write me a LinkedIn post about X"
28
+ - Use when the user has a topic and a rough angle but needs a hook and structure
29
+ - Use when the user wants to pick from proven post formats instead of improvising
30
+ - Use when a draft exists but the hook is weak and needs a formula-based rebuild
31
+ - Not for replying to comments or optimizing profiles; this skill only drafts posts
32
+
33
+ ## How It Works
34
+
35
+ ### Step 1: Gather inputs
36
+
37
+ Collect: topic, angle, target audience (founders, operators, marketers), desired length (short 300-500, medium 900-1,300, or long 1,500-1,900 characters), and any raw material the user already has (numbers, anecdotes, names).
38
+
39
+ ### Step 2: Pick the formula by engagement goal first
40
+
41
+ Ask (or infer) what the post should earn, then shortlist:
42
+
43
+ | Goal | Earned by | Formulas |
44
+ |---|---|---|
45
+ | Comments | questions, contrarian takes, vulnerability | F4 Time-Anchor Confession, F10 Contrarian + Receipts, F12 Permission Slip, F9 Curiosity-Gap |
46
+ | Reposts | quotable maxims, tributes, "X isn't Y" distinctions | F14 Named Gratitude, F2 R.I.P. Obituary, F8 Paid-vs-Free Reversal |
47
+ | Likes | emotional stories, celebrations, status-strip | F11 Emotional Cold-Open, F13 Bait-and-Switch Reversal, F16 Status-Strip Humility |
48
+ | Saves | simplifications, exact how-to, frameworks | F15 Explain-to-Kids, F7 Odd-Precision Money Ledger, F8 Paid-vs-Free Reversal |
49
+
50
+ The full set of 16, with reference engagement:
51
+
52
+ | Code | Formula | Reference | Best for |
53
+ |---|---|---|---|
54
+ | F1 | Platform Risk Anaphora | 4,240 eng | Category and platform-risk arguments |
55
+ | F2 | R.I.P. Obituary | 3,822 eng | Era-ending claims, industry pivots |
56
+ | F3 | Year-over-Year Pivot | 494 eng, 3.74x baseline | Identity shifts, founder reflection |
57
+ | F4 | Time-Anchor Confession | 1,519+ eng | Vulnerability, voice reset |
58
+ | F5 | Self-Proving Meta | 1,082 eng, 435 comments | Commitments and tests in public |
59
+ | F6 | Comment-Gate Lead Magnet | 717-3,008 eng | List building (max once a month) |
60
+ | F7 | Odd-Precision Money Ledger | 1,755 eng, 9.4x baseline | Build logs, cost breakdowns |
61
+ | F8 | Paid-vs-Free Reversal | 550 eng, 19.64x baseline | Framework giveaways |
62
+ | F9 | Curiosity-Gap Teaser | 306 eng, 4.25x baseline | Surprise and behind-the-scenes stories |
63
+ | F10 | Contrarian + Historical Receipts | 3,083 eng | Sacred-cow takes backed by history |
64
+ | F11 | Emotional Cold-Open | high raw reach | Real stories with emotional stakes |
65
+ | F12 | Permission Slip | comment-heavy | Encouragement to a discouraged audience |
66
+ | F13 | Bait-and-Switch Reversal | high raw reach | Bad-news framing that turns into an upgrade |
67
+ | F14 | Named Gratitude / Tribute | repost-heavy | Thanking mentors, teams, departing colleagues |
68
+ | F15 | Explain-to-Kids | save-heavy | Demystifying jargon into a reference post |
69
+ | F16 | Status-Strip Humility | like-heavy | Senior voices trading prestige for warmth |
70
+
71
+ Important caveat: F1-F10 references are engagement counts or format multipliers against the author's own baseline; F11-F16 references are raw corpus reach, often inflated by a famous author or a reshare. The two groups measure different things, so never rank formulas across groups by number.
72
+
73
+ The full skeletons for all 16 formulas are bundled with this skill in [references/hook-formulas.md](references/hook-formulas.md), along with per-formula warnings and the "Never do" list.
74
+
75
+ ### Step 3: Draft the post
76
+
77
+ Read the chosen formula's skeleton from the bundled [references/hook-formulas.md](references/hook-formulas.md) and fill it with the user's voice and material, respecting the 2026 formatting rules:
78
+
79
+ - Hook lands in the first 210 characters (before the "... see more" fold)
80
+ - 900-1,300 characters is the sweet spot for text posts
81
+ - Double line-breaks between ideas, never walls of text
82
+ - 0-2 hashtags, placed at the end
83
+ - No external links in the body (put links in the first comment)
84
+ - Prefer "How I" over "How to": first-person experience hooks outperform generic instruction 2-3x
85
+ - Put one specific number in the first sentence ($873.47 beats $900; 40,000 beats "a lot")
86
+
87
+ ### Step 4: Scrub AI tells
88
+
89
+ Before showing the draft: strip em dashes, AI vocabulary ("game-changer", "deep dive", "delve"), rule-of-three lists without receipts, and generic openers like "In today's fast-paced world". Add human fingerprints: at least one specific number, one named entity, and one first-person concrete detail per 100 words. Vary sentence length aggressively (mix 3-word and 25-word sentences).
90
+
91
+ ### Step 5: Present the result
92
+
93
+ Show the user: formula used, full draft, character count, and a suggested posting window (Tuesday to Thursday, 7:30-9:00 AM local time performs best for B2B audiences).
94
+
95
+ ## Examples
96
+
97
+ ### Example 1: Founder build-log (F7 Odd-Precision Money Ledger)
98
+
99
+ ```text
100
+ User: write a post about what my bootstrapped SaaS actually costs to run
101
+ Agent: Goal check: this reads like a saves play (people screenshot cost
102
+ breakdowns). Best fit is F7 Odd-Precision Money Ledger (9.4x baseline).
103
+ Draft hook:
104
+
105
+ "My SaaS costs $412.83 a month to run. Here is every line item."
106
+
107
+ Then the ledger: hosting $94.20, email $35.00, monitoring $18.63...
108
+ exact numbers only, totals that actually add up, one lesson at the end.
109
+ ```
110
+
111
+ ### Example 2: Goal-first selection
112
+
113
+ ```text
114
+ User: I want a post that gets real comments, topic: why I stopped doing demos
115
+ Agent: Comment-earners are F4 Confession, F10 Contrarian, F12 Permission Slip.
116
+ For "I stopped doing X" the strongest is F10 Contrarian + Receipts:
117
+ open with the unpopular claim, back it with 2 historical parallels,
118
+ close with a question that forces side-picking. Reference: 3,083 eng.
119
+ ```
120
+
121
+ ## Best Practices
122
+
123
+ - ✅ Pick the formula by engagement goal first, topic second
124
+ - ✅ Lead with a real failure or a specific number in the first 3 lines
125
+ - ✅ Include one moment of genuine vulnerability or concrete stakes; pure insight posts underperform in 2026
126
+ - ❌ Don't blend two hook formulas in one post; it dilutes both
127
+ - ❌ Don't use F5 Self-Proving Meta unless the user will actually keep the promise
128
+ - ❌ Don't pair F7 Money Ledger with rounded or invented numbers; readers notice
129
+ - ❌ Don't open with an all-caps line ("THIS CHANGED EVERYTHING")
130
+ - ❌ Don't frame LinkedIn as inferior inside a LinkedIn post
131
+
132
+ ## Limitations
133
+
134
+ - Reference engagement numbers describe the 2025-2026 corpus the formulas were extracted from; they are priors, not guarantees, and LinkedIn's ranking changes over time.
135
+ - The skill drafts text posts; it does not generate images, carousels, or video scripts.
136
+ - This standalone version does not schedule or publish. Scheduling, comment drafting, reply handling, and engagement analytics require the full bundle from the source repo.
137
+ - Voice quality depends on the raw material the user provides; a formula cannot invent authentic anecdotes, and the skill should ask for real details rather than fabricate them.
138
+
139
+ ## Common Pitfalls
140
+
141
+ - **Problem:** The draft sounds like every other AI-written LinkedIn post.
142
+ **Solution:** Run Step 4 ruthlessly. Cut em dashes, cut "game-changer" vocabulary, and force one concrete first-person detail per 100 words.
143
+ - **Problem:** The hook is buried in paragraph two.
144
+ **Solution:** The first 210 characters must carry the hook; everything before the fold decides the expand rate.
145
+ - **Problem:** Comparing F11's raw reach to F8's 19.64x multiplier and picking F11 "because the number is bigger".
146
+ **Solution:** The columns measure different things. Match formula to goal and topic, not to the largest number.
147
+ - **Problem:** Post gets reach but zero comments.
148
+ **Solution:** The formula was picked for the wrong goal. Comment-earners end with a question or a side-picking claim, not a summary.
149
+
150
+ ## Related Skills
151
+
152
+ - `@linkedin-content-generator` - broader LinkedIn content suite (carousels, newsletters, calendars)
153
+ - `@linkedin-profile-optimizer` - profile and authority optimization rather than post drafting
154
+ - `@social-post-writer-seo` - multi-platform social copy when LinkedIn is not the only target
155
+
156
+ ## Additional Resources
157
+
158
+ - [Source repo with all 16 formula skeletons and worked examples](https://github.com/sergebulaev/linkedin-skills)
159
+ - [Full 10-skill bundle install (Claude Code / Codex plugin)](https://github.com/sergebulaev/linkedin-skills#install)