@olegkoval/agent-skills 1.16.0 → 1.18.0

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 (69) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +5 -3
  3. package/.cursor-plugin/index.json +5 -0
  4. package/.grok-plugin/index.json +120 -0
  5. package/.kiro/steering/crash-course.md +259 -0
  6. package/.windsurf/rules/crash-course.md +258 -0
  7. package/README.md +27 -3
  8. package/catalog/skills.json +83 -21
  9. package/package.json +3 -2
  10. package/packages/marketing/search-console-indexing-audit/adapters/grok/plugin.json +6 -0
  11. package/packages/marketing/search-console-indexing-audit/adapters/grok/skills/search-console-indexing-audit/SKILL.md +69 -0
  12. package/packages/marketing/search-console-indexing-audit/adapters/grok/skills/search-console-indexing-audit/scripts/summarize_gsc_coverage.py +151 -0
  13. package/packages/marketing/viral-launch/adapters/grok/plugin.json +6 -0
  14. package/packages/marketing/viral-launch/adapters/grok/skills/viral-launch/SKILL.md +105 -0
  15. package/packages/music/fill-music-player/adapters/grok/plugin.json +6 -0
  16. package/packages/music/fill-music-player/adapters/grok/skills/fill-music-player/SKILL.md +111 -0
  17. package/packages/photography/gallery/adapters/grok/plugin.json +6 -0
  18. package/packages/photography/gallery/adapters/grok/skills/gallery/SKILL.md +237 -0
  19. package/packages/software-development/apple-store-submit/adapters/grok/plugin.json +6 -0
  20. package/packages/software-development/apple-store-submit/adapters/grok/skills/apple-store-submit/SKILL.md +158 -0
  21. package/packages/software-development/cloudflare-block-countries/adapters/grok/plugin.json +6 -0
  22. package/packages/software-development/cloudflare-block-countries/adapters/grok/skills/cloudflare-block-countries/SKILL.md +177 -0
  23. package/packages/software-development/crash-course/SKILL.md +270 -0
  24. package/packages/software-development/crash-course/adapters/claude/plugin.json +5 -0
  25. package/packages/software-development/crash-course/adapters/claude/skills/crash-course/SKILL.md +271 -0
  26. package/packages/software-development/crash-course/adapters/codex/README.md +3 -0
  27. package/packages/software-development/crash-course/adapters/cursor/plugin.json +6 -0
  28. package/packages/software-development/crash-course/adapters/cursor/skills/crash-course/SKILL.md +271 -0
  29. package/packages/software-development/crash-course/adapters/grok/plugin.json +6 -0
  30. package/packages/software-development/crash-course/adapters/grok/skills/crash-course/SKILL.md +271 -0
  31. package/packages/software-development/crash-course/adapters/kiro/steering/crash-course.md +259 -0
  32. package/packages/software-development/crash-course/adapters/windsurf/rules/crash-course.md +258 -0
  33. package/packages/software-development/docs-index-keeper/adapters/grok/plugin.json +6 -0
  34. package/packages/software-development/docs-index-keeper/adapters/grok/skills/docs-index-keeper/SKILL.md +65 -0
  35. package/packages/software-development/gh-cli/adapters/grok/plugin.json +6 -0
  36. package/packages/software-development/gh-cli/adapters/grok/skills/gh-cli/SKILL.md +2200 -0
  37. package/packages/software-development/git-commit/adapters/grok/plugin.json +6 -0
  38. package/packages/software-development/git-commit/adapters/grok/skills/git-commit/SKILL.md +133 -0
  39. package/packages/software-development/macos-menubar-app/adapters/grok/plugin.json +6 -0
  40. package/packages/software-development/macos-menubar-app/adapters/grok/skills/macos-menubar-app/SKILL.md +238 -0
  41. package/packages/software-development/mvp-oneshot/adapters/grok/plugin.json +6 -0
  42. package/packages/software-development/mvp-oneshot/adapters/grok/skills/mvp-oneshot/SKILL.md +176 -0
  43. package/packages/software-development/obsidian-pr-sync/adapters/grok/plugin.json +6 -0
  44. package/packages/software-development/obsidian-pr-sync/adapters/grok/skills/obsidian-pr-sync/SKILL.md +154 -0
  45. package/packages/software-development/obsidian-task-rollover/adapters/grok/plugin.json +6 -0
  46. package/packages/software-development/obsidian-task-rollover/adapters/grok/skills/obsidian-task-rollover/SKILL.md +198 -0
  47. package/packages/software-development/open-source-publisher/adapters/grok/plugin.json +6 -0
  48. package/packages/software-development/open-source-publisher/adapters/grok/skills/open-source-publisher/SKILL.md +471 -0
  49. package/packages/software-development/product-builder/adapters/grok/plugin.json +6 -0
  50. package/packages/software-development/product-builder/adapters/grok/skills/product-builder/SKILL.md +224 -0
  51. package/packages/software-development/promptctl/adapters/grok/plugin.json +6 -0
  52. package/packages/software-development/promptctl/adapters/grok/skills/promptctl/SKILL.md +90 -0
  53. package/packages/software-development/relay/adapters/claude/plugin.json +5 -0
  54. package/packages/software-development/relay/adapters/claude/skills/relay/SKILL.md +117 -0
  55. package/packages/software-development/relay/adapters/grok/plugin.json +6 -0
  56. package/packages/software-development/relay/adapters/grok/skills/relay/SKILL.md +117 -0
  57. package/packages/software-development/review-past-performance/adapters/grok/plugin.json +6 -0
  58. package/packages/software-development/review-past-performance/adapters/grok/skills/review-past-performance/SKILL.md +212 -0
  59. package/packages/software-development/self-critique/adapters/grok/plugin.json +6 -0
  60. package/packages/software-development/self-critique/adapters/grok/skills/self-critique/SKILL.md +149 -0
  61. package/packages/software-development/self-critique/adapters/grok/skills/self-critique/scripts/critique-nudge.mjs +57 -0
  62. package/packages/software-development/semantic-release-beta/adapters/grok/plugin.json +6 -0
  63. package/packages/software-development/semantic-release-beta/adapters/grok/skills/semantic-release-beta/SKILL.md +59 -0
  64. package/packages/software-development/skill-budget-audit/adapters/grok/plugin.json +6 -0
  65. package/packages/software-development/skill-budget-audit/adapters/grok/skills/skill-budget-audit/SKILL.md +146 -0
  66. package/packages/software-development/starter-rules/adapters/grok/plugin.json +6 -0
  67. package/packages/software-development/starter-rules/adapters/grok/skills/starter-rules/SKILL.md +73 -0
  68. package/scripts/build-adapters.sh +47 -4
  69. package/scripts/validate-catalog.sh +10 -0
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env python3
2
+ """Summarize Google Search Console Coverage CSV exports."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import csv
8
+ import json
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+
13
+ STANDARD_FILES = {
14
+ "chart": "Chart.csv",
15
+ "metadata": "Metadata.csv",
16
+ "critical": "Critical issues.csv",
17
+ "non_critical": "Non-critical issues.csv",
18
+ }
19
+
20
+
21
+ def read_rows(path: Path) -> list[dict[str, str]]:
22
+ if not path.exists():
23
+ return []
24
+ with path.open(newline="", encoding="utf-8-sig") as handle:
25
+ return [
26
+ {key: (value or "").strip() for key, value in row.items()}
27
+ for row in csv.DictReader(handle)
28
+ ]
29
+
30
+
31
+ def int_or_none(value: str) -> int | None:
32
+ if value == "":
33
+ return None
34
+ try:
35
+ return int(value.replace(",", ""))
36
+ except ValueError:
37
+ return None
38
+
39
+
40
+ def chart_summary(rows: list[dict[str, str]]) -> dict[str, Any]:
41
+ points = []
42
+ for row in rows:
43
+ points.append(
44
+ {
45
+ "date": row.get("Date", ""),
46
+ "not_indexed": int_or_none(row.get("Not indexed", "")),
47
+ "indexed": int_or_none(row.get("Indexed", "")),
48
+ "impressions": int_or_none(row.get("Impressions", "")),
49
+ }
50
+ )
51
+
52
+ indexed_points = [
53
+ point for point in points if point["indexed"] is not None or point["not_indexed"] is not None
54
+ ]
55
+ first = indexed_points[0] if indexed_points else None
56
+ last = indexed_points[-1] if indexed_points else None
57
+
58
+ return {
59
+ "date_range": [points[0]["date"], points[-1]["date"]] if points else None,
60
+ "first_indexing_point": first,
61
+ "latest_indexing_point": last,
62
+ "indexed_delta": None if not first or not last else (last["indexed"] or 0) - (first["indexed"] or 0),
63
+ "not_indexed_delta": None
64
+ if not first or not last
65
+ else (last["not_indexed"] or 0) - (first["not_indexed"] or 0),
66
+ "impressions_total": sum(point["impressions"] or 0 for point in points),
67
+ }
68
+
69
+
70
+ def issue_summary(rows: list[dict[str, str]]) -> list[dict[str, Any]]:
71
+ issues = []
72
+ for row in rows:
73
+ reason = row.get("Reason", "")
74
+ if not reason:
75
+ continue
76
+ issues.append(
77
+ {
78
+ "reason": reason,
79
+ "source": row.get("Source", ""),
80
+ "validation": row.get("Validation", ""),
81
+ "pages": int_or_none(row.get("Pages", "")) or 0,
82
+ }
83
+ )
84
+ return sorted(issues, key=lambda issue: issue["pages"], reverse=True)
85
+
86
+
87
+ def load_export(export_dir: Path) -> dict[str, Any]:
88
+ files = {key: export_dir / filename for key, filename in STANDARD_FILES.items()}
89
+ metadata_rows = read_rows(files["metadata"])
90
+ return {
91
+ "export_dir": str(export_dir),
92
+ "metadata": {row.get("Property", ""): row.get("Value", "") for row in metadata_rows},
93
+ "chart": chart_summary(read_rows(files["chart"])),
94
+ "critical_issues": issue_summary(read_rows(files["critical"])),
95
+ "non_critical_issues": issue_summary(read_rows(files["non_critical"])),
96
+ "missing_files": [filename for filename in STANDARD_FILES.values() if not (export_dir / filename).exists()],
97
+ }
98
+
99
+
100
+ def print_markdown(summary: dict[str, Any]) -> None:
101
+ chart = summary["chart"]
102
+ latest = chart["latest_indexing_point"] or {}
103
+ print("# Search Console Coverage Summary")
104
+ print()
105
+ print(f"- Export: `{summary['export_dir']}`")
106
+ if chart["date_range"]:
107
+ print(f"- Date range: {chart['date_range'][0]} to {chart['date_range'][1]}")
108
+ print(f"- Latest indexed: {latest.get('indexed', 'n/a')}")
109
+ print(f"- Latest not indexed: {latest.get('not_indexed', 'n/a')}")
110
+ print(f"- Total impressions in chart: {chart['impressions_total']}")
111
+ if summary["metadata"]:
112
+ print(f"- Metadata: {summary['metadata']}")
113
+ if summary["missing_files"]:
114
+ print(f"- Missing standard files: {', '.join(summary['missing_files'])}")
115
+
116
+ for label, issues in (
117
+ ("Critical Issues", summary["critical_issues"]),
118
+ ("Non-Critical Issues", summary["non_critical_issues"]),
119
+ ):
120
+ print()
121
+ print(f"## {label}")
122
+ if not issues:
123
+ print("- None reported")
124
+ continue
125
+ for issue in issues:
126
+ print(
127
+ f"- {issue['reason']}: {issue['pages']} pages"
128
+ f" ({issue['source']}, validation: {issue['validation']})"
129
+ )
130
+
131
+ print()
132
+ print("## Notes")
133
+ print("- Standard Coverage exports are aggregate reports; they may not include affected URL examples.")
134
+ print("- Redirect and canonical buckets should be checked against sitemap URLs and live canonical tags.")
135
+
136
+
137
+ def main() -> None:
138
+ parser = argparse.ArgumentParser(description=__doc__)
139
+ parser.add_argument("export_dir", type=Path)
140
+ parser.add_argument("--json", action="store_true", help="Print machine-readable JSON")
141
+ args = parser.parse_args()
142
+
143
+ summary = load_export(args.export_dir)
144
+ if args.json:
145
+ print(json.dumps(summary, indent=2, sort_keys=True))
146
+ else:
147
+ print_markdown(summary)
148
+
149
+
150
+ if __name__ == "__main__":
151
+ main()
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "olko:viral-launch",
3
+ "version": "0.1.0",
4
+ "description": "Set up a project repository and launch plan for shareable marketing, public launch readiness, and growth loops.",
5
+ "skills": "skills/"
6
+ }
@@ -0,0 +1,105 @@
1
+ ---
2
+
3
+ name: viral-launch
4
+ description: Set up a project repository and launch plan for shareable marketing, public launch readiness, and growth loops without pretending virality is guaranteed.
5
+ license: MIT
6
+ allowed-tools: Bash, Read, Write, WebSearch, WebFetch
7
+ compatibility: Codex, Claude Code, Cursor, GitHub Copilot, Windsurf, Kiro, and other Agent Skills compatible tools. Useful for product repos, open-source packages, SaaS launches, waitlists, and creator tools.
8
+ metadata:
9
+ targets: [_source-only]
10
+ author: Oleg Koval
11
+ tags:
12
+ - marketing
13
+ - launch
14
+ - growth
15
+ - virality
16
+ - positioning
17
+ - github
18
+
19
+ ---
20
+ <!-- Generated by scripts/build-adapters.sh. Do not edit directly. -->
21
+
22
+ # viral-launch
23
+
24
+ Use this skill when a user wants to launch a project, make a repo marketable, improve discoverability, or build shareable launch assets.
25
+
26
+ This skill is opinionated: virality is not a promise. The goal is to make the project easy to understand, easy to share, and easy to act on.
27
+
28
+ ## Trigger phrases
29
+
30
+ - make this repo launch-ready
31
+ - set this up to go viral
32
+ - prepare a product launch
33
+ - create marketing assets for this project
34
+ - improve the README for launch
35
+ - prepare Product Hunt / Hacker News / X / LinkedIn launch copy
36
+ - set up an open-source repo for growth
37
+
38
+ ## Workflow
39
+
40
+ 1. Inspect the project first:
41
+ - product purpose
42
+ - target user
43
+ - core use case
44
+ - current README and docs
45
+ - install or demo path
46
+ - screenshots, videos, or visual proof
47
+ - analytics or waitlist capture if present
48
+ 2. Define the launch angle in one sentence:
49
+ - who it is for
50
+ - what painful job it solves
51
+ - why now
52
+ - what makes it different
53
+ 3. Make the repo launch-ready:
54
+ - clear README headline and first paragraph
55
+ - demo or quickstart within the first screen
56
+ - installation steps that work from a clean checkout
57
+ - screenshots, GIF, or demo link where possible
58
+ - feature list based on outcomes, not implementation trivia
59
+ - examples for the highest-intent use cases
60
+ - badges only when they add trust
61
+ - license, contributing notes, and issue templates when useful
62
+ 4. Create launch assets:
63
+ - short tagline
64
+ - 1-paragraph announcement
65
+ - 5 social post variants
66
+ - Product Hunt tagline and description when relevant
67
+ - Hacker News title candidates when relevant
68
+ - launch email or DM when relevant
69
+ - creator/influencer outreach list criteria, not spam copy
70
+ 5. Design share loops:
71
+ - a reason users would show the output to someone else
72
+ - a public artifact worth linking
73
+ - a before/after demo
74
+ - a template, benchmark, checklist, or gallery that can travel
75
+ - referral or waitlist loop only if it fits the product
76
+ 6. Define proof and metrics:
77
+ - activation event
78
+ - share event
79
+ - conversion event
80
+ - retention proxy
81
+ - launch-day dashboard or simple tracking checklist
82
+ 7. Keep claims grounded:
83
+ - do not invent traction, logos, benchmarks, testimonials, or user counts
84
+ - mark assumptions explicitly
85
+ - prefer specific proof over hype
86
+ - avoid dark patterns and spam
87
+
88
+ ## Output format
89
+
90
+ When planning, produce:
91
+
92
+ - Launch angle
93
+ - Repo changes
94
+ - Launch assets
95
+ - Share loops
96
+ - Metrics
97
+ - Risks and assumptions
98
+ - Next actions
99
+
100
+ When editing a repository, make the smallest useful set of changes first, then validate by reading the README from a new user's perspective.
101
+
102
+ ## References
103
+
104
+ - `references/launch-checklist.md`
105
+ - `references/external-skills.md`
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "olko:fill-music-player",
3
+ "version": "0.1.0",
4
+ "description": "Fill a portable music player with a curated random selection from a source music library while balancing formats, artists, albums, and capacity.",
5
+ "skills": "skills/"
6
+ }
@@ -0,0 +1,111 @@
1
+ ---
2
+ name: fill-music-player
3
+ description: Use when a user wants to fill a portable music player (Walkman, iPod, USB drive) with a curated random selection from a music library on a NAS or local directory. Handles format filtering, artist/album diversity balancing, and size targeting.
4
+ license: MIT
5
+ allowed-tools: Bash
6
+ compatibility: Codex, Claude Code, Cursor, GitHub Copilot, Windsurf, Kiro, and other Agent Skills compatible tools. Requires shell access and the fill-music-player CLI or underlying script in the target environment.
7
+ metadata:
8
+ targets: [_source-only]
9
+ author: Oleg Koval
10
+ tags:
11
+ - music
12
+ - media
13
+ - curation
14
+ - copy
15
+ - cli
16
+ ---
17
+ <!-- Generated by scripts/build-adapters.sh. Do not edit directly. -->
18
+
19
+ # fill-music-player
20
+
21
+ Curates a DJ-balanced selection of tracks from a source music library and copies them to a portable player, respecting device capacity and supported formats.
22
+
23
+ ## Quick Reference
24
+
25
+ ```bash
26
+ # Dry run first (always recommend this)
27
+ fill-music-player --source /Volumes/music --dest /Volumes/WALKMAN/MUSIC --dry-run
28
+
29
+ # Copy for real
30
+ fill-music-player --source /Volumes/music --dest /Volumes/WALKMAN/MUSIC
31
+
32
+ # Or run the script directly with uv
33
+ uv run fill_music_player.py --source ~/Music --dest /Volumes/USB --dry-run
34
+ ```
35
+
36
+ Run `fill-music-player --help` for all options.
37
+
38
+ ## Key Parameters
39
+
40
+ | Flag | Default | Purpose |
41
+ |------|---------|---------|
42
+ | `--source` / `-s` | required | Source music root (NAS mount, ~/Music, etc.) |
43
+ | `--dest` / `-d` | required | Player destination directory |
44
+ | `--target-gb` | 95% of free space | How many GB to fill |
45
+ | `--formats` | mp3,m4a,wma,aac | Allowed extensions |
46
+ | `--max-per-artist` | 4 | Artist diversity cap |
47
+ | `--max-per-album` | 3 | Album diversity cap |
48
+ | `--skip-dirs` | (none) | Top-level dirs to skip |
49
+ | `--dry-run` | false | Preview without copying |
50
+
51
+ ## Workflow
52
+
53
+ 1. Always do `--dry-run` first
54
+ 2. Review the per-genre breakdown
55
+ 3. Adjust `--skip-dirs`, `--max-file-mb`, or `--max-per-artist` if needed
56
+ 4. Run without `--dry-run` to copy
57
+
58
+ ## Device Formats
59
+
60
+ | Device | Recommended `--formats` |
61
+ |--------|------------------------|
62
+ | Sony Walkman NWZ series | `mp3,m4a,wma` |
63
+ | iPod (classic/nano) | `mp3,m4a,aac` |
64
+ | Generic USB player | `mp3` |
65
+ | Any modern player | `mp3,m4a,wma,aac,ogg,flac` |
66
+
67
+ ## Edge Cases and Troubleshooting
68
+
69
+ | Situation | What happens | Fix |
70
+ |-----------|-------------|-----|
71
+ | NAS disconnects mid-copy | Copy fails for that file, logged as error, continues with next track | Re-run; already-copied files are skipped (`if not dst.exists()`) |
72
+ | Source has only FLAC/lossless | 0 files selected (FLAC not in default formats) | Use `--formats flac` or `--formats mp3,flac` |
73
+ | FAT32 filename too long or illegal chars | Filenames are sanitized: `<>:"/\|?*` replaced with `_`, truncated to 80 chars | Automatic — no action needed |
74
+ | Mixed/broken ID3 tags | Falls back to folder name parsing (`Genre/Artist - Album/track.mp3`) | Works transparently; folder structure is the reliable fallback |
75
+ | Very large files (DJ mixes, radio shows) | Skipped by default (>20 MB) | Adjust with `--max-file-mb 50` to include mixes |
76
+ | Tiny files (jingles, corrupt) | Skipped by default (<300 KB) | Adjust with `--min-file-kb 100` to be more permissive |
77
+ | One artist dominates the library | Capped at 4 tracks per artist, 3 per album | Lower with `--max-per-artist 2` for more variety |
78
+ | Empty genre directories | Silently skipped, no error | Normal behavior |
79
+ | Destination already has files | Existing files are not overwritten or deleted; new tracks added alongside | Clear destination first if you want a fresh set |
80
+ | SMB/NFS mount is slow | Scanning with mutagen tag reading can take 5-10 min for large libraries | Expected; the dry-run output appears once scanning completes |
81
+
82
+ ## Real-World Example
83
+
84
+ Filling a **Sony Walkman NWZ-B183F (4 GB)** from a **Synology DS918+ NAS** with ~9,000 tracks across blues, funk, hip-hop, soul, IDM, dubstep, dub, indie, and post-rock:
85
+
86
+ ```bash
87
+ # Step 1: Preview
88
+ fill-music-player \
89
+ --source /Volumes/music \
90
+ --dest /Volumes/WALKMAN/MUSIC \
91
+ --skip-dirs "jingles,!recordings,!restored,dubrecords" \
92
+ --formats mp3,m4a,wma \
93
+ --dry-run
94
+
95
+ # Output:
96
+ # Found 8914 candidate tracks across 14 genres
97
+ # Funk → 86 tracks (440.8 MB)
98
+ # blues → 38 tracks (316.7 MB)
99
+ # hiphop → 33 tracks (234.7 MB)
100
+ # ...
101
+ # Selected 348 tracks (2473.0 MB)
102
+
103
+ # Step 2: Copy
104
+ fill-music-player \
105
+ --source /Volumes/music \
106
+ --dest /Volumes/WALKMAN/MUSIC \
107
+ --skip-dirs "jingles,!recordings,!restored,dubrecords" \
108
+ --formats mp3,m4a,wma
109
+ ```
110
+
111
+ Result: 348 tracks, 2.4 GB, balanced across 14 genres with max 4 tracks per artist. Artists included Fela Kuti, ZZ Top, Mobb Deep, Gregory Porter, Tinariwen, Chick Corea, Hudson Mohawke, and Funkadelic. Total copy time over SMB: ~12 minutes.
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "olko:gallery",
3
+ "version": "0.1.0",
4
+ "description": "Create photo galleries with AI-assisted layout curation and sequencing.",
5
+ "skills": "skills/"
6
+ }
@@ -0,0 +1,237 @@
1
+ ---
2
+ name: gallery
3
+ description: Create photo galleries with AI-assisted layout curation. Orchestrates atomic scripts and sub-agents. Use when user wants to create a gallery from photos, mentions "/gallery", or asks about photo layout and sequencing.
4
+ license: MIT
5
+ allowed-tools: Bash, Read, Write, WebSearch, WebFetch
6
+ compatibility: Codex, Claude Code, Cursor, GitHub Copilot, Windsurf, Kiro, and other Agent Skills compatible tools. Requires a project that contains the gallery scripts and expected data layout.
7
+ metadata:
8
+ targets: [_source-only]
9
+ author: Oleg Koval
10
+ tags:
11
+ - photos
12
+ - gallery
13
+ - layout
14
+ - curation
15
+ ---
16
+ <!-- Generated by scripts/build-adapters.sh. Do not edit directly. -->
17
+
18
+ # Gallery Workflow
19
+
20
+ Composed skill that creates photo galleries by orchestrating atomic scripts and sub-agents.
21
+
22
+ ## Philosophy
23
+
24
+ This skill applies principles from masters of photo sequencing and curation:
25
+
26
+ - **Keith Smith** — Group/Series/Sequence taxonomy; the gallery as a unified experience
27
+ - **Jörg Colberg** — Ruthless editing; visual rhythm; pacing
28
+ - **John Szarkowski** — The Five Characteristics for image assessment
29
+
30
+ > "The individual pages have to give up their independence in order to form a union." — Keith Smith
31
+
32
+ For detailed principles, see `docs/photo-layout-principles.md`.
33
+
34
+ ## Usage
35
+
36
+ `/gallery {folder} "{title}" {slug}`
37
+
38
+ Example: `/gallery cabo-2025 "CABO 2025" cabo`
39
+
40
+ **Important:** All paths are relative to the current project directory. The `{folder}` argument is a path within the project (e.g., `./photos/cabo-2025` or just `cabo-2025`). Do NOT search the filesystem for folders — assume the user's folder exists in the current working directory.
41
+
42
+ ## Gallery Modes
43
+
44
+ Before starting, identify the gallery's organizational mode:
45
+
46
+ | Mode | Definition | Implication |
47
+ |------|------------|-------------|
48
+ | **Series** | Chronological journey (default) | Respect timestamp order; adjust only within temporal clusters |
49
+ | **Group** | Thematic collection | Order is flexible; optimize for visual rhythm |
50
+ | **Sequence** | Strict order with meaning | Order is sacred; only adjust layout, not arrangement |
51
+
52
+ Most travel/event galleries are **Series**.
53
+
54
+ ## Architecture
55
+
56
+ ```
57
+ Atomic Scripts (reusable):
58
+ ├── init-gallery-manifest.ts Create manifest from EXIF
59
+ ├── merge-gallery-batches.ts Combine batch files
60
+ ├── validate-gallery-manifest.ts Fix common errors
61
+ └── generate-gallery-files.ts Generate .astro pages
62
+
63
+ Sub-Agent Skill:
64
+ └── review-image-batch AI reviews images → batch-NN.json
65
+ ```
66
+
67
+ ## Workflow
68
+
69
+ ### Phase 1: Initialize
70
+
71
+ ```bash
72
+ bun scripts/init-gallery-manifest.ts {folder} "{title}" {slug}
73
+ ```
74
+
75
+ Creates:
76
+ - `src/data/gallery-manifests/{folder}.json` (images array, empty blocks)
77
+ - `/tmp/gallery-batches/{folder}/` (clean directory for batch files)
78
+
79
+ ### Phase 2: Batch Review
80
+
81
+ Spawn sub-agents to review images. **Each writes to its own file** (parallel-safe).
82
+
83
+ ```
84
+ For batchIndex in 0..ceil(imageCount/8):
85
+ Task(
86
+ subagent_type: "general-purpose",
87
+ description: "Review gallery batch {batchIndex}",
88
+ prompt: <see below>
89
+ )
90
+ ```
91
+
92
+ **Sub-agent prompt template:**
93
+
94
+ ```
95
+ Review batch {batchIndex} for {folder} gallery.
96
+
97
+ Read:
98
+ - Manifest: src/data/gallery-manifests/{folder}.json
99
+ - Layout guide: {plugin_base_dir}/skills/review-image-batch/references/layout-guide.md
100
+ (The plugin base directory is provided in the skill invocation header)
101
+
102
+ Images to review: indices {startImage} to {endImage}
103
+ (Calculate: startImage = batchIndex * 8, endImage = min(startImage + 7, totalImages - 1))
104
+
105
+ Workflow:
106
+ 1. Read manifest to get image filenames
107
+ 2. Read layout guide for rules
108
+ 3. View each image file with Read tool
109
+ 4. Create layout blocks using your visual judgment
110
+ 5. Write JSON to: /tmp/gallery-batches/{folder}/batch-{NN}.json
111
+ where NN = batchIndex padded to 2 digits (00, 01, 02...)
112
+ 6. Return TEXT SUMMARY ONLY
113
+
114
+ Output file format:
115
+ {
116
+ "batchIndex": {batchIndex},
117
+ "startImage": {startImage},
118
+ "endImage": {endImage},
119
+ "blocks": [
120
+ { "layout": "WideImage", "images": ["DSCF1234.jpeg"], "props": {}, "notes": "..." }
121
+ ]
122
+ }
123
+
124
+ LAYOUT PHILOSOPHY:
125
+ Apply visual judgment, not mechanical rules. A gallery needs rhythm and variety.
126
+
127
+ Single-image (WideImage, OffsetImage, InsetImage, FullBleed):
128
+ - Strong standalones, portraits, quiet moments
129
+
130
+ Multi-image (TwoUp, ThreeUp):
131
+ - Use when juxtaposition creates meaning beyond either image alone
132
+ - Apply Lyons's test: Temporal? Spatial? Formal echo? Emotional pairing? Transformation?
133
+ - A good TwoUp is BETTER than two mediocre WideImages
134
+ - A true sequence (entering→through→emerging) deserves ThreeUp
135
+
136
+ Spacer: Add breathing room after powerful images
137
+ Chapter: Insert at location/theme changes
138
+ ```
139
+
140
+ **Parallel execution:** Sub-agents can run in parallel since each writes to its own file.
141
+
142
+ ### Phase 3: Merge & Validate
143
+
144
+ After all batches complete:
145
+
146
+ ```bash
147
+ bun scripts/merge-gallery-batches.ts {folder}
148
+ bun scripts/validate-gallery-manifest.ts src/data/gallery-manifests/{folder}.json
149
+ ```
150
+
151
+ This combines all batch files and validates the result.
152
+
153
+ ### Phase 4: Global Arc Review (NEW)
154
+
155
+ Before generating, review the merged manifest for overall coherence:
156
+
157
+ 1. **Count hero moments** — If >4 FullBleeds, demote some to WideImage
158
+ 2. **Check multi-image limits** — Max 2-3 TwoUp, 1-2 ThreeUp across entire gallery
159
+ 3. **Verify rhythm** — No consecutive high-weight blocks
160
+ 4. **Identify the climax** — One clear emotional peak, not many
161
+ 5. **Working backwards** — Does the final image provide satisfying closure?
162
+
163
+ ### Phase 5: Generate
164
+
165
+ ```bash
166
+ bun scripts/generate-gallery-files.ts {folder}
167
+ ```
168
+
169
+ Creates:
170
+ - `src/data/{folder}.ts` (data file with chapters)
171
+ - `src/pages/photographing/{slug}/index.astro`
172
+ - Additional `.astro` files for each chapter (if multi-chapter)
173
+
174
+ ## Resume Protocol
175
+
176
+ If interrupted, check `/tmp/gallery-batches/{folder}/` for existing batch files:
177
+
178
+ ```bash
179
+ ls /tmp/gallery-batches/{folder}/
180
+ ```
181
+
182
+ Only run missing batches, then merge.
183
+
184
+ ## Layout Philosophy
185
+
186
+ A gallery needs **rhythm and variety** — loud moments, quiet moments, pairings that create meaning.
187
+
188
+ | Layout | Purpose |
189
+ |--------|---------|
190
+ | WideImage | Strong standalone landscapes |
191
+ | OffsetImage | Portraits, quiet moments, breathing room |
192
+ | FullBleed | Hero shots, climactic moments |
193
+ | InsetImage | Intimate details, visual whispers |
194
+ | Spacer | Pacing, section breaks |
195
+ | TwoUp | Images that speak to each other |
196
+ | ThreeUp | True sequences (entering→through→emerging) |
197
+ | SplitLayout | Asymmetric pairs, one frames the other |
198
+ | Chapter | Location/theme breaks |
199
+
200
+ ### Lyons's Juxtaposition Test
201
+
202
+ Pair images when juxtaposition creates meaning beyond either image alone:
203
+
204
+ 1. **Temporal** — Same moment, different angles
205
+ 2. **Spatial** — Same scene, complementary views
206
+ 3. **Formal echo** — Compositional rhyme (lines, shapes, colors)
207
+ 4. **Emotional** — Contrast or reinforcement
208
+ 5. **Transformation** — Together they say something neither says alone
209
+
210
+ A strong TwoUp is better than two mediocre WideImages.
211
+
212
+ ## Output
213
+
214
+ When complete:
215
+ - Gallery URL: `/photographing/{slug}/`
216
+ - Files:
217
+ - `src/data/gallery-manifests/{folder}.json`
218
+ - `src/data/{folder}.ts`
219
+ - `src/pages/photographing/{slug}/*.astro`
220
+
221
+ ## Composability
222
+
223
+ Each piece is reusable:
224
+
225
+ ```bash
226
+ # Just extract EXIF
227
+ bun scripts/init-gallery-manifest.ts photos "Photos" photos
228
+
229
+ # Just merge existing batches
230
+ bun scripts/merge-gallery-batches.ts photos
231
+
232
+ # Just regenerate pages from manifest
233
+ bun scripts/generate-gallery-files.ts photos
234
+
235
+ # Validate any manifest
236
+ bun scripts/validate-gallery-manifest.ts src/data/gallery-manifests/photos.json
237
+ ```
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "olko:apple-store-submit",
3
+ "version": "0.1.0",
4
+ "description": "Handle Apple App Store rejection emails end-to-end: parse rejection reasons, plan fixes, implement code changes, navigate App Store Connect, and resubmit.",
5
+ "skills": "skills/"
6
+ }