Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
217 changes: 217 additions & 0 deletions .cursor/skills/publish-braintree-plugin/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
---
name: publish-braintree-plugin
description: >-
Publish @lambdacurry/medusa-payment-braintree to npm. Checks CHANGELOG coverage
of latest plugin changes, verifies the package version is bumped above the
published npm version, runs typecheck and Biome lint, then packs and publishes.
Use when the user asks to publish the Braintree plugin, release medusa-payment-braintree,
or ship a new Braintree package version.
disable-model-invocation: true
---

# Publish Braintree Plugin

Publish `@lambdacurry/medusa-payment-braintree` from `plugins/braintree-payment`.

**Hard rule:** Do not pack or publish until every gate below passes. If a gate fails, stop, report what is missing, and wait for the user. Do not invent changelog entries or bump versions unless the user explicitly asks you to fix them.

Package root: `plugins/braintree-payment`
Package name: `@lambdacurry/medusa-payment-braintree`
Changelog: `plugins/braintree-payment/CHANGELOG.md`
Registry: always pass `--registry=https://registry.npmjs.org` on `npm whoami`, `npm view`, and `npm publish` (including dry-run).

```text
Publish Progress:
- [ ] 1. Changelog covers latest plugin changes
- [ ] 2. Version bumped above npm
- [ ] 3. Typecheck passes
- [ ] 4. Lint passes
- [ ] 5. Pack succeeds
- [ ] 6. Publish succeeds
```

## Gate 1 — Changelog covers latest changes

1. Read `plugins/braintree-payment/package.json` and note `version` as `LOCAL_VERSION`.
2. Read `plugins/braintree-payment/CHANGELOG.md`. The topmost `## X.Y.Z` section must equal `LOCAL_VERSION`.
3. Find the previous changelog version header (the next `##` below the top). Call it `PREV_VERSION`.
4. Require a clean plugin worktree before comparing history (pack/publish use the working tree):

```bash
git status --short -- plugins/braintree-payment
```

**Fail** if any staged, unstaged, or untracked paths appear under `plugins/braintree-payment`. Summarize the dirty paths and stop. (Do not publish undocumented local edits.)

5. Resolve `PREV_COMMIT` as the previous *release* boundary — the commit that **added** `PREV_VERSION` to `package.json`, not a later commit that removed it when bumping to `LOCAL_VERSION`. Fall back to the commit that introduced the `## PREV_VERSION` changelog header.

```bash
# Prefer the commit that introduced PREV_VERSION in package.json (addition only).
PREV_COMMIT=$(
git log -S"\"version\": \"$PREV_VERSION\"" --diff-filter=A --format=%H -- \
plugins/braintree-payment/package.json | tail -1
)

# Fallback: commit that introduced the PREV_VERSION changelog header.
if [ -z "$PREV_COMMIT" ]; then
PREV_COMMIT=$(
git log -S"## $PREV_VERSION" --diff-filter=A --format=%H -- \
plugins/braintree-payment/CHANGELOG.md | tail -1
Comment on lines +49 to +59

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major

Fix the PREV_COMMIT lookup.

--diff-filter=A selects commits where the file was added. It does not select a modified package.json commit that adds the matching version line. Git defines -S as an occurrence-count search and A as an added-path filter. A normal version bump therefore returns no commit for an existing package.json; the changelog fallback has the same defect. tail -1 also selects the oldest result because git log is reverse chronological by default. (git-scm.com)

Use a patch-aware search that identifies the commit adding the exact version line, then select the intended release boundary.

🧰 Tools
🪛 SkillSpector (2.5.1)

[warning] 130: [RP1] null: npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Remediation: Pin the version: npx @scope/server@1.2.3

(MCP Rug Pull (RP1))

🤖 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 @.cursor/skills/publish-braintree-plugin/SKILL.md around lines 49 - 59,
Update the PREV_COMMIT lookups in the publish flow to use patch-aware searches
that identify the commit adding the exact PREV_VERSION line, removing the
--diff-filter=A restriction from both package.json and CHANGELOG.md searches.
Select the intended release boundary from the results, ensuring the newest
matching commit is chosen rather than tail -1 returning the oldest.

)
fi

if [ -z "$PREV_COMMIT" ]; then
echo "Could not resolve PREV_COMMIT for $PREV_VERSION" >&2
exit 1
fi

git log --oneline "${PREV_COMMIT}..HEAD" -- plugins/braintree-payment
git diff "${PREV_COMMIT}..HEAD" --stat -- plugins/braintree-payment
```

