feat: unify skills + plugins into a multi-agent marketplace#23
feat: unify skills + plugins into a multi-agent marketplace#23sidneyswift wants to merge 12 commits into
Conversation
Made-with: Cursor
Adds the full music catalog deal review plugin foundation with diligence workflows, commands, specialist agents, validation scripts, templates, evals, and test-backed royalty normalization fixtures. Verification: - python3 scripts/test-normalize-royalty-statement.py - python3 scripts/test-golden-fixtures.py - JSON validation for plugin manifests, templates, and eval fixtures - script --help checks for every Python script - validate-normalized-ledger.py for all expected fixture ledgers - Neon River synthetic data room smoke test: 3,188 normalized rows Made-with: Cursor
Improve deal-readiness reliability with full provider fixture coverage, stronger workspace validation, readiness checks, dashboard output, structured QC, Cursor support, and expanded diligence evals. Made-with: Cursor
Replaces the SPDX shorthand license file with the full Apache 2.0 text for a clearer public plugin submission package. Verification: - claude plugin validate . - python3 scripts/test-normalize-royalty-statement.py - python3 scripts/test-golden-fixtures.py - python3 scripts/test-validate-deal-workspace.py - python3 scripts/test-diligence-readiness.py Made-with: Cursor
…guardrails
Add new ingest automation scripts (manual-review queue, dataroom hygiene
scan, PDF statement extractor, findings/evidence validator, workspace
consistency validator) plus a shared `_helpers.py` module and
unit tests for each.
Wire the new validators into `run-diligence-checks.py` so the readiness
check covers findings-to-evidence traceability and cross-artifact
consistency. Extend `normalize-royalty-statement.py`, `build-file-manifest.py`,
`calculate-concentration.py`, and `build-diligence-dashboard.py` with
provider classification, parse-status hints, threshold-driven concentration
findings, and richer dashboards. Add a BMI real-headers golden fixture
and update the golden test runner.
Introduce hooks: a `PreToolUse` script that denies writes into
`deals/{deal-id}/source/` (immutable evidence) and a prompt-based `Stop`
hook that gates completion claims against the deal-workspace checklist
in references/deal-workspace.md.
Document hooks in the README, point the catalog-ingest skill and command
at the new scripts, refresh red-flags references and the QC reviewer
agent, and add `requirements.txt` for the optional pdfplumber/openpyxl
extractors. Add `.gitignore` to keep Python bytecode out of the repo.
Co-authored-by: Cursor <cursoragent@cursor.com>
…place
Convert recoupable/skills into the single discoverable home for all Recoupable
agent skills and plugins. The repo now ships:
- A platform marketplace for Claude Code, Codex, and Cursor (three generated
marketplace.json files at the repo root) listing every installable plugin.
- A virtual `recoupable-skills` plugin over the existing 12 broad music skills
under `skills/` (Anthropic `anthropics/skills` pattern).
- A self-contained `music-catalog-diligence` plugin under
`plugins/music-catalog-diligence/` merged in via git subtree with full
history preserved (Anthropic `claude-plugins-official` pattern).
- A single `marketplace.source.json` source-of-truth plus
`scripts/generate-marketplaces.py` so the three platform files never drift.
- `scripts/validate-manifests.py` and a GitHub Actions workflow that runs it
on every PR — validates marketplace parity, plugin paths, plugin manifests,
and every SKILL.md frontmatter.
After this lands, users add one marketplace and choose which plugin to install:
/plugin marketplace add recoupable/skills
/plugin install recoupable-skills@recoupable
/plugin install music-catalog-diligence@recoupable
Legacy root `.claude-plugin/plugin.json` and `.codex-plugin/plugin.json` are
removed; they are superseded by the `recoupable-skills` virtual plugin entry
in the marketplace. No skill paths under `skills/` changed, so existing
manual-clone consumers keep working.
Follow-up work (separate PRs):
- Archive `recoupable/plugins` and `recoupable/music-catalog-diligence`.
- Update mono/.gitmodules to drop the `plugins` submodule.
- Update sandbox/Trigger.dev installers to point at recoupable/skills.
Co-authored-by: Cursor <cursoragent@cursor.com>
…etection + skill routing
The skill now drives a 5-step flow:
Step 0 Detect environment + auth state (env vars + CLI + filesystem)
Step 1 Install CLI (BYOA only, skipped if already installed)
Step 2 Get an API key (BYOA only, skipped if RECOUP_API_KEY/ACCESS_TOKEN set)
Step 3 Verify auth + identify the account via /api/whoami
Step 4 Detect roster + filesystem state (orgs, artists, scaffold)
Step 5 Route to the right next skill based on the detected state
This handles both target user journeys end-to-end:
- BYOA from developers.recoupable.com: user pastes a getting-started prompt,
agent runs Step 0-5 in sequence — installs CLI, creates an account, detects
state, and routes to the right follow-up skill (create-artist for empty
rosters, artist-workspace for established ones, setup-sandbox when the
workspace tree is missing).
- Recoup-hosted on chat.recoupable.com: RECOUP_ACCESS_TOKEN is already
injected, so the skill skips install/auth and goes straight to state
detection + routing.
Also adds a throwaway-account warning (per the create-artist skill's caveat
about agent+ emails) and a routing table that picks between create-artist,
setup-sandbox, and artist-workspace based on the (orgs, artists, filesystem)
state vector.
No env vars or endpoints invented — uses the same `RECOUP_API_KEY` /
`RECOUP_ACCESS_TOKEN` / `RECOUP_ACCOUNT_ID` / `RECOUP_ORG_ID` already
documented in `recoup-api` and `setup-sandbox` skills, and the same
`/api/whoami`, `/api/organizations`, and `/api/organizations/{id}/artists`
endpoints used elsewhere.
Co-authored-by: Cursor <cursoragent@cursor.com>
…ble" to "recoup" Renames the brand inside our agent surface — what users type into install commands and what's displayed in plugin manifests — from "recoupable" to the shorter "recoup". Same product, cleaner identifier. Renamed (identifiers + display names): - Marketplace name: `recoupable` → `recoup` - Plugin name: `recoupable-skills` → `recoup-skills` - Display names: "Recoupable Skills" → "Recoup Skills", developerName "Recoupable" → "Recoup" - Owner/author name: "Recoupable" → "Recoup" (in marketplace.source.json + catalog plugin manifests) - Install paths: `@recoupable` → `@recoup` - MCP server key: `"recoupable":` → `"recoup":` (in getting-started skill's MCP config snippet) - Brand references in skill bodies + README/AGENTS/CHANGELOG/contributing.md: "Recoupable" → "Recoup" Intentionally NOT renamed (real-world resources that can't just rebrand): - All `recoupable.com` URLs (recoupable.com, chat.recoupable.com, developers.recoupable.com, api.recoupable.com) - Email address `agent@recoupable.com` (real account, real `agent+*@...` signup pattern) - GitHub repo paths `recoupable/skills`, `recoupable/music-catalog-diligence`, `recoupable/plugins` - NPM package `@recoupable/cli` - LICENSE legal entity `Copyright 2026 Recoupable Inc.` After this change, users install with: /plugin marketplace add recoupable/skills # repo path unchanged /plugin install recoup-skills@recoup # new identifier branding /plugin install music-catalog-diligence@recoup Regenerated all three platform marketplace JSONs from marketplace.source.json. Validator passes (`python3 scripts/validate-manifests.py`). Co-authored-by: Cursor <cursoragent@cursor.com>
📝 WalkthroughWalkthroughThis PR creates a unified "Recoup" skills marketplace (source: ChangesMarketplace Platform Infrastructure
Music Catalog Diligence Plugin
Repository Documentation and Branding
Sequence Diagram(s)sequenceDiagram
participant Dev as Developer
participant Repo as Repository
participant Gen as Generator
participant Val as Validator
participant CI as GitHub Actions
Dev->>Repo: Update `marketplace.source.json` or plugin files
CI->>Gen: Run `scripts/generate-marketplaces.py --check`
Gen->>Repo: Load `marketplace.source.json` and compare generated targets
Gen-->>CI: Return sync / diff result
CI->>Val: Run `scripts/validate-manifests.py`
Val->>Repo: Verify plugin dirs, manifests, and `SKILL.md` frontmatter
Val-->>CI: Return validation result
CI-->>Dev: PR check passes/fails
sequenceDiagram
participant User as User/Agent
participant Skill as Plugin Skill
participant Scripts as Plugin Scripts
participant Workspace as Deal Workspace
participant Findings as Findings & Ledger
participant Hooks as Hooks
User->>Skill: Run `catalog-kickoff`
Skill->>Workspace: Create `deals/{deal-id}` and `assumptions.yaml`
User->>Skill: Run `catalog-ingest`
Skill->>Scripts: run `build-file-manifest.py` -> `file-manifest.json`
Skill->>Scripts: run `normalize-royalty-statement.py` -> `royalty-ledger.csv`
Skill->>Scripts: run `validate-normalized-ledger.py`
Scripts-->>Findings: Write `workpapers` / `findings`
User->>Skill: Run `catalog-qc`
Skill->>Hooks: Invoke `Stop` hook for completion gate
Hooks->>Findings: Load `assumptions`, `evidence-ledger.json`, `findings.json`
Hooks-->>User: Approve or Block package completion
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 16
🧹 Nitpick comments (3)
plugins/music-catalog-diligence/scripts/_helpers.py (1)
35-60: ⚡ Quick winConsider handling escaped quotes in the minimal YAML parser.
The
_parse_scalarfunction (line 43) extracts quoted string content ass[1:-1]without processing escape sequences. This means a value like"foo\"bar"in YAML would be incorrectly parsed asfoo\"barinstead offoo"bar.While this is acceptable for the documented "limited grammar" and the function falls back to PyYAML when available, consider either:
- Adding basic escape handling for common cases (
\",\\)- Documenting this specific limitation in the docstring at lines 15-25
📝 Example: Add escape handling
if (s.startswith('"') and s.endswith('"')) or (s.startswith("'") and s.endswith("'")): - return s[1:-1] + content = s[1:-1] + if s[0] == '"': + content = content.replace(r'\"', '"').replace(r'\\', '\\') + return content🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/music-catalog-diligence/scripts/_helpers.py` around lines 35 - 60, The quoted-string branch in _parse_scalar currently returns s[1:-1] without unescaping, so escaped quotes and backslashes (e.g. "foo\"bar" or "c:\\path") are left raw; update _parse_scalar to unescape common sequences before returning (handle at minimum `\"` -> `"` and `\\` -> `\`, and consider `\n`, `\t` if desired), ensuring it only applies when the value is quoted, and reuse or add a small helper (or inline logic near the quoted-string branch) so other parsing functions like _split_flow remain unchanged; alternatively, if you prefer not to change behavior, add a clear note to the module docstring describing the limitation of not processing escape sequences for quoted scalars.plugins/music-catalog-diligence/scripts/test-build-manual-review-queue.py (1)
73-73: 💤 Low valueUnused variable
queue_mdflagged by static analysis.The test unpacks
queue_mdbut doesn't use it. Other tests in this file (lines 85, 93) follow the pattern of prefixing unused variables with_. Consider applying the same pattern for consistency.♻️ Suggested fix
- code, payload, coverage, queue_md = run_queue(workspace) + code, payload, coverage, _queue_md = run_queue(workspace)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/music-catalog-diligence/scripts/test-build-manual-review-queue.py` at line 73, The test currently unpacks four return values from run_queue(workspace) into code, payload, coverage, queue_md but never uses queue_md; change the unpack to use a discarded/underscore-prefixed name (e.g., _queue_md or _ ) to match the pattern used in other tests and satisfy static analysis. Locate the run_queue(...) call in the test (the call that assigns code, payload, coverage, queue_md) and replace queue_md with _queue_md (or _) so the unused value is clearly ignored while keeping the existing variables (code, payload, coverage) intact.plugins/music-catalog-diligence/hooks/hooks.json (1)
22-22: 💤 Low valueConsider extracting the long Stop hook prompt to a separate file.
The inline prompt is ~1400 characters. Extracting it to
hooks/completion-gate-prompt.mdwould improve readability and make the prompt easier to maintain and test. You could then reference it with a file path or load it at runtime.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/music-catalog-diligence/hooks/hooks.json` at line 22, The long inline prompt string assigned to the "prompt" field in hooks.json should be extracted to a separate file and referenced from hooks.json; create hooks/completion-gate-prompt.md containing the current prompt text and update the "prompt" value in plugins/music-catalog-diligence/hooks/hooks.json to load or point to that file (e.g., by replacing the large inline string with a file reference or loader call used by your runtime), ensuring the prompt content is identical and tests still pass; locate the prompt by the "prompt" key in hooks.json and the completion-gate behavior described there when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/validate.yml:
- Around line 12-16: The workflow uses floating tags for actions
(actions/checkout@v4 and actions/setup-python@v5); replace those with the
corresponding immutable full-length commit SHAs for actions/checkout and
actions/setup-python in the workflow file so CI is pinned to a reproducible
version. Locate the uses lines referencing "actions/checkout" and
"actions/setup-python" and update the ref values to the official full commit
SHAs for their respective releases.
In `@AGENTS.md`:
- Line 118: The checklist currently lists only `.claude-plugin/plugin.json` and
`.codex-plugin/plugin.json`; update the validation line to include
`.cursor-plugin/plugin.json` so it reads that every plugin folder under
`plugins/` contains `.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`,
and `.cursor-plugin/plugin.json`, and adjust any surrounding text that
references the "3-platform plugin structure" to reflect the three manifests
(Claude, Codex, Cursor).
In `@plugins/music-catalog-diligence/.cursor-plugin/plugin.json`:
- Line 10: The repository metadata in plugin.json is inconsistent with the
monorepo; update the "repository" value in
plugins/music-catalog-diligence/.cursor-plugin/plugin.json from
"https://github.com/recoupable/music-catalog-diligence" to point to the unified
monorepo "https://github.com/recoupable/skills" so the repository field for the
plugin matches the marketplace definitions and other plugins.
In `@plugins/music-catalog-diligence/hooks/protect-source-files.sh`:
- Around line 43-45: The case pattern `*/deals/*/source/*` misses relative paths
like `deals/abc/source/file.csv`; update the case for `file_path` to include
additional glob variants (e.g., `deals/*/source/*` and `*/deals/*/source/*`) so
both relative and non-relative matches are caught, keeping the same
`reason="Refused write to immutable source file: ${file_path}. "` assignment;
modify the case arm for `file_path` to use a combined pattern (`deals/*/source/*
| */deals/*/source/*)`) to block all variants.
In `@plugins/music-catalog-diligence/references/tooling-landscape.md`:
- Line 56: Update the wording in the table row for "Royalty Exchange" to replace
the awkward phrase "needs standardized royalty history" with "requires
standardized royalty history" so the sentence reads: "Royalty Exchange |
Marketplace underwriting requires standardized royalty history and buyer-facing
materials." Locate the table row containing the "Royalty Exchange" cell and
adjust the phrase accordingly in the tooling-landscape.md content.
In `@plugins/music-catalog-diligence/requirements.txt`:
- Around line 12-13: The requirements list allows pdfplumber to pull in
vulnerable Pillow versions; add an explicit requirement for Pillow pinned to a
patched release (e.g., Pillow>=12.2.0) alongside pdfplumber in requirements.txt
so that installing pdfplumber cannot bring in a vulnerable Pillow, updating the
file entries near the existing pdfplumber and openpyxl lines and optionally add
a comment referencing pdfplumber's transitive dependency to aid future
reviewers.
In `@plugins/music-catalog-diligence/scripts/calculate-nps-nls-bridge.py`:
- Around line 24-28: The loop over adjustments assumes each item is a dict;
before calling adjustment.get(...) validate each entry with
isinstance(adjustment, dict) (use the loop index from the iteration to identify
the offending element), and if the check fails raise a clear exception (e.g.,
ValueError/TypeError) that includes the index and the actual value/type; only
after validation access adjustment.get(...) to set label/amount and append to
rows (variables referenced: adjustments, adjustment, label, amount, normalized,
rows).
- Around line 17-27: Replace floating-point usage with Decimal for monetary
calculations: import Decimal and use it to parse reported =
Decimal(str(data.get("reported_amount", 0))) (with try/except to raise
SystemExit on invalid input), convert each adjustment amount via amount =
Decimal(str(adjustment.get("amount", 0))) inside the loop, keep normalized as a
Decimal and update normalized += amount, and populate rows with Decimal amounts;
ensure JSON serialization later converts Decimal values to strings. Update
handling around reported, adjustments, normalized, rows, and the adjustment loop
to use Decimal and add minimal validation/error handling for bad numeric inputs.
In `@plugins/music-catalog-diligence/scripts/extract-pdf-statement.py`:
- Around line 550-556: The loop currently appends per-file statuses (e.g., in
the except block that calls extract_rows) but the script still returns a global
success exit code even when some files errored; after the per_file processing
(and similarly for the block around lines 576-587), compute whether any entry
has a non-"ok" status (e.g., failures = any(item.get("status") != "ok" for item
in per_file)) and if so call sys.exit(1) (otherwise sys.exit(0)); add imports as
needed and place this check at the end of the script so extraction failures
cause a non-zero exit.
- Around line 373-384: In determine_period, guard access to row with
template.period_index to avoid IndexError on short rows: before using
row[template.period_index] in the "row_quarter", "row_year", and "row_period"
branches, verify template.period_index is not None and within bounds (0 <=
template.period_index < len(row)); if out of range, use an empty string as the
source value so row_quarter/row_year are called with "" (or return the
appropriate ("", "")/("",) fallback) instead of indexing into row directly.
Ensure you update the checks around determine_period, referencing the function
name determine_period and helpers row_quarter, row_year, and the attribute
template.period_index.
In `@plugins/music-catalog-diligence/scripts/normalize-royalty-statement.py`:
- Around line 700-705: The code currently computes rate and leaves status="ok"
even when normalized_rows is empty; change the logic so an empty normalization
is treated as non-OK: first check if not normalized_rows and in that case set
status to "partial" (or another non-OK value), populate missing via
expected_columns_not_found(provider, headers), and skip/avoid relying on
compute_population_rate; otherwise compute_population_rate(normalized_rows) and
apply the PARTIAL_THRESHOLD branch as before (refer to symbols: normalized_rows,
compute_population_rate, PARTIAL_THRESHOLD, status, expected_columns_not_found).
In `@plugins/music-catalog-diligence/scripts/test-normalize-royalty-statement.py`:
- Around line 18-22: write_csv currently assumes rows is non-empty and does
rows[0].keys(), which will IndexError for an empty list; add a guard at the top
of the write_csv function to handle empty rows (e.g., if not rows: raise
ValueError("rows must be non-empty") or return after creating an empty file) so
the function fails fast or produces a predictable empty output; update write_csv
to check `if not rows` before accessing rows[0] (refer to the write_csv function
and its use of rows[0].keys()).
In `@plugins/music-catalog-diligence/scripts/validate-deal-workspace.py`:
- Around line 25-29: The validator currently only catches json.JSONDecodeError
in validate_json_file when calling json.loads(path.read_text(...)) so file read
failures (OSError) or invalid UTF-8 (UnicodeDecodeError) will raise and crash
the script; update validate_json_file to first wrap
path.read_text(encoding="utf-8") in a try/except that catches OSError and
UnicodeDecodeError (in addition to keeping the JSONDecodeError handling for
json.loads) and return a structured error like f"{label} could not be read:
{err}" or f"{label} has invalid encoding: {err}" so all read/parse failures
produce validation errors rather than exceptions.
In `@plugins/music-catalog-diligence/scripts/validate-evidence-ledger.py`:
- Around line 43-47: The duplicate detection logic incorrectly only adds string
evidence_id values to the seen set but checks duplicates for all types; update
the handling in the loop that reads entry.get("evidence_id") so that either (A)
you validate type explicitly (raise or append an error to errors when
evidence_id is not a str) and only perform duplicate logic for valid strings, or
(B) allow any hashable type by adding non-string evidence_id values to seen as
well; modify the block around evidence_id, seen, and errors so duplicate
detection and tracking are consistent (refer to the variables evidence_id, seen,
errors and the entry.get call and adjust to one of the two approaches).
In `@plugins/music-catalog-diligence/skills/royalty-audit/SKILL.md`:
- Around line 21-23: This step currently references an external skill artifact
("skills/catalog-analysis/references/pro-performance-income.md"), creating a
cross-skill dependency; to fix, remove that external path from SKILL.md and
instead point to or include a plugin-level shared reference (e.g., add or
reference
"plugins/music-catalog-diligence/references/pro-performance-income.md") or
duplicate the essential guidance into this skill’s references, then update the
instruction lines that mention decomposing PRO performance income to reference
the new local/shared artifact and leave the output locations
(findings/findings.json and workpapers/) unchanged; search for the exact string
"skills/catalog-analysis/references/pro-performance-income.md" in SKILL.md and
replace it with the new local/shared reference or embedded guidance.
In `@scripts/validate-manifests.py`:
- Line 62: Change the call that reads the skill manifest to specify UTF-8
encoding: replace the plain skill_md.read_text() call with an explicit encoding
parameter (i.e., skill_md.read_text(encoding="utf-8")) so the file is read
consistently across platforms; update any neighboring reads in the same module
using the same pattern if needed.
---
Nitpick comments:
In `@plugins/music-catalog-diligence/hooks/hooks.json`:
- Line 22: The long inline prompt string assigned to the "prompt" field in
hooks.json should be extracted to a separate file and referenced from
hooks.json; create hooks/completion-gate-prompt.md containing the current prompt
text and update the "prompt" value in
plugins/music-catalog-diligence/hooks/hooks.json to load or point to that file
(e.g., by replacing the large inline string with a file reference or loader call
used by your runtime), ensuring the prompt content is identical and tests still
pass; locate the prompt by the "prompt" key in hooks.json and the
completion-gate behavior described there when making the change.
In `@plugins/music-catalog-diligence/scripts/_helpers.py`:
- Around line 35-60: The quoted-string branch in _parse_scalar currently returns
s[1:-1] without unescaping, so escaped quotes and backslashes (e.g. "foo\"bar"
or "c:\\path") are left raw; update _parse_scalar to unescape common sequences
before returning (handle at minimum `\"` -> `"` and `\\` -> `\`, and consider
`\n`, `\t` if desired), ensuring it only applies when the value is quoted, and
reuse or add a small helper (or inline logic near the quoted-string branch) so
other parsing functions like _split_flow remain unchanged; alternatively, if you
prefer not to change behavior, add a clear note to the module docstring
describing the limitation of not processing escape sequences for quoted scalars.
In `@plugins/music-catalog-diligence/scripts/test-build-manual-review-queue.py`:
- Line 73: The test currently unpacks four return values from
run_queue(workspace) into code, payload, coverage, queue_md but never uses
queue_md; change the unpack to use a discarded/underscore-prefixed name (e.g.,
_queue_md or _ ) to match the pattern used in other tests and satisfy static
analysis. Locate the run_queue(...) call in the test (the call that assigns
code, payload, coverage, queue_md) and replace queue_md with _queue_md (or _) so
the unused value is clearly ignored while keeping the existing variables (code,
payload, coverage) intact.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e0bc9bc6-e7da-432d-a0df-0e9a0aa0d441
⛔ Files ignored due to path filters (20)
plugins/music-catalog-diligence/fixtures/golden/ascap-performance/expected-royalty-ledger.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/ascap-performance/input.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/bmi-performance/expected-royalty-ledger.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/bmi-performance/input.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/bmi-real-headers/expected-royalty-ledger.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/bmi-real-headers/input.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/curve-income/expected-royalty-ledger.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/curve-income/input.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/direct-sync/expected-royalty-ledger.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/direct-sync/input.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/distributor-master/expected-royalty-ledger.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/distributor-master/input.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/mlc-mechanical/expected-royalty-ledger.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/mlc-mechanical/input.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/publisher-admin/expected-royalty-ledger.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/publisher-admin/input.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/soundexchange/expected-royalty-ledger.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/soundexchange/input.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/youtube-content-id/expected-royalty-ledger.csvis excluded by!**/*.csvplugins/music-catalog-diligence/fixtures/golden/youtube-content-id/input.csvis excluded by!**/*.csv
📒 Files selected for processing (106)
.agents/plugins/marketplace.json.claude-plugin/marketplace.json.claude-plugin/plugin.json.codex-plugin/plugin.json.cursor-plugin/marketplace.json.github/workflows/validate.ymlAGENTS.mdCHANGELOG.mdREADME.mdcontributing.mdmarketplace.source.jsonplugins/music-catalog-diligence/.claude-plugin/plugin.jsonplugins/music-catalog-diligence/.codex-plugin/plugin.jsonplugins/music-catalog-diligence/.cursor-plugin/plugin.jsonplugins/music-catalog-diligence/.gitignoreplugins/music-catalog-diligence/LICENSEplugins/music-catalog-diligence/README.mdplugins/music-catalog-diligence/agents/diligence-qc-reviewer.mdplugins/music-catalog-diligence/agents/metadata-reconciler.mdplugins/music-catalog-diligence/agents/rights-chain-reviewer.mdplugins/music-catalog-diligence/agents/royalty-audit-analyst.mdplugins/music-catalog-diligence/agents/valuation-sensitivity-analyst.mdplugins/music-catalog-diligence/commands/catalog-analyze.mdplugins/music-catalog-diligence/commands/catalog-diligence.mdplugins/music-catalog-diligence/commands/catalog-ingest.mdplugins/music-catalog-diligence/commands/catalog-kickoff.mdplugins/music-catalog-diligence/commands/catalog-package.mdplugins/music-catalog-diligence/commands/catalog-qc.mdplugins/music-catalog-diligence/evals/README.mdplugins/music-catalog-diligence/evals/scenarios/financing-collateral-pack.jsonplugins/music-catalog-diligence/evals/scenarios/messy-data-room.jsonplugins/music-catalog-diligence/evals/scenarios/missing-chain-of-title.jsonplugins/music-catalog-diligence/evals/scenarios/one-song-concentration.jsonplugins/music-catalog-diligence/evals/scenarios/pro-bonus-spike.jsonplugins/music-catalog-diligence/evals/scenarios/recoupment-cliff.jsonplugins/music-catalog-diligence/evals/scenarios/seller-prep-cleanup.jsonplugins/music-catalog-diligence/fixtures/external-sources.mdplugins/music-catalog-diligence/fixtures/golden/bmi-real-headers/expected-status.jsonplugins/music-catalog-diligence/hooks/hooks.jsonplugins/music-catalog-diligence/hooks/protect-source-files.shplugins/music-catalog-diligence/references/deal-workspace.mdplugins/music-catalog-diligence/references/diligence-workflow.mdplugins/music-catalog-diligence/references/normalization.mdplugins/music-catalog-diligence/references/red-flags.mdplugins/music-catalog-diligence/references/tooling-landscape.mdplugins/music-catalog-diligence/requirements.txtplugins/music-catalog-diligence/scripts/_helpers.pyplugins/music-catalog-diligence/scripts/build-diligence-dashboard.pyplugins/music-catalog-diligence/scripts/build-file-manifest.pyplugins/music-catalog-diligence/scripts/build-manual-review-queue.pyplugins/music-catalog-diligence/scripts/calculate-concentration.pyplugins/music-catalog-diligence/scripts/calculate-nps-nls-bridge.pyplugins/music-catalog-diligence/scripts/dataroom-hygiene-scan.pyplugins/music-catalog-diligence/scripts/extract-pdf-statement.pyplugins/music-catalog-diligence/scripts/normalize-royalty-statement.pyplugins/music-catalog-diligence/scripts/run-diligence-checks.pyplugins/music-catalog-diligence/scripts/test-build-manual-review-queue.pyplugins/music-catalog-diligence/scripts/test-calculate-concentration.pyplugins/music-catalog-diligence/scripts/test-dataroom-hygiene-scan.pyplugins/music-catalog-diligence/scripts/test-diligence-readiness.pyplugins/music-catalog-diligence/scripts/test-golden-fixtures.pyplugins/music-catalog-diligence/scripts/test-helpers.pyplugins/music-catalog-diligence/scripts/test-normalize-royalty-statement.pyplugins/music-catalog-diligence/scripts/test-validate-deal-workspace.pyplugins/music-catalog-diligence/scripts/test-validate-findings-evidence.pyplugins/music-catalog-diligence/scripts/test-validate-workspace-consistency.pyplugins/music-catalog-diligence/scripts/validate-deal-workspace.pyplugins/music-catalog-diligence/scripts/validate-evidence-ledger.pyplugins/music-catalog-diligence/scripts/validate-findings-evidence.pyplugins/music-catalog-diligence/scripts/validate-normalized-ledger.pyplugins/music-catalog-diligence/scripts/validate-workspace-consistency.pyplugins/music-catalog-diligence/skills/catalog-analysis/SKILL.mdplugins/music-catalog-diligence/skills/catalog-analysis/evals/evals.jsonplugins/music-catalog-diligence/skills/catalog-analysis/references/output-templates.mdplugins/music-catalog-diligence/skills/catalog-analysis/references/pro-performance-income.mdplugins/music-catalog-diligence/skills/catalog-analysis/references/valuation-framework.mdplugins/music-catalog-diligence/skills/catalog-ingest/SKILL.mdplugins/music-catalog-diligence/skills/catalog-ingest/evals/evals.jsonplugins/music-catalog-diligence/skills/catalog-ingest/references/canonical-schema.mdplugins/music-catalog-diligence/skills/catalog-ingest/references/cleaning-rules.mdplugins/music-catalog-diligence/skills/catalog-ingest/references/data-room-checklist.mdplugins/music-catalog-diligence/skills/diligence-kickoff/SKILL.mdplugins/music-catalog-diligence/skills/financing-underwrite/SKILL.mdplugins/music-catalog-diligence/skills/financing-underwrite/evals/evals.jsonplugins/music-catalog-diligence/skills/ic-memo-package/SKILL.mdplugins/music-catalog-diligence/skills/post-close-admin/SKILL.mdplugins/music-catalog-diligence/skills/rights-diligence/SKILL.mdplugins/music-catalog-diligence/skills/rights-diligence/evals/evals.jsonplugins/music-catalog-diligence/skills/royalty-audit/SKILL.mdplugins/music-catalog-diligence/skills/royalty-audit/evals/evals.jsonplugins/music-catalog-diligence/skills/seller-prep/SKILL.mdplugins/music-catalog-diligence/skills/seller-prep/evals/evals.jsonplugins/music-catalog-diligence/templates/deal-workspace/assumptions.yamlplugins/music-catalog-diligence/templates/deal-workspace/evidence-ledger.jsonplugins/music-catalog-diligence/templates/deal-workspace/findings.jsonplugins/music-catalog-diligence/templates/deal-workspace/memos/financing-pack.mdplugins/music-catalog-diligence/templates/deal-workspace/memos/ic-memo.mdplugins/music-catalog-diligence/templates/deal-workspace/memos/seller-cleanup-report.mdplugins/music-catalog-diligence/templates/deal-workspace/missing-files.mdscripts/generate-marketplaces.pyscripts/validate-manifests.pyskills/artist-workspace/SKILL.mdskills/getting-started/SKILL.mdskills/recoup-api/SKILL.mdskills/setup-sandbox/SKILL.mdskills/trend-to-song/SKILL.md
💤 Files with no reviewable changes (2)
- .claude-plugin/plugin.json
- .codex-plugin/plugin.json
There was a problem hiding this comment.
14 issues found across 126 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="plugins/music-catalog-diligence/scripts/validate-evidence-ledger.py">
<violation number="1" location="plugins/music-catalog-diligence/scripts/validate-evidence-ledger.py:28">
P2: Handle non-object top-level JSON before calling `.get()` to avoid validator crashes on malformed ledger shape.</violation>
</file>
<file name="plugins/music-catalog-diligence/scripts/calculate-nps-nls-bridge.py">
<violation number="1" location="plugins/music-catalog-diligence/scripts/calculate-nps-nls-bridge.py:25">
P2: Validate each `adjustments` entry is an object before using `.get`, otherwise malformed input causes an unhandled `AttributeError`.</violation>
</file>
<file name="plugins/music-catalog-diligence/scripts/validate-normalized-ledger.py">
<violation number="1" location="plugins/music-catalog-diligence/scripts/validate-normalized-ledger.py:43">
P2: Blank `ledger_line_id` values are silently accepted, so the validator can pass ledgers with unidentified rows.</violation>
</file>
<file name="plugins/music-catalog-diligence/hooks/protect-source-files.sh">
<violation number="1" location="plugins/music-catalog-diligence/hooks/protect-source-files.sh:44">
P1: The source-protection glob misses repo-relative paths like `deals/<id>/source/...`, allowing writes that should be denied.</violation>
</file>
<file name="plugins/music-catalog-diligence/hooks/hooks.json">
<violation number="1" location="plugins/music-catalog-diligence/hooks/hooks.json:10">
P2: Quote `${CLAUDE_PLUGIN_ROOT}` in this shell-form hook command so the path survives shell tokenization.</violation>
</file>
<file name="plugins/music-catalog-diligence/skills/royalty-audit/SKILL.md">
<violation number="1" location="plugins/music-catalog-diligence/skills/royalty-audit/SKILL.md:22">
P3: Use the correct sibling reference path here; this path won’t resolve from the current skill file.</violation>
</file>
<file name="marketplace.source.json">
<violation number="1" location="marketplace.source.json:2">
P2: Point `$schema` at a real schema file or remove the reference; this path does not exist, so schema validation can't resolve it.</violation>
</file>
<file name="plugins/music-catalog-diligence/commands/catalog-analyze.md">
<violation number="1" location="plugins/music-catalog-diligence/commands/catalog-analyze.md:17">
P2: These helper-script invocations are incomplete; both scripts require a positional input file, so this step will just hit argparse usage errors.</violation>
</file>
<file name="plugins/music-catalog-diligence/skills/catalog-analysis/references/output-templates.md">
<violation number="1" location="plugins/music-catalog-diligence/skills/catalog-analysis/references/output-templates.md:37">
P2: Use the canonical bridge adjustments from `SKILL.md`. This version drops direct-license, spike, and undercollection/missing-registration adjustments, which can understate or double-count the normalized run-rate.</violation>
<violation number="2" location="plugins/music-catalog-diligence/skills/catalog-analysis/references/output-templates.md:70">
P2: Add a separate sensitivity table section; the catalog-analysis skill requires it as a distinct deliverable, and the current template only covers scenarios.</violation>
</file>
<file name="plugins/music-catalog-diligence/references/normalization.md">
<violation number="1" location="plugins/music-catalog-diligence/references/normalization.md:103">
P3: Document `expected-status.json` for partial fixtures; the current fixture layout omits a required file used by the test suite.</violation>
</file>
<file name="plugins/music-catalog-diligence/templates/deal-workspace/evidence-ledger.json">
<violation number="1" location="plugins/music-catalog-diligence/templates/deal-workspace/evidence-ledger.json:4">
P2: Use an empty evidence ledger template instead of seeding a fake row; the placeholder entry is treated as real traceability data and `confidence: "example"` is not a valid label.</violation>
</file>
<file name="plugins/music-catalog-diligence/scripts/normalize-royalty-statement.py">
<violation number="1" location="plugins/music-catalog-diligence/scripts/normalize-royalty-statement.py:703">
P1: Handle the zero-row case as non-OK output. Leaving `status` as `ok` when nothing is normalized lets ingest proceed with an empty ledger.</violation>
</file>
<file name="plugins/music-catalog-diligence/references/deal-workspace.md">
<violation number="1" location="plugins/music-catalog-diligence/references/deal-workspace.md:1">
P2: Custom agent: **Enforce Clear Code Style and Maintainability Practices**
This new file exceeds the repository’s <100-line file-size limit and consolidates multiple concerns into one monolithic markdown doc.</violation>
</file>
Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed. cubic prioritizes the most important files to review.
On a pro plan you can use ultrareview for larger PRs.
…st account.json
PR review surfaced four real issues that would break customers post-merge.
Three are fixed here. The fourth (setup-sandbox vs artist-workspace layout
inconsistency) is a deeper design decision documented as a follow-up.
1. **getting-started — /whoami endpoint doesn't exist.**
Step 3 originally called `/api/whoami` (returns 404 — the endpoint was
never built). Replaced with `/api/organizations` as the auth verification
call: 200 means the key works, response body is reused in Step 4 instead
of fetching orgs twice.
The account_id + email come from two sources now:
- Fresh signup (Path A or B): capture from agents/signup or
agents/verify response, persist to ~/.config/recoup/account.json.
- Returning agent: read account.json from disk.
- Sandbox: RECOUP_ACCOUNT_ID is already injected — wins.
- None of the above: proceed with "identity unknown" — throwaway
warning silently skips, the rest of the flow still works.
Step 5 routing table extended for the "0 orgs + email unknown"
case (returning agent with no cached identity) — asks the user
instead of crashing.
2. **create-artist — Bearer-only auth + wrong base URL.**
The skill required $RECOUP_ACCESS_TOKEN (sandbox-only) and used
api.recoupable.com (inconsistent with the rest of the marketplace).
Now uses the same AUTH_HEADER abstraction as getting-started, so it
accepts either RECOUP_API_KEY (BYOA) or RECOUP_ACCESS_TOKEN (sandbox).
All ~13 curl invocations updated to recoup-api.vercel.app + $AUTH_HEADER.
3. **chart-metric/README.md — bogus install command.**
Said `npx skills add recoupable/chartmetric` — no such repo. Replaced
with the actual install commands across Claude Code, Codex, and the
Vercel CLI (with `--skill chart-metric` for cherry-pick).
Validator passes. ETA on the fourth issue (setup-sandbox layout): separate
follow-up after a layout decision (single-org vs multi-org sandbox).
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
2 issues found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="skills/create-artist/SKILL.md">
<violation number="1" location="skills/create-artist/SKILL.md:18">
P1: Populate `AUTH_HEADER` with the bearer token here; otherwise the access-token path never authenticates the later curl calls.</violation>
</file>
<file name="skills/getting-started/SKILL.md">
<violation number="1" location="skills/getting-started/SKILL.md:163">
P1: Don't source account identity from the persisted cache without validating it against the current auth token; a stale `account.json` can misclassify the active account and route onboarding incorrectly.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
…o skill Two coordinated changes for the upcoming recoupable/skills#23 (unified marketplace where skills and plugins ship from one repo). 1. Add the short-video skill that just shipped in recoupable/skills#24: - New skills/short-video.mdx (card page) - New row in skills.mdx 'Doing the work' table - Sidebar entry in docs.json 2. Rewrite the Install section of skills.mdx for the post-#23 world: - One marketplace serves both skills and plugins (recoupable/skills) - Per-runtime install instructions for Claude Code / Cowork / Codex / Cursor / npx skills CLI - Drop references to the now-merged-in recoupable/plugins and recoupable/music-catalog-diligence external repos - 'How a plugin is structured' updated to reflect in-tree layout at recoupable/skills/plugins/<name>/ with the three per-runtime manifests 3. Update plugins/music-catalog-diligence.mdx install table to match. Net: 156 nav entries (was 155, +1 for short-video), 0 404s, 0 orphans, no em-dashes, all install commands point at the unified marketplace.
Five high-leverage fixes from the coderabbitai + cubic-dev-ai reviews
on this PR. Deferred ~27 lower-confidence bot comments for human review.
1. skills/create-artist/SKILL.md:18 - real AUTH_HEADER bug. The Bearer
token branch self-assigned AUTH_HEADER, so /api calls were never
authenticated when only RECOUP_ACCESS_TOKEN was set. Now sets
AUTH_HEADER="Authorization: Bearer $RECOUP_ACCESS_TOKEN".
2. AGENTS.md:118 - validator checklist omitted the Cursor manifest.
Added .cursor-plugin/plugin.json to keep the docs aligned with the
3-platform plugin structure.
3. plugins/music-catalog-diligence/.{cursor,claude,codex}-plugin/plugin.json
- 'repository' field pointed at the old recoupable/music-catalog-diligence
repo. Now points at recoupable/skills since the plugin lives in-tree.
4. marketplace.source.json:2 - $schema pointed at a non-existent file
(./scripts/marketplace.source.schema.json), which broke JSON schema
validation in editors. Removed the dead reference. (A real schema
can be added later if needed.)
5. plugins/music-catalog-diligence/hooks/hooks.json:10 - bash command
didn't quote ${CLAUDE_PLUGIN_ROOT}, so paths containing spaces would
tokenize incorrectly. Now: bash "${CLAUDE_PLUGIN_ROOT}/hooks/...".
validate-manifests.py: all manifests valid.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
skills/create-artist/SKILL.md (1)
10-10: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy liftCross-skill dependency violates self-contained principle.
This skill has a hard dependency on
artist-workspaceto scaffold theRECOUP.mdfile before execution (line 27 lists it as a prerequisite, line 10 describes the workflow dependency). The retrieved learning states: "Skills must be self-contained with no cross-dependencies between skills."While the dependency is explicit and well-documented, it creates coupling that may complicate skill distribution, testing, and maintenance. Consider either:
- Embedding the RECOUP.md scaffolding logic directly in this skill, or
- Making the skill gracefully handle missing workspace files (e.g., scaffold on-demand if not present), or
- Documenting an exception to the self-contained principle for orchestrator skills in this marketplace.
Based on learnings: Skills must be self-contained with no cross-dependencies between skills.
Also applies to: 27-27
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@skills/create-artist/SKILL.md` at line 10, This skill currently depends on the external artist-workspace scaffold (RECOUP.md) which violates self-contained rules; update the create-artist skill to detect a missing RECOUP.md at startup and scaffold a local RECOUP.md checklist (including frontmatter fields) on-demand, then drive the 8-step workflow from that checklist and persist captured values back into the RECOUP.md frontmatter as each step completes so state can be resumed; specifically add a "ensureRecoupScaffold" step at the start of the workflow that creates the RECOUP.md scaffold if absent, and update the code that processes the checklist to write progress into the RECOUP.md frontmatter rather than assuming artist-workspace exists.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@skills/create-artist/SKILL.md`:
- Line 10: This skill currently depends on the external artist-workspace
scaffold (RECOUP.md) which violates self-contained rules; update the
create-artist skill to detect a missing RECOUP.md at startup and scaffold a
local RECOUP.md checklist (including frontmatter fields) on-demand, then drive
the 8-step workflow from that checklist and persist captured values back into
the RECOUP.md frontmatter as each step completes so state can be resumed;
specifically add a "ensureRecoupScaffold" step at the start of the workflow that
creates the RECOUP.md scaffold if absent, and update the code that processes the
checklist to write progress into the RECOUP.md frontmatter rather than assuming
artist-workspace exists.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ac058d84-33b3-4f8e-a605-e0078025634f
📒 Files selected for processing (9)
AGENTS.mdmarketplace.source.jsonplugins/music-catalog-diligence/.claude-plugin/plugin.jsonplugins/music-catalog-diligence/.codex-plugin/plugin.jsonplugins/music-catalog-diligence/.cursor-plugin/plugin.jsonplugins/music-catalog-diligence/hooks/hooks.jsonskills/chart-metric/README.mdskills/create-artist/SKILL.mdskills/getting-started/SKILL.md
✅ Files skipped from review due to trivial changes (4)
- plugins/music-catalog-diligence/.cursor-plugin/plugin.json
- plugins/music-catalog-diligence/.claude-plugin/plugin.json
- skills/chart-metric/README.md
- plugins/music-catalog-diligence/.codex-plugin/plugin.json
🚧 Files skipped from review as they are similar to previous changes (3)
- plugins/music-catalog-diligence/hooks/hooks.json
- marketplace.source.json
- skills/getting-started/SKILL.md
There was a problem hiding this comment.
1 issue found across 7 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="AGENTS.md">
<violation number="1" location="AGENTS.md:118">
P2: CI validation still doesn't enforce `.cursor-plugin/plugin.json`, so this checklist item overstates the check and can give false confidence.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Hardening fixes across the music-catalog-diligence plugin scripts plus a handful of doc, template, and CI parity fixes flagged by CodeRabbit and cubic-dev-ai on PR #23. Hardening - protect-source-files.sh: extend deny glob to match relative (deals/.../source), dot-relative (./deals/.../source), and absolute (/abs/deals/.../source) paths so the guard cannot be bypassed. - calculate-nps-nls-bridge.py: switch monetary math to Decimal, reject non-object top-level and non-dict adjustment entries. - extract-pdf-statement.py: guard period_index against short rows; return summary status "partial" and exit 1 when any PDF errored. - normalize-royalty-statement.py: treat zero normalized rows as partial, never ok. - validate-deal-workspace.py: catch OSError and UnicodeDecodeError so unreadable files surface as validation errors instead of crashes. - validate-evidence-ledger.py: validate non-object top-level JSON and require evidence_id to be a string before deduping. - validate-normalized-ledger.py: flag blank ledger_line_id rows so unidentified rows do not pass silently. - test-normalize-royalty-statement.py: guard write_csv against empty rows. - requirements.txt: pin pillow>=12.2.0 to avoid vulnerable transitive versions pulled in by pdfplumber. Docs and templates - evidence-ledger.json template: ship empty entries (no fake placeholder row with confidence="example"). - output-templates.md: align quality-of-earnings bridge with the canonical adjustment ladder from catalog-analysis SKILL.md and add the separate sensitivity-table section the skill requires. - normalization.md: document expected-status.json for partial fixtures. - tooling-landscape.md: fix awkward "needs standardized" phrasing. - catalog-analyze.md: show the required positional input args for calculate-concentration.py and calculate-nps-nls-bridge.py. - royalty-audit/SKILL.md and catalog-analysis/SKILL.md: move pro-performance-income.md to plugin-level references/ so the two skills no longer cross-depend. Cache safety - getting-started/SKILL.md: validate the cached account_id against the current auth token before using it; discard the cache when the token cannot read the account, so onboarding cannot be routed into a different account's workspace. CI parity - scripts/validate-manifests.py: enforce .cursor-plugin/plugin.json on every self-contained plugin so the AGENTS.md checklist matches what CI actually checks. Also pass encoding="utf-8" to read_text. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
2 issues found across 19 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="skills/getting-started/SKILL.md">
<violation number="1" location="skills/getting-started/SKILL.md:168">
P2: Custom agent: **Enforce Clear Code Style and Maintainability Practices**
This change adds more logic to an already oversized, multi-responsibility SKILL.md instead of splitting the concern into a smaller file, violating the under-100-lines/single-responsibility rule.</violation>
</file>
<file name="plugins/music-catalog-diligence/skills/catalog-analysis/references/output-templates.md">
<violation number="1" location="plugins/music-catalog-diligence/skills/catalog-analysis/references/output-templates.md:36">
P2: Custom agent: **Enforce Clear Code Style and Maintainability Practices**
This file exceeds the custom rule’s 100-line limit, so it no longer complies with the maintainability constraint.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
|
Splitting this PR into 4 reviewable units per @sweetmantech's feedback (128 files was too big for one review).
Each PR has:
PR 1 and PR 4 are independent (off Closing this in favor of the 4-PR stack once you've taken a look. |
…nding) Sets up the single-source-of-truth marketplace pattern. After this lands, adding a new plugin is one entry in marketplace.source.json + a regen. No end-user behavior change yet — this PR ships the infrastructure only. What this PR adds - marketplace.source.json — single source of truth for the marketplace + the recoup-skills virtual plugin (the existing 12 broad music skills). - scripts/generate-marketplaces.py — emits the three platform manifests: .claude-plugin/marketplace.json, .agents/plugins/marketplace.json, .cursor-plugin/marketplace.json. --check mode detects drift. - scripts/validate-manifests.py — checks generator parity, plugin source paths, .claude/.codex/.cursor plugin manifests on each plugins/ folder, and SKILL.md frontmatter (name + description) on every skill. - .github/workflows/validate.yml — runs both scripts on every PR. - AGENTS.md, README.md, contributing.md, CHANGELOG.md — describe the source-of-truth + generated-files pattern (with recoup branding). - Removes the now-superseded root .claude-plugin/plugin.json and .codex-plugin/plugin.json (the recoup-skills virtual plugin entry in the marketplace replaces them). Branding - Marketplace name: recoup - Plugin name: recoup-skills - Display names: Recoup Skills, developerName: Recoup - Owner/author: Recoup - Install path: @recoup - recoupable.com URLs, agent@recoupable.com, recoupable/skills repo path, @recoupable/cli npm package, and LICENSE entity stay unchanged. After this lands /plugin marketplace add recoupable/skills /plugin install recoup-skills@recoup Why this is small enough to review - 13 files, +961 / -137. All-new infrastructure, zero behavior change for end users (no skill paths under skills/ moved). - Generated files are mechanically derivable from marketplace.source.json. Spot-check the source file; trust the generator. What's intentionally NOT in this PR - No catalog plugin (separate PR — adds plugins/music-catalog-diligence/ directory and registers it in marketplace.source.json). - No getting-started orchestrator rewrite (separate PR). Test plan - python3 scripts/generate-marketplaces.py --check → exit 0 - python3 scripts/validate-manifests.py → exit 0 Replaces #23 (split into 3 PRs; rename baked into each). Co-authored-by: Cursor <cursoragent@cursor.com>
|
Update: split is now 3 PRs, not 4. Baked the recoup rename into each PR instead of separating it.
(Original PR 28 closed — rename now lives in each of the 3 PRs above.) All 3 are MERGEABLE with CI green. PR 1 and PR 3 are independent (off Closing this in favor of the 3-PR stack once you've taken a look. |
…nding) Sets up the single-source-of-truth marketplace pattern. After this lands, adding a new plugin is one entry in marketplace.source.json + a regen. No end-user behavior change yet — this PR ships the infrastructure only. What this PR adds - marketplace.source.json — single source of truth for the marketplace + the recoup-skills virtual plugin (the existing 12 broad music skills). - scripts/generate-marketplaces.py — emits the three platform manifests: .claude-plugin/marketplace.json, .agents/plugins/marketplace.json, .cursor-plugin/marketplace.json. --check mode detects drift. - scripts/validate-manifests.py — checks generator parity, plugin source paths, .claude/.codex/.cursor plugin manifests on each plugins/ folder, and SKILL.md frontmatter (name + description) on every skill. - .github/workflows/validate.yml — runs both scripts on every PR. - AGENTS.md, README.md, contributing.md, CHANGELOG.md — describe the source-of-truth + generated-files pattern (with recoup branding). - Removes the now-superseded root .claude-plugin/plugin.json and .codex-plugin/plugin.json (the recoup-skills virtual plugin entry in the marketplace replaces them). Branding - Marketplace name: recoup - Plugin name: recoup-skills - Display names: Recoup Skills, developerName: Recoup - Owner/author: Recoup - Install path: @recoup - recoupable.com URLs, agent@recoupable.com, recoupable/skills repo path, @recoupable/cli npm package, and LICENSE entity stay unchanged. After this lands /plugin marketplace add recoupable/skills /plugin install recoup-skills@recoup Why this is small enough to review - 13 files, +961 / -137. All-new infrastructure, zero behavior change for end users (no skill paths under skills/ moved). - Generated files are mechanically derivable from marketplace.source.json. Spot-check the source file; trust the generator. What's intentionally NOT in this PR - No catalog plugin (separate PR — adds plugins/music-catalog-diligence/ directory and registers it in marketplace.source.json). - No getting-started orchestrator rewrite (separate PR). Test plan - python3 scripts/generate-marketplaces.py --check → exit 0 - python3 scripts/validate-manifests.py → exit 0 Replaces #23 (split into 3 PRs; rename baked into each). Co-authored-by: Cursor <cursoragent@cursor.com>
…nding) Sets up the single-source-of-truth marketplace pattern. After this lands, adding a new plugin is one entry in marketplace.source.json + a regen. No end-user behavior change yet — this PR ships the infrastructure only. What this PR adds - marketplace.source.json — single source of truth for the marketplace + the recoup-skills virtual plugin (the existing 12 broad music skills). - scripts/generate-marketplaces.py — emits the three platform manifests: .claude-plugin/marketplace.json, .agents/plugins/marketplace.json, .cursor-plugin/marketplace.json. --check mode detects drift. - scripts/validate-manifests.py — checks generator parity, plugin source paths, .claude/.codex/.cursor plugin manifests on each plugins/ folder, and SKILL.md frontmatter (name + description) on every skill. - .github/workflows/validate.yml — runs both scripts on every PR. - AGENTS.md, README.md, contributing.md, CHANGELOG.md — describe the source-of-truth + generated-files pattern (with recoup branding). - Removes the now-superseded root .claude-plugin/plugin.json and .codex-plugin/plugin.json (the recoup-skills virtual plugin entry in the marketplace replaces them). Branding - Marketplace name: recoup - Plugin name: recoup-skills - Display names: Recoup Skills, developerName: Recoup - Owner/author: Recoup - Install path: @recoup - recoupable.com URLs, agent@recoupable.com, recoupable/skills repo path, @recoupable/cli npm package, and LICENSE entity stay unchanged. After this lands /plugin marketplace add recoupable/skills /plugin install recoup-skills@recoup Why this is small enough to review - 13 files, +961 / -137. All-new infrastructure, zero behavior change for end users (no skill paths under skills/ moved). - Generated files are mechanically derivable from marketplace.source.json. Spot-check the source file; trust the generator. What's intentionally NOT in this PR - No catalog plugin (separate PR — adds plugins/music-catalog-diligence/ directory and registers it in marketplace.source.json). - No getting-started orchestrator rewrite (separate PR). Test plan - python3 scripts/generate-marketplaces.py --check → exit 0 - python3 scripts/validate-manifests.py → exit 0 Replaces #23 (split into 3 PRs; rename baked into each). Co-authored-by: Cursor <cursoragent@cursor.com>
…nding) Sets up the single-source-of-truth marketplace pattern. After this lands, adding a new plugin is one entry in marketplace.source.json + a regen. No end-user behavior change yet — this PR ships the infrastructure only. What this PR adds - marketplace.source.json — single source of truth for the marketplace + the recoup-skills virtual plugin (the existing 12 broad music skills). - scripts/generate-marketplaces.py — emits the three platform manifests: .claude-plugin/marketplace.json, .agents/plugins/marketplace.json, .cursor-plugin/marketplace.json. --check mode detects drift. - scripts/validate-manifests.py — checks generator parity, plugin source paths, .claude/.codex/.cursor plugin manifests on each plugins/ folder, and SKILL.md frontmatter (name + description) on every skill. - .github/workflows/validate.yml — runs both scripts on every PR. - AGENTS.md, README.md, contributing.md, CHANGELOG.md — describe the source-of-truth + generated-files pattern (with recoup branding). - Removes the now-superseded root .claude-plugin/plugin.json and .codex-plugin/plugin.json (the recoup-skills virtual plugin entry in the marketplace replaces them). Branding - Marketplace name: recoup - Plugin name: recoup-skills - Display names: Recoup Skills, developerName: Recoup - Owner/author: Recoup - Install path: @recoup - recoupable.com URLs, agent@recoupable.com, recoupable/skills repo path, @recoupable/cli npm package, and LICENSE entity stay unchanged. After this lands /plugin marketplace add recoupable/skills /plugin install recoup-skills@recoup Why this is small enough to review - 13 files, +961 / -137. All-new infrastructure, zero behavior change for end users (no skill paths under skills/ moved). - Generated files are mechanically derivable from marketplace.source.json. Spot-check the source file; trust the generator. What's intentionally NOT in this PR - No catalog plugin (separate PR — adds plugins/music-catalog-diligence/ directory and registers it in marketplace.source.json). - No getting-started orchestrator rewrite (separate PR). Test plan - python3 scripts/generate-marketplaces.py --check → exit 0 - python3 scripts/validate-manifests.py → exit 0 Replaces #23 (split into 3 PRs; rename baked into each). Co-authored-by: Cursor <cursoragent@cursor.com>
…nding) Sets up the single-source-of-truth marketplace pattern. After this lands, adding a new plugin is one entry in marketplace.source.json + a regen. No end-user behavior change yet — this PR ships the infrastructure only. What this PR adds - marketplace.source.json — single source of truth for the marketplace + the recoup-skills virtual plugin (the existing 12 broad music skills). - scripts/generate-marketplaces.py — emits the three platform manifests: .claude-plugin/marketplace.json, .agents/plugins/marketplace.json, .cursor-plugin/marketplace.json. --check mode detects drift. - scripts/validate-manifests.py — checks generator parity, plugin source paths, .claude/.codex/.cursor plugin manifests on each plugins/ folder, and SKILL.md frontmatter (name + description) on every skill. - .github/workflows/validate.yml — runs both scripts on every PR. - AGENTS.md, README.md, contributing.md, CHANGELOG.md — describe the source-of-truth + generated-files pattern (with recoup branding). - Removes the now-superseded root .claude-plugin/plugin.json and .codex-plugin/plugin.json (the recoup-skills virtual plugin entry in the marketplace replaces them). Branding - Marketplace name: recoup - Plugin name: recoup-skills - Display names: Recoup Skills, developerName: Recoup - Owner/author: Recoup - Install path: @recoup - recoupable.com URLs, agent@recoupable.com, recoupable/skills repo path, @recoupable/cli npm package, and LICENSE entity stay unchanged. After this lands /plugin marketplace add recoupable/skills /plugin install recoup-skills@recoup Why this is small enough to review - 13 files, +961 / -137. All-new infrastructure, zero behavior change for end users (no skill paths under skills/ moved). - Generated files are mechanically derivable from marketplace.source.json. Spot-check the source file; trust the generator. What's intentionally NOT in this PR - No catalog plugin (separate PR — adds plugins/music-catalog-diligence/ directory and registers it in marketplace.source.json). - No getting-started orchestrator rewrite (separate PR). Test plan - python3 scripts/generate-marketplaces.py --check → exit 0 - python3 scripts/validate-manifests.py → exit 0 Replaces #23 (split into 3 PRs; rename baked into each). Co-authored-by: Cursor <cursoragent@cursor.com>
|
Renumbered for review order — least-code first. Each PR description is now problem-first and quick to read.
Order doesn't constrain merging:
Read in order 1→2→3 for the smallest-to-largest review effort. |
|
Update: PR 26 closed and replaced by #29 (fix-only). Decision: PR 26 was doing too much. Two scopes mixed in one PR (fix broken stuff + introduce orchestrator pattern with state detection / persona routing / cache safety). Split.
Closed: Order doesn't constrain merging:
Read 1 → 2 → 3 for smallest-to-largest review effort. |
Summary
Consolidates three previously-separate repos (
recoupable/skills,recoupable/plugins,recoupable/music-catalog-diligence) into a single multi-agent marketplace + plugin host. After this lands, customers add ONE marketplace and install whichever plugins they need; Claude Code, Codex, and Cursor all work from the same source.Four logical changes, one cohesive shipment:
marketplace.source.jsonis the single source of truth. A zero-dependency Python generator (scripts/generate-marketplaces.py) produces the three platform marketplace files (.claude-plugin/,.agents/plugins/,.cursor-plugin/) on every change. CI runs a validator (scripts/validate-manifests.py) on every PR that checks marketplace parity, plugin source paths, plugin manifests, and everySKILL.mdfrontmatter.git subtree—plugins/music-catalog-diligence/brings in the full standalone catalog plugin with history preserved (commita95f3dc). 9 catalog skills + 5 specialist agents + 6 slash commands + scripts + templates + evals + fixtures, all in-tree.getting-startedbecomes an onboarding orchestrator — was a one-shot CLI install doc, is now a 5-step orchestrator: detects environment + auth state, installs CLI/key only when needed, identifies the account via/api/whoami, fetches roster (orgs + artists), inspects scaffold state, then routes tocreate-artist/setup-sandbox/artist-workspacebased on the detected state vector. Handles both target user journeys: BYOA viadevelopers.recoupable.com(paste-prompt onboarding) and Recoup-hosted viachat.recoupable.com(injected-token sandbox).recoupable→recouprename across marketplace + plugin identifiers and display names. Repo path staysrecoupable/skills. Real URLs (recoupable.com,agent@recoupable.com) and the legal entity in LICENSE stay unchanged. Install commands become/plugin install recoup-skills@recoupand/plugin install music-catalog-diligence@recoup.After merging:
What's deliberately NOT in this PR
These are real follow-ups, scoped to separate branches/PRs after this lands. Documented in
SCRATCHPAD.mdat the mono root.mono/tasks/src/sandboxes/installSkills.tsto scope sandbox installs to broad skills only via--skill <comma-list>frommarketplace.source.json. Verified earlier:npx skills add recoupable/skillsdiscovers all 21 skills (12 broad + 9 catalog) by default. Without scoping, customer sandboxes would silently install catalog skill descriptions whose Python runtime isn't in the sandbox.recoupable/pluginsandrecoupable/music-catalog-diligenceonce consumers are migrated.pluginssubmodule frommono/.gitmodulesafter Open Agents migration.SCRATCHPAD.mdParts VIII-XI for full plan.Test plan
python3 scripts/generate-marketplaces.py --check— exits 0, generated files match sourcepython3 scripts/validate-manifests.py— exits 0, all 21 SKILL.md files pass frontmatter validation, all plugin paths resolve, all marketplace JSONs in syncnpx skills add 'https://github.com/recoupable/skills.git#feat/unified-marketplace' --list— finds all 21 skills as expectednpx skills add ... --all --copy— installs all 21 SKILL.md files into.agents/skills/{name}//plugin marketplace add recoupable/skills, verify both plugins are discoverable/plugin install recoup-skills@recoupin Claude Code, verify all 12 broad skills load/plugin install music-catalog-diligence@recoupin Claude Code, verify catalog plugin loads with skills + agents + commandsdevelopers.recoupable.com"getting started" prompt successfully runs/getting-startedand routes tocreate-artistfor a fresh accountFiles
marketplace.source.json— single source of truth (edit this; regenerate)scripts/generate-marketplaces.py— regenerate the three platform marketplace filesscripts/validate-manifests.py— CI validator.github/workflows/validate.yml— runs validator on every PR.claude-plugin/marketplace.json,.agents/plugins/marketplace.json,.cursor-plugin/marketplace.json— generated, do not editskills/— 12 broad music skills (existing + the newcreate-artistfrom prior PR)plugins/music-catalog-diligence/— full catalog plugin (skills + agents + commands + scripts + templates + evals + fixtures)skills/getting-started/SKILL.md— rewritten as 5-step orchestratorREADME.md,AGENTS.md,contributing.md,CHANGELOG.md— rewritten for the unified modelWhy the
recoupable/skillsrepo path stays unchangedGitHub repo paths are real-world references (CLI installers, npm cache keys, customer bookmarks). Renaming would break every existing consumer. Only the marketplace and plugin identifiers are renamed — what users type into install commands.
Made with Cursor
Summary by cubic
Unifies skills and plugins into a single multi‑agent marketplace with generated manifests for Claude Code, Codex, and Cursor. Adds the
music-catalog-diligenceplugin, a guided onboarding flow, CI validation, and follow‑up hardening across diligence scripts and guardrails.New Features
marketplace.source.jsonwith generatorscripts/generate-marketplaces.pyand CI validatorscripts/validate-manifests.py; produces.claude-plugin/marketplace.json,.agents/plugins/marketplace.json,.cursor-plugin/marketplace.json.plugins/music-catalog-diligence/plugin with skills, agents, commands, scripts, fixtures, tests, and guardrails (PreToolUse to protectdeals/*/source/, Stop hook for QC).skills/getting-startedbecomes a 5‑step onboarding orchestrator that detects env/auth and routes tocreate-artist,setup-sandbox, orartist-workspace.recoup(e.g.,recoup-skills@recoup); repo path andrecoupable.comURLs unchanged.Bug Fixes
deals/*/source/; manifest validator enforces a.cursor-plugin/plugin.jsonon each plugin; normalized ledger validator flags blankledger_line_id.calculate-nps-nls-bridge.pyuses Decimal and rejects bad inputs;extract-pdf-statement.pyfails fast and reports partial status on errors;normalize-royalty-statement.pytreats zero‑row outputs aspartial.validate-evidence-ledger.pyrequires object top‑level and stringevidence_ids;validate-deal-workspace.pyhandles unreadable files without crashing; findings/evidence and workspace consistency validators tightened.getting-startednow validates cachedaccount_idagainst the current token; fixed docs and command args; pinnedpillow>=12.2.0inrequirements.txt.Written for commit 32d7f76. Summary will update on new commits.
Summary by CodeRabbit
New Features
Documentation
Chores
Tests