mirror of
https://github.com/recoupable/skills.git
synced 2026-09-14 20:26:58 +08:00
aaf9c06c42
* feat: consolidate skills + plugins into single recoupable/skills repo
Unify the open skills library, rich plugins, and the marketplace registry
into one repo to kill the drift between recoupable/marketplace (live) and
recoupable/skills (stale), and the Claude/Codex registry mismatches.
Structure:
- skills/ generic, install-anywhere library skills
- plugins/{research,catalog,content}/ rich plugins (commands, agents, evals, refs)
- .claude-plugin/marketplace.json + .agents/plugins/marketplace.json
unified registries listing the library (source ".") + 3 plugins
Content reconciliation (single-source the duplicated short-video):
- Promote the newer short-video to the library and bundle its references
(short-video-manual.md, song-sourcing.md) inside skills/short-video/
so the library skill is self-contained.
- Remove the duplicated short-video + content-creation from plugins/content.
- Rewrite recoup-content-create to inline the async pipeline and reference
library skills (short-video, content-creation, create-artist) by name,
decoupling it from in-tree relative paths.
- Refresh plugins/content README + legacy command to drop dead in-tree refs.
Validated e2e: all manifest sources resolve, every SKILL.md has valid
frontmatter, no duplicate skill names (42 distinct), JSON parses clean.
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat: make all 42 skills cross-harness portable (self-contained)
Reject the Claude-only ${CLAUDE_PLUGIN_ROOT} pattern (verified: doesn't
expand in markdown bodies, runtime CWD is the user project) in favor of
the compound-engineering self-containment model so every skill runs on
any harness (Claude Code, Codex, Cursor, bare npx skills), not just as a
Claude marketplace plugin.
- Portable Skill Contract added to AGENTS.md + contributing.md
- scripts/: portability_lint, check_vendored (file + recursive-dir drift),
validate_manifests (+ dual-manifest parity), vendored.json (27 groups)
- research (10): vendor the used shared refs per skill; md-links -> backtick
- catalog (17): vendor plugin-root refs + deal-workspace template +
demo-data-room fixtures + full 17-script set into 8 script-bearing skills;
kill cross-skill dep (royalty-audit<-catalog-analysis); rephrase prose
- library (14) + content (1): fix mir links, de-path cross-skill prose,
replace dead content-strategy.md ref with inline guidance
- ci: .github/workflows/validate.yml runs lint + drift + manifests
Verified: lint 42/42 portable, drift 174 copies/27 groups, manifests
13 files + parity OK. Vendored catalog scripts execute from skill dir.
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor: vendor catalog scripts as per-skill dependency closures
The first portability pass copied the full 17-script catalog toolkit into
all 8 script-bearing skills (136 copies). Most skills invoke far fewer:
dashboard 1, kickoff 1, report 2. Replace the superset with each skill's
true closure — scripts named in SKILL.md plus their transitive deps:
run-deal-checks's 5 subprocess validators, auto-column-map's importlib
normalize target, and the shared _helpers import.
Drops 80 dead copies (174 -> 94 tracked). Verified: lint 42/42, drift
94/27 green, and the dynamic subprocess/importlib chains execute from
each tightened skill dir.
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor(deals): fold 14 deal skills into 6 workflow stages
Collapse the deal skills into start / ingest / analysis / dashboard /
report / demo so the slash menu reads as a natural workflow. Most users
only run the orchestrator, so granular skills became modes, params, and
bundled reference playbooks. Nothing dropped.
- start: absorbs workspace-setup + carries deal-type modes
(buy-side / seller-prep / financing / post-close)
- analysis: absorbs rights-reviewer, royalties-auditor,
financing-underwriter, seller-prep, post-close-admin as references/*.md
- dashboard: absorbs quality-checker pre-share QC gate
- report: absorbs memo-maker (memo assembly + packages)
- vendored.json slug-remapped + resynced; hooks.json Gate B fixed;
plugin README rewritten to the true 6
Gates green: portability_lint (31 portable), check_vendored (85 copies /
25 groups), validate_manifests (13 files + parity). Repo-wide sweep:
zero lingering old deal slugs.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: address cubic review findings on deal validators + docs
Resolves 9 review comments from cubic-dev-ai on PR #33.
P1:
- protect-source-files.sh: also match relative `deals/*/source/*` paths
(Write/Edit tools pass repo-relative paths; the leading-segment glob alone
missed them, leaving immutable source files writable).
- validate-evidence-ledger.py: guard non-dict top-level JSON (array/primitive
no longer crashes with AttributeError; emits a clean error).
- deal-workspace.md: workpapers list now `nps-bridge.json` + `nls-bridge.json`
(two files, matching the dashboard validator) instead of a combined file.
P2:
- validate-deal-workspace.py: catch OSError/UnicodeDecodeError, not just
JSONDecodeError, so unreadable/non-UTF8 files report cleanly.
- validate-normalized-ledger.py: report empty `ledger_line_id` values as a
data-integrity error instead of silently accepting them.
- validate-findings-evidence.py: handle malformed JSON in both loaders;
emit a structured `invalid_findings_file` result instead of a traceback.
- content plugin manifests: author email -> agent@recoupable.com to match
marketplace.json + branding.
- deal-workspace.md: `valuation-scenarios.md` -> `valuation-summary.json`
(matches the workspace + dashboard validators).
P3:
- AGENTS.md: align Skill Format (templates/, fixtures/) with the Portable
Contract; drop the contradictory assets/ reference.
Vendored copies resynced (20). Gates green: portability 31, vendored 85, manifests 13.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: rename root plugin recoupable-skills -> recoup-skills
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: drop -plugin suffix from plugin names (recoup-deals/research/content)
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs: rebrand root library to "Recoup Skills" + add CLAUDE.md symlink
- README.md / AGENTS.md titles: "Recoupable Skills" -> "Recoup Skills"
- displayName fields (marketplace + codex manifest) -> "Recoup Skills"
- README skill-dir tree: assets/ -> templates/ + fixtures/ (matches AGENTS.md contract)
- CLAUDE.md -> AGENTS.md symlink so Claude Code reads the same instructions
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs: add Plugins section to README; drop ' Plugin' from research/content titles
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat: add recoup-song-* skills for Music Flamingo audio analysis
Add a P0 router skill (recoup-song-analyzer) plus five focused P1
workflow skills built on the Music Flamingo /api/songs/analyze endpoint:
- recoup-song-analyzer: entry point — resolves audio URL, picks preset,
routes to focused workflows
- recoup-song-metadata-tagger: structured catalog metadata
- recoup-song-lyrics-transcriber: sectioned lyric sheet (draft)
- recoup-song-playlist-pitch-maker: DSP/editorial pitch + positioning
- recoup-song-sync-brief-maker: supervisor sync brief + clearance risk
- recoup-song-mix-feedback-reviewer: technical mix critique
Canonical endpoint reference lives in recoup-song-analyzer and is
vendored byte-identical into the other five (registered in
scripts/vendored.json). README skill table updated. All three gates
pass (portability, vendor drift, manifests).
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor: promote recoup-song-* skills into recoup-songs plugin
Convert the six loose recoup-song-* skills into a cohesive recoup-songs
plugin, matching the structure of recoup-deals/research/content.
- git mv skills/recoup-song-* -> plugins/songs/skills/ (history preserved)
- promote canonical flamingo-api.md to plugins/songs/references/ and vendor
it byte-identical into all 6 skills (analyzer included) — fixes the prior
smell where recoup-song-analyzer owned the reference the others depended on
- add plugin manifests (.claude-plugin + .cursor-plugin), README, LICENSE
- register recoup-songs in both marketplace files (.claude-plugin + .agents)
- root README: drop the analyzer skills-table row, add Recoup Songs to the
Plugins table
All 3 gates pass: portability (37), vendor drift (91 copies / 26 groups),
manifests (15 files + dual-manifest parity).
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor: shorten recoup-song-* P1 skill names (drop action suffixes)
Drop the action-noun suffix from the five P1 skills so the names read as
the thing they produce, not the actor:
- recoup-song-metadata-tagger -> recoup-song-metadata
- recoup-song-lyrics-transcriber -> recoup-song-lyrics
- recoup-song-playlist-pitch-maker -> recoup-song-playlist-pitch
- recoup-song-sync-brief-maker -> recoup-song-sync-brief
- recoup-song-mix-feedback-reviewer-> recoup-song-mix-feedback
git mv preserves history. Updated SKILL.md name frontmatter, analyzer
decision-tree handoffs, sibling cross-references, plugin README skill
table, and scripts/vendored.json paths. recoup-song-analyzer (router)
unchanged. All 3 gates pass.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: address cubic review comments on PR #33
- validate-findings-evidence.py: a present-but-malformed evidence-ledger.json
is now a hard error (invalid_ledger_file) instead of being silently
downgraded to an empty set, which could report status: ok without
validating any evidence references. Missing ledger stays tolerable.
- validate-normalized-ledger.py: only inspect ledger_line_id values when the
column exists, so empty strings no longer leak into the dedup sets and
produce a confusing "duplicate empty id" error on top of the real
missing-column error.
- Standardize all plugin author emails on agent@recoupable.com (documented
support email) across marketplace + plugin.json manifests for consistency.
- Re-vendored 9 byte-identical script copies; all 3 gates green.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
101 lines
3.7 KiB
Python
101 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Drift check for vendored (duplicated) files.
|
|
|
|
Portable skills are self-contained, so shared references/scripts are COPIED
|
|
into each skill rather than centralized. This check keeps every copy
|
|
byte-identical to its single canonical source, so vendoring can never silently
|
|
diverge.
|
|
|
|
Reads scripts/vendored.json. Two group kinds:
|
|
- file group: {"canonical": "<file>", "copies": ["<file>", ...]}
|
|
- dir group: {"canonical_dir": "<dir>", "copies_dirs": ["<dir>", ...]}
|
|
File groups assert byte-identity; dir groups assert the trees are identical
|
|
(same files, all byte-identical) via filecmp.dircmp.
|
|
|
|
Usage: python3 scripts/check_vendored.py
|
|
Exit 0 = all copies match canonical; 1 = drift or missing files. Stdlib only.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import filecmp
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
MAP_FILE = REPO_ROOT / "scripts" / "vendored.json"
|
|
|
|
|
|
def main() -> int:
|
|
if not MAP_FILE.exists():
|
|
print(f"No {MAP_FILE.relative_to(REPO_ROOT)} yet — nothing vendored to check.")
|
|
return 0
|
|
|
|
try:
|
|
data = json.loads(MAP_FILE.read_text(encoding="utf-8"))
|
|
except json.JSONDecodeError as exc:
|
|
print(f" ✗ scripts/vendored.json: invalid JSON: {exc}")
|
|
return 1
|
|
|
|
groups = data.get("groups", [])
|
|
problems: list[str] = []
|
|
copies_checked = 0
|
|
|
|
def tree_diff(a: Path, b: Path) -> list[str]:
|
|
"""Return human-readable differences between two directory trees."""
|
|
cmp = filecmp.dircmp(a, b)
|
|
diffs: list[str] = []
|
|
if cmp.left_only:
|
|
diffs.append(f"only in canonical: {sorted(cmp.left_only)}")
|
|
if cmp.right_only:
|
|
diffs.append(f"only in copy: {sorted(cmp.right_only)}")
|
|
if cmp.diff_files:
|
|
diffs.append(f"differing files: {sorted(cmp.diff_files)}")
|
|
for sub in cmp.common_dirs:
|
|
diffs.extend(f"{sub}/{d}" for d in tree_diff(a / sub, b / sub))
|
|
return diffs
|
|
|
|
for g in groups:
|
|
if "canonical_dir" in g:
|
|
canonical = REPO_ROOT / g["canonical_dir"]
|
|
if not canonical.is_dir():
|
|
problems.append(f"canonical dir missing: {g['canonical_dir']}")
|
|
continue
|
|
for copy_rel in g.get("copies_dirs", []):
|
|
copies_checked += 1
|
|
copy = REPO_ROOT / copy_rel
|
|
if not copy.is_dir():
|
|
problems.append(f"copy dir missing: {copy_rel} (canonical {g['canonical_dir']})")
|
|
continue
|
|
d = tree_diff(canonical, copy)
|
|
if d:
|
|
problems.append(f"DRIFT (tree): {copy_rel} != {g['canonical_dir']} -> {'; '.join(d)}")
|
|
continue
|
|
|
|
canonical = REPO_ROOT / g["canonical"]
|
|
if not canonical.exists():
|
|
problems.append(f"canonical missing: {g['canonical']}")
|
|
continue
|
|
for copy_rel in g.get("copies", []):
|
|
copies_checked += 1
|
|
copy = REPO_ROOT / copy_rel
|
|
if not copy.exists():
|
|
problems.append(f"copy missing: {copy_rel} (canonical {g['canonical']})")
|
|
continue
|
|
if not filecmp.cmp(canonical, copy, shallow=False):
|
|
problems.append(f"DRIFT: {copy_rel} != {g['canonical']}")
|
|
|
|
if problems:
|
|
print(f"VENDOR DRIFT CHECK FAILED — {len(problems)} problem(s):\n")
|
|
for p in problems:
|
|
print(f" ✗ {p}")
|
|
print("\nRe-sync copies from canonical, or update scripts/vendored.json.")
|
|
return 1
|
|
|
|
print(f"VENDOR DRIFT CHECK PASSED — {copies_checked} copy/copies match canonical across {len(groups)} group(s).")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|