Skip to content
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"homepage": "https://www.purchasely.com",
"repository": "https://github.com/Purchasely/Purchasely-AI-Plugin",
"license": "MIT",
"hooks": "./hooks/hooks.json",
"keywords": [
"purchasely",
"sdk",
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ All notable changes to this project are documented here. The format is based on
- `gemini-extension.json` at the repository root — unlocks one-shot install via `gemini extensions install https://github.com/Purchasely/Purchasely-AI-Plugin`.
- OpenCode plugin support via `.opencode/INSTALL.md` — covers prerequisites, the one-line `opencode.json` install, usage examples, updating, and Windows troubleshooting (`npm install --prefix` workaround).
- README *OpenCode* block in the per-harness Quickstart section pointing at `.opencode/INSTALL.md`.
- `SessionStart` hook (`hooks/hooks.json`, `hooks/hooks-cursor.json`, `hooks/session-start`, `hooks/run-hook.cmd`, `hooks/intro.md`) auto-injects pointers to the `integrate` / `review` / `debug` skills and `/purchasely:question` command so the plugin is discoverable without the user typing a slash command first. Works on Claude Code, Cursor, and any host honoring the standard SDK `additionalContext` envelope. Zero-dependency POSIX shell + polyglot `.cmd` wrapper for Windows (Git Bash / WSL).
- `.claude-plugin/plugin.json` now points at `./hooks/hooks.json`.

### Changed

Expand Down
8 changes: 8 additions & 0 deletions hooks/hooks-cursor.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"version": 1,
"hooks": {
"sessionStart": [
{ "command": "./hooks/run-hook.cmd session-start" }
]
}
}
16 changes: 16 additions & 0 deletions hooks/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|clear|compact",
"hooks": [
{
"type": "command",
"command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd\" session-start",
"async": false
}
]
}
]
}
}
15 changes: 15 additions & 0 deletions hooks/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Purchasely AI Plugin is available

This project is using the **Purchasely AI Plugin**. You have access to three task-scoped skills and one free-form Q&A command that cover the Purchasely SDK across iOS, Android, React Native, Flutter, and Cordova.

## Skills (auto-invoked when relevant)

- **`integrate`** — step-by-step SDK integration: install, `Purchasely.start(...)`, paywall display via `fetchPresentation(...)`, action interceptor, user login/logout, Restore, Manage Subscription, plus campaigns / promo offers / analytics.
- **`review`** — 24-point checklist that audits an existing integration for missing `processAction(true)` branches, deprecated APIs, identity ordering, `PrivacyInfo.xcprivacy`, Google Play Billing v8, log-level gating, and more.
- **`debug`** — diagnostic flow for blank paywalls, frozen UI, purchase failures, and deeplinks. Includes SDK debug logging (Step 0), `PLYError` decoding (Step 6), and the screen-issue-report escalation template (Step 5).

## Slash command

- **`/purchasely:question`** — free-form SDK Q&A. Routes to the `sdk-expert` agent for anything that doesn't fit the three skills above.

When the user mentions Purchasely, paywalls, subscriptions, `PLYPresentation`, `userLogin`, or related concepts, load the matching skill before answering. The wrapper class pattern (`PurchaselyService`, `IAPManager`, …) is a **recommendation**, not a requirement — direct `Purchasely.*` calls anywhere in the app are fully supported.
44 changes: 44 additions & 0 deletions hooks/run-hook.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
:; # -*- mode: sh -*-
:; # Polyglot wrapper: runs as cmd.exe on Windows and as /bin/sh elsewhere.
:; # On POSIX shells the leading ':;' lines are no-ops; the cmd-side @goto
:; # jumps past them into the Windows branch.
:;
:; # --- POSIX branch ---------------------------------------------------------
:; DIR=$(cd -- "$(dirname -- "$0")" && pwd)
:; HOOK_NAME=${1:-session-start}
:; shift 2>/dev/null || true
:; if [ -x "$DIR/$HOOK_NAME" ]; then
:; exec "$DIR/$HOOK_NAME" "$@"
:; elif [ -f "$DIR/$HOOK_NAME" ]; then
:; exec sh "$DIR/$HOOK_NAME" "$@"
:; else
:; echo "run-hook.cmd: hook not found: $DIR/$HOOK_NAME" >&2
:; exit 1
:; fi
:; exit 0

@echo off
setlocal
set "DIR=%~dp0"
set "HOOK_NAME=%~1"
if "%HOOK_NAME%"=="" set "HOOK_NAME=session-start"
shift
if exist "%DIR%%HOOK_NAME%" (
where sh >nul 2>nul
if %ERRORLEVEL%==0 (
sh "%DIR%%HOOK_NAME%" %*
exit /b %ERRORLEVEL%
) else (
where bash >nul 2>nul
if %ERRORLEVEL%==0 (
bash "%DIR%%HOOK_NAME%" %*
exit /b %ERRORLEVEL%
) else (
echo run-hook.cmd: no POSIX shell on PATH; install Git Bash or WSL. 1>&2
exit /b 1
)
)
) else (
echo run-hook.cmd: hook not found: %DIR%%HOOK_NAME% 1>&2
exit /b 1
)
53 changes: 53 additions & 0 deletions hooks/session-start
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/bin/sh
# Purchasely AI Plugin — SessionStart hook
#
# Emits a short pointer to the plugin's skills + slash command so the host
# (Claude Code, Cursor, Copilot CLI, …) knows the Purchasely playbook is
# available without the user having to type a slash command first.
#
# Output format is chosen at runtime:
# - Claude Code -> {"hookSpecificOutput": {"hookEventName": "SessionStart",
# "additionalContext": "..."}}
# - Cursor -> {"additional_context": "..."}
# - Standard SDK -> {"additionalContext": "..."} (top-level)
#
# Zero runtime dependencies: pure /bin/sh, no jq, no node.

set -eu

DIR=$(cd -- "$(dirname -- "$0")" && pwd)
INTRO_FILE="$DIR/intro.md"

if [ ! -f "$INTRO_FILE" ]; then
echo "session-start: missing $INTRO_FILE" >&2
exit 1
fi

# JSON-escape stdin (\, ", control chars, newlines) without external tools.
json_escape() {
awk '
BEGIN { ORS = "" }
{
s = $0
gsub(/\\/, "\\\\", s)
gsub(/"/, "\\\"", s)
gsub(/\r/, "\\r", s)
gsub(/\t/, "\\t", s)
printf "%s\\n", s
}
' "$1"
}

ESCAPED=$(json_escape "$INTRO_FILE")

# Platform detection.
# CLAUDECODE / CLAUDE_PLUGIN_ROOT -> Claude Code
# CURSOR_AGENT / CURSOR_TRACE_ID -> Cursor
# anything else -> generic SDK envelope
if [ "${CLAUDECODE:-}" = "1" ] || [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
printf '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"%s"}}\n' "$ESCAPED"
elif [ -n "${CURSOR_AGENT:-}" ] || [ -n "${CURSOR_TRACE_ID:-}" ]; then
printf '{"additional_context":"%s"}\n' "$ESCAPED"
else
printf '{"additionalContext":"%s"}\n' "$ESCAPED"
fi
Loading