6. Compare those commits/diffs to the `## LOCAL_VERSION` changelog section.
- User-facing fixes, improvements, breaking changes, and docs updates must appear.
- Ignore noise: lockfile-only, formatting-only, or leftover `.tgz` artifacts unless the release intentionally changes packaging.
7. **Fail** if the top section is missing, mismatched, empty when there are meaningful changes, or omits notable behavior/API/packaging changes. Summarize the gaps and stop.

## Gate 2 — Version is bumped

**Release policy**

- Stable `LOCAL_VERSION` (no `-` prerelease id): compare against the highest **stable** published version. Publish without a custom dist-tag (npm default `latest`).
- Prerelease `LOCAL_VERSION` (contains `-`, e.g. `0.2.0-next`): compare against the highest published version in the **same prerelease channel** (same suffix after `-`). Publish with `--tag <channel>` (e.g. `--tag next`). Never let a prerelease update `latest`.

1. Read local version:

```bash
node -p "require('./plugins/braintree-payment/package.json').version"
```

2. Read the full published version list (not the `latest` dist-tag alone):

```bash
npm view @lambdacurry/medusa-payment-braintree versions --json \
--registry=https://registry.npmjs.org
```

3. Filter that list per the release policy above, take the highest allowed published version as `HIGHEST_ALLOWED`, and **fail** unless `LOCAL_VERSION` is strictly greater (semver).
4. Exact-version guard (separate from the highest-version check):

```bash
npm view "@lambdacurry/medusa-payment-braintree@$LOCAL_VERSION" version \
--registry=https://registry.npmjs.org
```

**Fail** if that command returns `LOCAL_VERSION` (already published).
Comment on lines +90 to +105

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -u

file=".cursor/skills/publish-braintree-plugin/SKILL.md"
printf '%s\n' "== file status =="
git ls-files -- "$file"
printf '%s\n' "== lines 1-180 =="
sed -n '1,180p' "$file"
printf '%s\n' "== related references =="
rg -n -C 3 'npm view|versions --json|LOCAL_VERSION|HIGHEST_ALLOWED|set -e|exit status|not-found|not found|fail closed|registry' .cursor/skills .github 2>/dev/null || true

Repository: lambda-curry/medusa-plugins

Length of output: 18124


🏁 Script executed:

#!/bin/bash
set +e

pkg='`@lambdacurry/medusa-payment-braintree`'
missing='0.0.0-coderabbit-review-nonexistent'

run() {
  label="$1"
  shift
  "$@" >/tmp/npm-review-out 2>/tmp/npm-review-err
  status=$?
  printf '\n== %s ==\nstatus=%s\nstdout=%s\nstderr=%s\n' \
    "$label" "$status" "$(cat /tmp/npm-review-out)" "$(head -5 /tmp/npm-review-err)"
}

if command -v npm >/dev/null 2>&1; then
  run "missing exact version" npm view "$pkg@$missing" version \
    --registry=https://registry.npmjs.org
  run "unreachable registry" npm view "$pkg@$missing" version \
    --registry=http://127.0.0.1:1
else
  printf 'npm is unavailable; no runtime probe executed\n'
fi
rm -f /tmp/npm-review-out /tmp/npm-review-err

Repository: lambda-curry/medusa-plugins

Length of output: 157


🌐 Web query:

npm CLI documentation npm view package version not found exit code registry error

💡 Result:

When the npm CLI cannot find a requested package or specific version, it typically returns an exit code of 1 and an E404 error [1][2][3]. Key details regarding this behavior include: Exit Codes and Error Handling Historically, some versions of the npm CLI exhibited inconsistent behavior where certain "not found" scenarios would result in a zero exit code with no output [4][5][1]. However, modern versions of the npm CLI have been updated to ensure that requesting a non-existent package or a specific non-existent version correctly triggers an E404 error and a non-zero (1) exit code [1][2]. Common Causes for "Not Found" (E404) When you encounter an E404 error, it generally indicates that the registry does not contain the specified package or version [3]. Common causes include: - Incorrect Package Name: Typographical errors in the package name [3]. - Incorrect Registry Configuration: The npm CLI may be pointing to a registry that does not host the package (e.g., a private registry instead of the public npm registry or a misconfigured scope) [3][6]. - Missing Version/Tag: The specific version or dist-tag requested does not exist [5][1][2]. - Unpublishing or Deletion: The package or specific version may have been unpublished or removed by the author [3]. - Temporary Registry Issues: Intermittent issues with the npm registry itself can occasionally cause valid packages to be reported as not found [7][8]. Troubleshooting Steps If you receive an E404 error for a package you believe should exist: 1. Verify Registry Settings: Run npm config get registry to ensure you are pointing to the intended registry (commonly https://registry.npmjs.org/) [6]. 2. Check Package Availability: Search the npm website directly to confirm the package and version exist [3]. 3. Check Network/Proxies: If in a corporate environment, verify if a proxy is interfering with registry requests [7]. 4. Retry: If the error seems sporadic, it may be a transient issue with the registry [7][8]. For further details, refer to the official npm documentation on common errors and the npm-view command [7][9].

Citations:


Fail closed on npm lookup errors.

Handle the exit status of both npm view commands explicitly. Continue the exact-version check only for a confirmed E404 (“not found”) result. Stop for all other errors. Fail if the full versions --json query does not succeed.

🧰 Tools
🪛 SkillSpector (2.5.1)

[warning] 130: [RP1] null: npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Remediation: Pin the version: npx @scope/server@1.2.3

(MCP Rug Pull (RP1))

🤖 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 @.cursor/skills/publish-braintree-plugin/SKILL.md around lines 90 - 105,
Update the publishing instructions around the full-version and exact-version npm
view commands to explicitly inspect exit status. Fail immediately if the
versions query fails, and run the exact-version check only when its result is a
confirmed E404; stop for every other error while still failing when the exact
version is returned.

5. Confirm `CHANGELOG.md` top section matches `LOCAL_VERSION` (already required by Gate 1).

## Gate 3 — Typecheck

From repo root:

```bash
yarn workspace @lambdacurry/medusa-payment-braintree typecheck
```

**Fail** on any TypeScript error. Do not continue.

## Gate 4 — Lint

Biome is the repo linter/formatter (`biome.json` at repo root). From repo root:

```bash
yarn biome check plugins/braintree-payment/src
```

**Fail** on diagnostics. Do not auto-fix unless the user asks; if they ask, run `yarn biome check --write plugins/braintree-payment/src`, re-run check, then continue.

## Gate 5 — Pack

`prepublishOnly` builds via `npx medusa plugin:build`, but **`npm pack` does not run `prepublishOnly`**. Build explicitly before packing.

From repo root:

```bash
yarn workspace @lambdacurry/medusa-payment-braintree build
npm pack ./plugins/braintree-payment
```

Confirm the tarball is created and contains the built `.medusa/server` output that `files` includes:

```bash
tar -tzf lambdacurry-medusa-payment-braintree-*.tgz | head -50
tar -tzf lambdacurry-medusa-payment-braintree-*.tgz | grep -E '\.medusa/server/' | head -20
```

**Fail** if `.medusa/server` paths are missing from the tarball.
Comment on lines +141 to +146

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '120,185p' .cursor/skills/publish-braintree-plugin/SKILL.md

Repository: lambda-curry/medusa-plugins

Length of output: 2410


🏁 Script executed:

printf '%s\n' '--- skill file ---'
sed -n '136,178p' .cursor/skills/publish-braintree-plugin/SKILL.md
printf '%s\n' '--- package metadata ---'
cat plugins/braintree-payment/package.json
printf '%s\n' '--- publish workflow references ---'
rg -n -C 5 'npm (pack|publish)|prepublishOnly|medusa plugin:build' .github plugins/braintree-payment/package.json
printf '%s\n' '--- deterministic shell behavior ---'
python3 - <<'PY'
import subprocess
for command in [
    "printf '%s\\n' absent | grep -E '\\.medusa/server/' | head -20",
    "set -o pipefail; printf '%s\\n' absent | grep -E '\\.medusa/server/' | head -20",
]:
    result = subprocess.run(["bash", "-c", command], text=True, capture_output=True)
    print(f"{command}\nstatus={result.returncode}")
PY

Repository: lambda-curry/medusa-plugins

Length of output: 6019


Publish the exact tarball that you verify.

The wildcard can select stale or multiple archives. Without pipefail, grep ... | head exits successfully when no .medusa/server path exists. Capture the filename from npm pack, validate that exact archive with an explicit failure check, and publish it with npm publish "$TARBALL". Publishing the package directory repacks it and runs prepublishOnly, so Gate 6 does not publish the archive inspected by Gate 5.

🧰 Tools
🪛 SkillSpector (2.5.1)

[warning] 130: [RP1] null: npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Remediation: Pin the version: npx @scope/server@1.2.3

(MCP Rug Pull (RP1))

🤖 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 @.cursor/skills/publish-braintree-plugin/SKILL.md around lines 141 - 146,
Update the packaging and publishing steps in the skill to capture the exact
filename returned by npm pack as TARBALL, inspect that archive rather than using
a wildcard, and explicitly fail when no .medusa/server entry is found. Publish
the verified archive with npm publish "$TARBALL" instead of publishing the
package directory, avoiding a repack through prepublishOnly.


Report the tarball name (e.g. `lambdacurry-medusa-payment-braintree-X.Y.Z.tgz`).

Do not commit generated `.tgz` files.

## Gate 6 — Publish

Confirm npm auth before publishing:

```bash
npm whoami --registry=https://registry.npmjs.org
```

If unauthenticated, stop and tell the user to log in (`npm login` or configure a token). Do not publish anonymously.

Publish the package directory (matches CI in `.github/workflows/publish.yml`). For prereleases, include `--tag <channel>` per the release policy:

```bash
# Stable example:
npm publish ./plugins/braintree-payment --access public \
--registry=https://registry.npmjs.org

# Prerelease example (0.2.0-next → tag next):
npm publish ./plugins/braintree-payment --access public --tag next \
--registry=https://registry.npmjs.org
Comment on lines +164 to +171

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- target skill context ---'
sed -n '130,225p' .cursor/skills/publish-braintree-plugin/SKILL.md

printf '%s\n' '--- workflow files ---'
git ls-files | rg '(^|/)\.github/workflows/|publish|braintree' | head -200

printf '%s\n' '--- publication-related references ---'
rg -n -C 4 --glob '*.yml' --glob '*.yaml' --glob '*.md' --glob '*.json' \
  'npm (publish|pack|view)|--tag|dist-tag|braintree-payment|PREV_COMMIT|git log|git diff|npx' .

Repository: lambda-curry/medusa-plugins

Length of output: 11180


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- publish workflow ---'
cat -n .github/workflows/publish.yml

printf '%s\n' '--- npm configuration and publish references ---'
git ls-files | rg '(^|/)(\.npmrc|package\.json)$' | xargs -r -n1 sh -c 'echo "--- $0"; cat -n "$0"'
rg -n -C 5 'npm publish|--dry-run|--tag|dist-tag|publish' .github .cursor plugins/braintree-payment/package.json 2>/dev/null || true

printf '%s\n' '--- relevant history-independent file metadata ---'
git diff --stat
git status --short

Repository: lambda-curry/medusa-plugins

Length of output: 37191


🌐 Web query:

npm publish default dist-tag prerelease version --tag latest official documentation

💡 Result:

By default, the npm publish command tags all published versions, including prerelease versions, with the latest distribution tag [1][2][3]. This behavior is the default behavior of the npm CLI unless the --tag option is explicitly provided during publication [1][2][4]. To avoid assigning the latest tag to a prerelease version, you must use the --tag flag to specify a different, non-latest tag [1][2]. Example of publishing a prerelease version without overwriting the latest tag: npm publish --tag beta In this example, the version will be tagged as beta instead of latest. When users run npm install <package_name> without a specific version or tag, npm will continue to install the version associated with the latest tag [5][2]. Official documentation indicates that this is intended behavior, though it is often noted by the community as a point of friction ("footgun") when developers inadvertently update the latest version for all users with a prerelease [6][3][7]. Projects typically use latest for stable releases and separate tags (e.g., beta, next, alpha) for unstable or prerelease versions to manage distribution effectively [1][2][8].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
from pathlib import Path
import re

skill = Path(".cursor/skills/publish-braintree-plugin/SKILL.md").read_text()
workflow = Path(".github/workflows/publish.yml").read_text()

publish_lines = [
    line.strip()
    for text in (skill, workflow)
    for line in text.splitlines()
    if re.search(r"\bnpm publish\b", line)
]

print("Publish commands:")
for line in publish_lines:
    print(line)

print("\nExplicit-tag status:")
for line in publish_lines:
    print(f"{'tag-present' if re.search(r'--tag(?:=|\s+)\S+', line) else 'tag-absent'}: {line}")

print("\nCurrent Braintree version:")
package = Path("plugins/braintree-payment/package.json").read_text()
match = re.search(r'"version"\s*:\s*"([^"]+)"', package)
print(match.group(1) if match else "not found")

assert "npm publish \"./$dir\" --access public" in workflow
assert "npm publish ./plugins/braintree-payment --access public \\" in skill
assert "--tag next" in skill
PY

Repository: lambda-curry/medusa-plugins

Length of output: 450


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
from pathlib import Path
import re

skill = Path(".cursor/skills/publish-braintree-plugin/SKILL.md").read_text()
workflow = Path(".github/workflows/publish.yml").read_text()

print("Skill publish blocks:")
for match in re.finditer(r"npm publish[^\n]*(?:\\\n[^\n]*)*", skill):
    command = match.group(0).replace("\\\n", " ")
    print(command)

print("\nWorkflow publish command:")
for line in workflow.splitlines():
    if "npm publish" in line:
        print(line.strip())

print("\nExplicit tag checks:")
commands = [
    match.group(0).replace("\\\n", " ")
    for match in re.finditer(r"npm publish[^\n]*(?:\\\n[^\n]*)*", skill)
]
commands += [line.strip() for line in workflow.splitlines() if "npm publish" in line]
for command in commands:
    has_tag = bool(re.search(r"--tag(?:=|\s+)\S+", command))
    print(("tag-present" if has_tag else "tag-absent") + ": " + command)

package = Path("plugins/braintree-payment/package.json").read_text()
version = re.search(r'"version"\s*:\s*"([^"]+)"', package).group(1)
print("\nCurrent Braintree version: " + version)
print("Current version is prerelease: " + str("-" in version))

assert "npm publish \"./$dir\" --access public" in workflow
assert any("--tag next" in command for command in commands)
assert "-next" in version
PY

Repository: lambda-curry/medusa-plugins

Length of output: 904


Make the dist-tag explicit on every publication path.

Pass --tag latest for stable publish and dry-run commands. In .github/workflows/publish.yml, derive latest for stable versions and the prerelease channel (for example, next) for prerelease versions, then pass it to npm publish. Otherwise, the current 0.2.0-next release can update latest.

🧰 Tools
🪛 SkillSpector (2.5.1)

[warning] 130: [RP1] null: npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Remediation: Pin the version: npx @scope/server@1.2.3

(MCP Rug Pull (RP1))

🤖 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 @.cursor/skills/publish-braintree-plugin/SKILL.md around lines 164 - 171,
Make the dist-tag explicit across all publication paths: add --tag latest to
stable npm publish and dry-run examples, and update the publish workflow to
derive latest for stable versions or the prerelease channel (such as next) for
prerelease versions before passing it to npm publish.

```

Verify the **exact** version (not the `latest` dist-tag), with `--prefer-online` and bounded retries for registry propagation:

```bash
LOCAL_VERSION="$(node -p "require('./plugins/braintree-payment/package.json').version")"
for i in 1 2 3 4 5; do
PUBLISHED="$(
npm view "@lambdacurry/medusa-payment-braintree@$LOCAL_VERSION" version \
--registry=https://registry.npmjs.org \
--prefer-online 2>/dev/null || true
)"
if [ "$PUBLISHED" = "$LOCAL_VERSION" ]; then
echo "Verified $LOCAL_VERSION on npm"
break
fi
if [ "$i" -eq 5 ]; then
echo "Timed out waiting for $LOCAL_VERSION on npm (last: ${PUBLISHED:-none})" >&2
exit 1
fi
sleep $((i * 3))
done
```

## Done report

After success, report:

- Published version and dist-tag used
- npm package URL: `https://www.npmjs.com/package/@lambdacurry/medusa-payment-braintree`
- Whether changelog/version gates were clean
- Typecheck/lint/pack/publish results
- Reminder: merging to `main` also triggers CI publish; if this version is already on npm, CI will skip it

## Dry run

If the user asks for a dry run, complete Gates 1–5 only, then run:

```bash
npm publish ./plugins/braintree-payment --access public --dry-run \
--registry=https://registry.npmjs.org
```

For prerelease dry-runs, include the same `--tag <channel>` you would use for a real publish.

Do not publish for real.