recur-skills 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,18 +1,41 @@
1
1
  {
2
+ "$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
2
3
  "name": "recur-skills",
4
+ "description": "Claude Code skills for integrating Recur — Taiwan's subscription payment platform",
5
+ "version": "0.0.9",
3
6
  "owner": {
4
7
  "name": "Recur",
5
- "email": "hi@recur.tw"
8
+ "email": "hi@recur.tw",
9
+ "url": "https://recur.tw"
6
10
  },
7
11
  "metadata": {
8
12
  "description": "Claude Code skills for integrating Recur - Taiwan's subscription payment platform",
9
- "version": "0.0.8"
13
+ "version": "0.0.9"
10
14
  },
11
15
  "plugins": [
12
16
  {
13
17
  "name": "recur-skills",
14
- "description": "Skills for Recur SDK integration including quickstart, checkout, webhooks, and entitlements",
18
+ "displayName": "Recur Skills",
19
+ "description": "Skills for Recur SDK integration (quickstart, checkout, webhooks, entitlements, portal) and the Recur MCP server for signup, API keys, and products",
20
+ "version": "0.0.9",
15
21
  "source": "./",
22
+ "category": "development",
23
+ "homepage": "https://docs.recur.tw/guides/skills",
24
+ "repository": "https://github.com/recur-tw/skills",
25
+ "license": "MIT",
26
+ "keywords": [
27
+ "recur",
28
+ "payments",
29
+ "taiwan",
30
+ "subscription",
31
+ "checkout",
32
+ "webhooks"
33
+ ],
34
+ "author": {
35
+ "name": "Recur",
36
+ "email": "hi@recur.tw",
37
+ "url": "https://recur.tw"
38
+ },
16
39
  "skills": [
17
40
  "./skills/recur-help",
18
41
  "./skills/recur-quickstart",
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
+ "name": "recur-skills",
4
+ "displayName": "Recur Skills",
5
+ "version": "0.0.9",
6
+ "description": "Skills for Recur SDK integration (quickstart, checkout, webhooks, entitlements, customer portal) plus the Recur MCP server (https://mcp.recur.tw/) for account signup, API keys, and products.",
7
+ "author": {
8
+ "name": "Recur",
9
+ "email": "hi@recur.tw",
10
+ "url": "https://recur.tw"
11
+ },
12
+ "homepage": "https://docs.recur.tw/guides/skills",
13
+ "repository": "https://github.com/recur-tw/skills",
14
+ "license": "MIT",
15
+ "keywords": [
16
+ "recur",
17
+ "payments",
18
+ "taiwan",
19
+ "subscription",
20
+ "checkout",
21
+ "webhooks",
22
+ "mcp"
23
+ ],
24
+ "mcpServers": "./.mcp.json"
25
+ }
package/.mcp.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "recur": {
4
+ "type": "http",
5
+ "url": "https://mcp.recur.tw/"
6
+ }
7
+ }
8
+ }
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Recur Skills
2
2
 
3
+ [![skills.sh](https://skills.sh/b/recur-tw/skills)](https://skills.sh/recur-tw/skills)
4
+
3
5
  Skills to help developers integrate [Recur](https://recur.tw) - Taiwan's subscription payment platform.
4
6
 
5
7
  Supports Claude Code, Cursor, Codex, GitHub Copilot, Gemini CLI, Antigravity, and other AI coding agents.
@@ -12,13 +14,32 @@ Supports Claude Code, Cursor, Codex, GitHub Copilot, Gemini CLI, Antigravity, an
12
14
  npx skills add recur-tw/skills
13
15
  ```
14
16
 
15
- ### Claude Code Plugin
17
+ ### Claude Code Plugin (skills + MCP server)
16
18
 
17
19
  ```bash
18
20
  /plugin marketplace add recur-tw/skills
19
21
  /plugin install recur-skills@recur-skills
20
22
  ```
21
23
 
24
+ The plugin bundles the Recur MCP server (`https://mcp.recur.tw/`, see `.mcp.json`).
25
+ After installing, run `/mcp` and authorize `recur` — no account yet? Click 註冊 on the
26
+ login page; the account is created inside the OAuth flow.
27
+
28
+ ### MCP for other agents
29
+
30
+ `npx skills add` installs skills only. Add `https://mcp.recur.tw/` as a remote MCP
31
+ server in Cursor, VS Code, Codex, Gemini CLI, or Claude Desktop — one-click links
32
+ and config snippets at https://docs.recur.tw/guides/mcp.
33
+
34
+ Or from a shell:
35
+
36
+ ```bash
37
+ claude plugin marketplace add recur-tw/skills
38
+ claude plugin install recur-skills@recur-skills
39
+ ```
40
+
41
+ Plugin skills are namespaced, e.g. `/recur-skills:recur-checkout`. See [Claude Code plugins](https://code.claude.com/docs/en/plugins).
42
+
22
43
  ## Getting Started
23
44
 
24
45
  Not sure where to begin? Ask Claude:
@@ -27,6 +48,12 @@ Not sure where to begin? Ask Claude:
27
48
  Recur 有什麼功能?
28
49
  ```
29
50
 
51
+ No Recur account yet? Ask:
52
+
53
+ ```
54
+ 幫我申請 Recur 帳號,建立每月 $499 的訂閱方案並串接付款流程
55
+ ```
56
+
30
57
  Or type `/recur-help` to see all available skills.
31
58
 
32
59
  ## Available Skills
@@ -98,6 +125,7 @@ Once installed, Claude will automatically use these skills when you're working o
98
125
 
99
126
  ## Links
100
127
 
128
+ - [skills.sh](https://skills.sh/recur-tw/skills): `npx skills add recur-tw/skills`
101
129
  - [Recur Website](https://recur.tw)
102
130
  - [Documentation](https://recur.tw/docs)
103
131
  - [SDK on npm](https://www.npmjs.com/package/recur-tw)
package/dist/cli.mjs CHANGED
@@ -1,11 +1,9 @@
1
1
  #!/usr/bin/env node
2
-
3
2
  import { program } from "commander";
4
3
  import pc from "picocolors";
5
4
  import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync } from "node:fs";
6
5
  import { dirname, join } from "node:path";
7
6
  import { fileURLToPath } from "node:url";
8
-
9
7
  //#region src/index.ts
10
8
  const SKILLS_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "skills");
11
9
  /**
@@ -73,13 +71,12 @@ function getSkill(slug) {
73
71
  function getSkillsDir() {
74
72
  return SKILLS_DIR;
75
73
  }
76
-
77
74
  //#endregion
78
75
  //#region src/cli.ts
79
76
  const __dirname = dirname(fileURLToPath(import.meta.url));
80
77
  const SKILLS_SOURCE = join(__dirname, "..", "skills");
81
- const { version } = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf8"));
82
- program.name("recur-skills").description("Claude Code skills for Recur payment integration").version(version);
78
+ const { version: PKG_VERSION } = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf8"));
79
+ program.name("recur-skills").description("Claude Code skills for Recur payment integration").version(PKG_VERSION);
83
80
  program.command("list").description("List all available skills").action(() => {
84
81
  const skills = getAllSkills();
85
82
  if (skills.length === 0) {
@@ -150,7 +147,7 @@ program.command("path").description("Show the path to the skills directory").act
150
147
  console.log(getSkillsDir());
151
148
  });
152
149
  program.parse();
153
-
154
150
  //#endregion
155
- export { };
151
+ export {};
152
+
156
153
  //# sourceMappingURL=cli.mjs.map
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":["data: Record<string, string>","skills: Skill[]","targetDir: string","toInstall: string[]"],"sources":["../src/index.ts","../src/cli.ts"],"sourcesContent":["import { readFileSync, readdirSync, existsSync } from 'node:fs'\nimport { join, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst SKILLS_DIR = join(__dirname, '..', 'skills')\n\nexport interface SkillMetadata {\n name: string\n description: string\n}\n\nexport interface Skill extends SkillMetadata {\n slug: string\n content: string\n path: string\n}\n\n/**\n * Parse SKILL.md frontmatter\n */\nfunction parseFrontmatter(content: string): { data: Record<string, string>; content: string } {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/)\n if (!match) {\n return { data: {}, content }\n }\n\n const data: Record<string, string> = {}\n const lines = match[1].split('\\n')\n for (const line of lines) {\n const colonIndex = line.indexOf(':')\n if (colonIndex > 0) {\n const key = line.slice(0, colonIndex).trim()\n const value = line.slice(colonIndex + 1).trim()\n data[key] = value\n }\n }\n\n return { data, content: match[2] }\n}\n\n/**\n * Get all available skills\n */\nexport function getAllSkills(): Skill[] {\n if (!existsSync(SKILLS_DIR)) {\n return []\n }\n\n const skillDirs = readdirSync(SKILLS_DIR, { withFileTypes: true })\n .filter(dirent => dirent.isDirectory())\n .map(dirent => dirent.name)\n\n const skills: Skill[] = []\n\n for (const dir of skillDirs) {\n const skillPath = join(SKILLS_DIR, dir, 'SKILL.md')\n if (!existsSync(skillPath)) continue\n\n const rawContent = readFileSync(skillPath, 'utf-8')\n const { data, content } = parseFrontmatter(rawContent)\n\n skills.push({\n slug: dir,\n name: data.name || dir,\n description: data.description || '',\n content,\n path: skillPath,\n })\n }\n\n return skills\n}\n\n/**\n * Get a single skill by slug\n */\nexport function getSkill(slug: string): Skill | null {\n const skillPath = join(SKILLS_DIR, slug, 'SKILL.md')\n if (!existsSync(skillPath)) return null\n\n const rawContent = readFileSync(skillPath, 'utf-8')\n const { data, content } = parseFrontmatter(rawContent)\n\n return {\n slug,\n name: data.name || slug,\n description: data.description || '',\n content,\n path: skillPath,\n }\n}\n\n/**\n * Get skill names only (for listing)\n */\nexport function getSkillNames(): string[] {\n if (!existsSync(SKILLS_DIR)) {\n return []\n }\n\n return readdirSync(SKILLS_DIR, { withFileTypes: true })\n .filter(dirent => dirent.isDirectory())\n .filter(dirent => existsSync(join(SKILLS_DIR, dirent.name, 'SKILL.md')))\n .map(dirent => dirent.name)\n}\n\n/**\n * Get the path to skills directory\n */\nexport function getSkillsDir(): string {\n return SKILLS_DIR\n}\n","import { program } from 'commander'\nimport pc from 'picocolors'\nimport { existsSync, mkdirSync, cpSync, readdirSync, readFileSync } from 'node:fs'\nimport { join, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { getAllSkills, getSkill, getSkillsDir } from './index.js'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst SKILLS_SOURCE = join(__dirname, '..', 'skills')\nconst { version } = JSON.parse(\n readFileSync(join(__dirname, '..', 'package.json'), 'utf8')\n) as { version: string }\n\nprogram\n .name('recur-skills')\n .description('Claude Code skills for Recur payment integration')\n .version(version)\n\nprogram\n .command('list')\n .description('List all available skills')\n .action(() => {\n const skills = getAllSkills()\n\n if (skills.length === 0) {\n console.log(pc.yellow('No skills found.'))\n return\n }\n\n console.log(pc.bold('\\n📦 Available Recur Skills\\n'))\n\n for (const skill of skills) {\n console.log(pc.cyan(` ${skill.name}`))\n console.log(pc.dim(` ${skill.description}\\n`))\n }\n\n console.log(pc.dim(`Total: ${skills.length} skills\\n`))\n })\n\nprogram\n .command('info <skill>')\n .description('Show detailed information about a skill')\n .action((skillName: string) => {\n const skill = getSkill(skillName)\n\n if (!skill) {\n console.log(pc.red(`Skill \"${skillName}\" not found.`))\n console.log(pc.dim('\\nRun `recur-skills list` to see available skills.'))\n process.exit(1)\n }\n\n console.log(pc.bold(`\\n📦 ${skill.name}\\n`))\n console.log(pc.dim('Description:'))\n console.log(` ${skill.description}\\n`)\n console.log(pc.dim('Path:'))\n console.log(` ${skill.path}\\n`)\n })\n\nprogram\n .command('install [skills...]')\n .description('Install skills to your Claude Code skills directory')\n .option('-g, --global', 'Install to global ~/.claude/skills/', false)\n .option('-p, --project', 'Install to project .claude/skills/', false)\n .option('-a, --all', 'Install all skills', false)\n .action((skillNames: string[], options: { global: boolean; project: boolean; all: boolean }) => {\n // Determine target directory\n let targetDir: string\n if (options.global) {\n targetDir = join(process.env.HOME || '~', '.claude', 'skills')\n } else if (options.project) {\n targetDir = join(process.cwd(), '.claude', 'skills')\n } else {\n // Default to global\n targetDir = join(process.env.HOME || '~', '.claude', 'skills')\n }\n\n // Determine which skills to install\n let toInstall: string[]\n if (options.all) {\n toInstall = readdirSync(SKILLS_SOURCE, { withFileTypes: true })\n .filter(d => d.isDirectory())\n .map(d => d.name)\n } else if (skillNames.length === 0) {\n console.log(pc.yellow('Please specify skills to install or use --all'))\n console.log(pc.dim('\\nExamples:'))\n console.log(pc.dim(' recur-skills install recur-quickstart'))\n console.log(pc.dim(' recur-skills install recur-checkout recur-webhooks'))\n console.log(pc.dim(' recur-skills install --all'))\n process.exit(1)\n } else {\n toInstall = skillNames\n }\n\n // Create target directory\n if (!existsSync(targetDir)) {\n mkdirSync(targetDir, { recursive: true })\n }\n\n console.log(pc.bold(`\\n📦 Installing skills to ${targetDir}\\n`))\n\n let installed = 0\n for (const skillName of toInstall) {\n const sourcePath = join(SKILLS_SOURCE, skillName)\n const destPath = join(targetDir, skillName)\n\n if (!existsSync(sourcePath)) {\n console.log(pc.red(` ✗ ${skillName} - not found`))\n continue\n }\n\n try {\n cpSync(sourcePath, destPath, { recursive: true })\n console.log(pc.green(` ✓ ${skillName}`))\n installed++\n } catch (error) {\n console.log(pc.red(` ✗ ${skillName} - ${error}`))\n }\n }\n\n console.log(pc.dim(`\\nInstalled ${installed}/${toInstall.length} skills\\n`))\n\n if (installed > 0) {\n console.log(pc.cyan('Skills are now available in Claude Code!'))\n console.log(pc.dim('Claude will automatically use them when relevant,'))\n console.log(pc.dim('or you can invoke them directly with /skill-name\\n'))\n }\n })\n\nprogram\n .command('path')\n .description('Show the path to the skills directory')\n .action(() => {\n console.log(getSkillsDir())\n })\n\nprogram.parse()\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../src/index.ts","../src/cli.ts"],"sourcesContent":["import { readFileSync, readdirSync, existsSync } from 'node:fs'\nimport { join, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst SKILLS_DIR = join(__dirname, '..', 'skills')\n\nexport interface SkillMetadata {\n name: string\n description: string\n}\n\nexport interface Skill extends SkillMetadata {\n slug: string\n content: string\n path: string\n}\n\n/**\n * Parse SKILL.md frontmatter\n */\nfunction parseFrontmatter(content: string): { data: Record<string, string>; content: string } {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/)\n if (!match) {\n return { data: {}, content }\n }\n\n const data: Record<string, string> = {}\n const lines = match[1].split('\\n')\n for (const line of lines) {\n const colonIndex = line.indexOf(':')\n if (colonIndex > 0) {\n const key = line.slice(0, colonIndex).trim()\n const value = line.slice(colonIndex + 1).trim()\n data[key] = value\n }\n }\n\n return { data, content: match[2] }\n}\n\n/**\n * Get all available skills\n */\nexport function getAllSkills(): Skill[] {\n if (!existsSync(SKILLS_DIR)) {\n return []\n }\n\n const skillDirs = readdirSync(SKILLS_DIR, { withFileTypes: true })\n .filter(dirent => dirent.isDirectory())\n .map(dirent => dirent.name)\n\n const skills: Skill[] = []\n\n for (const dir of skillDirs) {\n const skillPath = join(SKILLS_DIR, dir, 'SKILL.md')\n if (!existsSync(skillPath)) continue\n\n const rawContent = readFileSync(skillPath, 'utf-8')\n const { data, content } = parseFrontmatter(rawContent)\n\n skills.push({\n slug: dir,\n name: data.name || dir,\n description: data.description || '',\n content,\n path: skillPath,\n })\n }\n\n return skills\n}\n\n/**\n * Get a single skill by slug\n */\nexport function getSkill(slug: string): Skill | null {\n const skillPath = join(SKILLS_DIR, slug, 'SKILL.md')\n if (!existsSync(skillPath)) return null\n\n const rawContent = readFileSync(skillPath, 'utf-8')\n const { data, content } = parseFrontmatter(rawContent)\n\n return {\n slug,\n name: data.name || slug,\n description: data.description || '',\n content,\n path: skillPath,\n }\n}\n\n/**\n * Get skill names only (for listing)\n */\nexport function getSkillNames(): string[] {\n if (!existsSync(SKILLS_DIR)) {\n return []\n }\n\n return readdirSync(SKILLS_DIR, { withFileTypes: true })\n .filter(dirent => dirent.isDirectory())\n .filter(dirent => existsSync(join(SKILLS_DIR, dirent.name, 'SKILL.md')))\n .map(dirent => dirent.name)\n}\n\n/**\n * Get the path to skills directory\n */\nexport function getSkillsDir(): string {\n return SKILLS_DIR\n}\n","import { program } from 'commander'\nimport pc from 'picocolors'\nimport { existsSync, mkdirSync, cpSync, readdirSync, readFileSync } from 'node:fs'\nimport { join, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { getAllSkills, getSkill, getSkillsDir } from './index.js'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst SKILLS_SOURCE = join(__dirname, '..', 'skills')\nconst { version: PKG_VERSION } = JSON.parse(\n readFileSync(join(__dirname, '..', 'package.json'), 'utf8'),\n) as { version: string }\n\nprogram\n .name('recur-skills')\n .description('Claude Code skills for Recur payment integration')\n .version(PKG_VERSION)\n\nprogram\n .command('list')\n .description('List all available skills')\n .action(() => {\n const skills = getAllSkills()\n\n if (skills.length === 0) {\n console.log(pc.yellow('No skills found.'))\n return\n }\n\n console.log(pc.bold('\\n📦 Available Recur Skills\\n'))\n\n for (const skill of skills) {\n console.log(pc.cyan(` ${skill.name}`))\n console.log(pc.dim(` ${skill.description}\\n`))\n }\n\n console.log(pc.dim(`Total: ${skills.length} skills\\n`))\n })\n\nprogram\n .command('info <skill>')\n .description('Show detailed information about a skill')\n .action((skillName: string) => {\n const skill = getSkill(skillName)\n\n if (!skill) {\n console.log(pc.red(`Skill \"${skillName}\" not found.`))\n console.log(pc.dim('\\nRun `recur-skills list` to see available skills.'))\n process.exit(1)\n }\n\n console.log(pc.bold(`\\n📦 ${skill.name}\\n`))\n console.log(pc.dim('Description:'))\n console.log(` ${skill.description}\\n`)\n console.log(pc.dim('Path:'))\n console.log(` ${skill.path}\\n`)\n })\n\nprogram\n .command('install [skills...]')\n .description('Install skills to your Claude Code skills directory')\n .option('-g, --global', 'Install to global ~/.claude/skills/', false)\n .option('-p, --project', 'Install to project .claude/skills/', false)\n .option('-a, --all', 'Install all skills', false)\n .action((skillNames: string[], options: { global: boolean; project: boolean; all: boolean }) => {\n // Determine target directory\n let targetDir: string\n if (options.global) {\n targetDir = join(process.env.HOME || '~', '.claude', 'skills')\n } else if (options.project) {\n targetDir = join(process.cwd(), '.claude', 'skills')\n } else {\n // Default to global\n targetDir = join(process.env.HOME || '~', '.claude', 'skills')\n }\n\n // Determine which skills to install\n let toInstall: string[]\n if (options.all) {\n toInstall = readdirSync(SKILLS_SOURCE, { withFileTypes: true })\n .filter(d => d.isDirectory())\n .map(d => d.name)\n } else if (skillNames.length === 0) {\n console.log(pc.yellow('Please specify skills to install or use --all'))\n console.log(pc.dim('\\nExamples:'))\n console.log(pc.dim(' recur-skills install recur-quickstart'))\n console.log(pc.dim(' recur-skills install recur-checkout recur-webhooks'))\n console.log(pc.dim(' recur-skills install --all'))\n process.exit(1)\n } else {\n toInstall = skillNames\n }\n\n // Create target directory\n if (!existsSync(targetDir)) {\n mkdirSync(targetDir, { recursive: true })\n }\n\n console.log(pc.bold(`\\n📦 Installing skills to ${targetDir}\\n`))\n\n let installed = 0\n for (const skillName of toInstall) {\n const sourcePath = join(SKILLS_SOURCE, skillName)\n const destPath = join(targetDir, skillName)\n\n if (!existsSync(sourcePath)) {\n console.log(pc.red(` ✗ ${skillName} - not found`))\n continue\n }\n\n try {\n cpSync(sourcePath, destPath, { recursive: true })\n console.log(pc.green(` ✓ ${skillName}`))\n installed++\n } catch (error) {\n console.log(pc.red(` ✗ ${skillName} - ${error}`))\n }\n }\n\n console.log(pc.dim(`\\nInstalled ${installed}/${toInstall.length} skills\\n`))\n\n if (installed > 0) {\n console.log(pc.cyan('Skills are now available in Claude Code!'))\n console.log(pc.dim('Claude will automatically use them when relevant,'))\n console.log(pc.dim('or you can invoke them directly with /skill-name\\n'))\n }\n })\n\nprogram\n .command('path')\n .description('Show the path to the skills directory')\n .action(() => {\n console.log(getSkillsDir())\n })\n\nprogram.parse()\n"],"mappings":";;;;;;;AAKA,MAAM,aAAa,KADD,QAAQ,cAAc,OAAO,KAAK,GAAG,CACvB,GAAG,MAAM,QAAQ;;;;AAgBjD,SAAS,iBAAiB,SAAoE;CAC5F,MAAM,QAAQ,QAAQ,MAAM,mCAAmC;CAC/D,IAAI,CAAC,OACH,OAAO;EAAE,MAAM,CAAC;EAAG;CAAQ;CAG7B,MAAM,OAA+B,CAAC;CACtC,MAAM,QAAQ,MAAM,EAAE,CAAC,MAAM,IAAI;CACjC,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,aAAa,KAAK,QAAQ,GAAG;EACnC,IAAI,aAAa,GAAG;GAClB,MAAM,MAAM,KAAK,MAAM,GAAG,UAAU,CAAC,CAAC,KAAK;GAE3C,KAAK,OADS,KAAK,MAAM,aAAa,CAAC,CAAC,CAAC,KACzB;EAClB;CACF;CAEA,OAAO;EAAE;EAAM,SAAS,MAAM;CAAG;AACnC;;;;AAKA,SAAgB,eAAwB;CACtC,IAAI,CAAC,WAAW,UAAU,GACxB,OAAO,CAAC;CAGV,MAAM,YAAY,YAAY,YAAY,EAAE,eAAe,KAAK,CAAC,CAAC,CAC/D,QAAO,WAAU,OAAO,YAAY,CAAC,CAAC,CACtC,KAAI,WAAU,OAAO,IAAI;CAE5B,MAAM,SAAkB,CAAC;CAEzB,KAAK,MAAM,OAAO,WAAW;EAC3B,MAAM,YAAY,KAAK,YAAY,KAAK,UAAU;EAClD,IAAI,CAAC,WAAW,SAAS,GAAG;EAG5B,MAAM,EAAE,MAAM,YAAY,iBADP,aAAa,WAAW,OACS,CAAC;EAErD,OAAO,KAAK;GACV,MAAM;GACN,MAAM,KAAK,QAAQ;GACnB,aAAa,KAAK,eAAe;GACjC;GACA,MAAM;EACR,CAAC;CACH;CAEA,OAAO;AACT;;;;AAKA,SAAgB,SAAS,MAA4B;CACnD,MAAM,YAAY,KAAK,YAAY,MAAM,UAAU;CACnD,IAAI,CAAC,WAAW,SAAS,GAAG,OAAO;CAGnC,MAAM,EAAE,MAAM,YAAY,iBADP,aAAa,WAAW,OACS,CAAC;CAErD,OAAO;EACL;EACA,MAAM,KAAK,QAAQ;EACnB,aAAa,KAAK,eAAe;EACjC;EACA,MAAM;CACR;AACF;;;;AAmBA,SAAgB,eAAuB;CACrC,OAAO;AACT;;;ACzGA,MAAM,YAAY,QAAQ,cAAc,OAAO,KAAK,GAAG,CAAC;AACxD,MAAM,gBAAgB,KAAK,WAAW,MAAM,QAAQ;AACpD,MAAM,EAAE,SAAS,gBAAgB,KAAK,MACpC,aAAa,KAAK,WAAW,MAAM,cAAc,GAAG,MAAM,CAC5D;AAEA,QACG,KAAK,cAAc,CAAC,CACpB,YAAY,kDAAkD,CAAC,CAC/D,QAAQ,WAAW;AAEtB,QACG,QAAQ,MAAM,CAAC,CACf,YAAY,2BAA2B,CAAC,CACxC,aAAa;CACZ,MAAM,SAAS,aAAa;CAE5B,IAAI,OAAO,WAAW,GAAG;EACvB,QAAQ,IAAI,GAAG,OAAO,kBAAkB,CAAC;EACzC;CACF;CAEA,QAAQ,IAAI,GAAG,KAAK,+BAA+B,CAAC;CAEpD,KAAK,MAAM,SAAS,QAAQ;EAC1B,QAAQ,IAAI,GAAG,KAAK,KAAK,MAAM,MAAM,CAAC;EACtC,QAAQ,IAAI,GAAG,IAAI,QAAQ,MAAM,YAAY,GAAG,CAAC;CACnD;CAEA,QAAQ,IAAI,GAAG,IAAI,UAAU,OAAO,OAAO,UAAU,CAAC;AACxD,CAAC;AAEH,QACG,QAAQ,cAAc,CAAC,CACvB,YAAY,yCAAyC,CAAC,CACtD,QAAQ,cAAsB;CAC7B,MAAM,QAAQ,SAAS,SAAS;CAEhC,IAAI,CAAC,OAAO;EACV,QAAQ,IAAI,GAAG,IAAI,UAAU,UAAU,aAAa,CAAC;EACrD,QAAQ,IAAI,GAAG,IAAI,oDAAoD,CAAC;EACxE,QAAQ,KAAK,CAAC;CAChB;CAEA,QAAQ,IAAI,GAAG,KAAK,QAAQ,MAAM,KAAK,GAAG,CAAC;CAC3C,QAAQ,IAAI,GAAG,IAAI,cAAc,CAAC;CAClC,QAAQ,IAAI,KAAK,MAAM,YAAY,GAAG;CACtC,QAAQ,IAAI,GAAG,IAAI,OAAO,CAAC;CAC3B,QAAQ,IAAI,KAAK,MAAM,KAAK,GAAG;AACjC,CAAC;AAEH,QACG,QAAQ,qBAAqB,CAAC,CAC9B,YAAY,qDAAqD,CAAC,CAClE,OAAO,gBAAgB,uCAAuC,KAAK,CAAC,CACpE,OAAO,iBAAiB,sCAAsC,KAAK,CAAC,CACpE,OAAO,aAAa,sBAAsB,KAAK,CAAC,CAChD,QAAQ,YAAsB,YAAiE;CAE9F,IAAI;CACJ,IAAI,QAAQ,QACV,YAAY,KAAK,QAAQ,IAAI,QAAQ,KAAK,WAAW,QAAQ;MACxD,IAAI,QAAQ,SACjB,YAAY,KAAK,QAAQ,IAAI,GAAG,WAAW,QAAQ;MAGnD,YAAY,KAAK,QAAQ,IAAI,QAAQ,KAAK,WAAW,QAAQ;CAI/D,IAAI;CACJ,IAAI,QAAQ,KACV,YAAY,YAAY,eAAe,EAAE,eAAe,KAAK,CAAC,CAAC,CAC5D,QAAO,MAAK,EAAE,YAAY,CAAC,CAAC,CAC5B,KAAI,MAAK,EAAE,IAAI;MACb,IAAI,WAAW,WAAW,GAAG;EAClC,QAAQ,IAAI,GAAG,OAAO,+CAA+C,CAAC;EACtE,QAAQ,IAAI,GAAG,IAAI,aAAa,CAAC;EACjC,QAAQ,IAAI,GAAG,IAAI,yCAAyC,CAAC;EAC7D,QAAQ,IAAI,GAAG,IAAI,sDAAsD,CAAC;EAC1E,QAAQ,IAAI,GAAG,IAAI,8BAA8B,CAAC;EAClD,QAAQ,KAAK,CAAC;CAChB,OACE,YAAY;CAId,IAAI,CAAC,WAAW,SAAS,GACvB,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;CAG1C,QAAQ,IAAI,GAAG,KAAK,6BAA6B,UAAU,GAAG,CAAC;CAE/D,IAAI,YAAY;CAChB,KAAK,MAAM,aAAa,WAAW;EACjC,MAAM,aAAa,KAAK,eAAe,SAAS;EAChD,MAAM,WAAW,KAAK,WAAW,SAAS;EAE1C,IAAI,CAAC,WAAW,UAAU,GAAG;GAC3B,QAAQ,IAAI,GAAG,IAAI,OAAO,UAAU,aAAa,CAAC;GAClD;EACF;EAEA,IAAI;GACF,OAAO,YAAY,UAAU,EAAE,WAAW,KAAK,CAAC;GAChD,QAAQ,IAAI,GAAG,MAAM,OAAO,WAAW,CAAC;GACxC;EACF,SAAS,OAAO;GACd,QAAQ,IAAI,GAAG,IAAI,OAAO,UAAU,KAAK,OAAO,CAAC;EACnD;CACF;CAEA,QAAQ,IAAI,GAAG,IAAI,eAAe,UAAU,GAAG,UAAU,OAAO,UAAU,CAAC;CAE3E,IAAI,YAAY,GAAG;EACjB,QAAQ,IAAI,GAAG,KAAK,0CAA0C,CAAC;EAC/D,QAAQ,IAAI,GAAG,IAAI,mDAAmD,CAAC;EACvE,QAAQ,IAAI,GAAG,IAAI,oDAAoD,CAAC;CAC1E;AACF,CAAC;AAEH,QACG,QAAQ,MAAM,CAAC,CACf,YAAY,uCAAuC,CAAC,CACpD,aAAa;CACZ,QAAQ,IAAI,aAAa,CAAC;AAC5B,CAAC;AAEH,QAAQ,MAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";UAOiB,aAAA;EAAA,IAAA,EAAA,MAAA;EAKA,WAAM,EAAA,MAAQ;AAgC/B;AAiCgB,UAjEC,KAAA,SAAc,aAiEc,CAAA;EAmB7B,IAAA,EAAA,MAAA;EAcA,OAAA,EAAA,MAAY;;;;;;iBAlEZ,YAAA,CAAA,GAAgB;;;;iBAiChB,QAAA,gBAAwB;;;;iBAmBxB,aAAA,CAAA;;;;iBAcA,YAAA,CAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";UAOiB;EACf;EACA;;UAGe,cAAc;EAC7B;EACA;EACA;;;;;iBA6Bc,gBAAgB;;;;iBAiChB,SAAS,eAAe;;;;iBAmBxB;;;;iBAcA"}
package/dist/index.mjs CHANGED
@@ -1,7 +1,6 @@
1
1
  import { existsSync, readFileSync, readdirSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
-
5
4
  //#region src/index.ts
6
5
  const SKILLS_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "skills");
7
6
  /**
@@ -76,7 +75,7 @@ function getSkillNames() {
76
75
  function getSkillsDir() {
77
76
  return SKILLS_DIR;
78
77
  }
79
-
80
78
  //#endregion
81
79
  export { getAllSkills, getSkill, getSkillNames, getSkillsDir };
80
+
82
81
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["data: Record<string, string>","skills: Skill[]"],"sources":["../src/index.ts"],"sourcesContent":["import { readFileSync, readdirSync, existsSync } from 'node:fs'\nimport { join, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst SKILLS_DIR = join(__dirname, '..', 'skills')\n\nexport interface SkillMetadata {\n name: string\n description: string\n}\n\nexport interface Skill extends SkillMetadata {\n slug: string\n content: string\n path: string\n}\n\n/**\n * Parse SKILL.md frontmatter\n */\nfunction parseFrontmatter(content: string): { data: Record<string, string>; content: string } {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/)\n if (!match) {\n return { data: {}, content }\n }\n\n const data: Record<string, string> = {}\n const lines = match[1].split('\\n')\n for (const line of lines) {\n const colonIndex = line.indexOf(':')\n if (colonIndex > 0) {\n const key = line.slice(0, colonIndex).trim()\n const value = line.slice(colonIndex + 1).trim()\n data[key] = value\n }\n }\n\n return { data, content: match[2] }\n}\n\n/**\n * Get all available skills\n */\nexport function getAllSkills(): Skill[] {\n if (!existsSync(SKILLS_DIR)) {\n return []\n }\n\n const skillDirs = readdirSync(SKILLS_DIR, { withFileTypes: true })\n .filter(dirent => dirent.isDirectory())\n .map(dirent => dirent.name)\n\n const skills: Skill[] = []\n\n for (const dir of skillDirs) {\n const skillPath = join(SKILLS_DIR, dir, 'SKILL.md')\n if (!existsSync(skillPath)) continue\n\n const rawContent = readFileSync(skillPath, 'utf-8')\n const { data, content } = parseFrontmatter(rawContent)\n\n skills.push({\n slug: dir,\n name: data.name || dir,\n description: data.description || '',\n content,\n path: skillPath,\n })\n }\n\n return skills\n}\n\n/**\n * Get a single skill by slug\n */\nexport function getSkill(slug: string): Skill | null {\n const skillPath = join(SKILLS_DIR, slug, 'SKILL.md')\n if (!existsSync(skillPath)) return null\n\n const rawContent = readFileSync(skillPath, 'utf-8')\n const { data, content } = parseFrontmatter(rawContent)\n\n return {\n slug,\n name: data.name || slug,\n description: data.description || '',\n content,\n path: skillPath,\n }\n}\n\n/**\n * Get skill names only (for listing)\n */\nexport function getSkillNames(): string[] {\n if (!existsSync(SKILLS_DIR)) {\n return []\n }\n\n return readdirSync(SKILLS_DIR, { withFileTypes: true })\n .filter(dirent => dirent.isDirectory())\n .filter(dirent => existsSync(join(SKILLS_DIR, dirent.name, 'SKILL.md')))\n .map(dirent => dirent.name)\n}\n\n/**\n * Get the path to skills directory\n */\nexport function getSkillsDir(): string {\n return SKILLS_DIR\n}\n"],"mappings":";;;;;AAKA,MAAM,aAAa,KADD,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC,EACtB,MAAM,SAAS;;;;AAgBlD,SAAS,iBAAiB,SAAoE;CAC5F,MAAM,QAAQ,QAAQ,MAAM,oCAAoC;AAChE,KAAI,CAAC,MACH,QAAO;EAAE,MAAM,EAAE;EAAE;EAAS;CAG9B,MAAMA,OAA+B,EAAE;CACvC,MAAM,QAAQ,MAAM,GAAG,MAAM,KAAK;AAClC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,aAAa,KAAK,QAAQ,IAAI;AACpC,MAAI,aAAa,GAAG;GAClB,MAAM,MAAM,KAAK,MAAM,GAAG,WAAW,CAAC,MAAM;AAE5C,QAAK,OADS,KAAK,MAAM,aAAa,EAAE,CAAC,MAAM;;;AAKnD,QAAO;EAAE;EAAM,SAAS,MAAM;EAAI;;;;;AAMpC,SAAgB,eAAwB;AACtC,KAAI,CAAC,WAAW,WAAW,CACzB,QAAO,EAAE;CAGX,MAAM,YAAY,YAAY,YAAY,EAAE,eAAe,MAAM,CAAC,CAC/D,QAAO,WAAU,OAAO,aAAa,CAAC,CACtC,KAAI,WAAU,OAAO,KAAK;CAE7B,MAAMC,SAAkB,EAAE;AAE1B,MAAK,MAAM,OAAO,WAAW;EAC3B,MAAM,YAAY,KAAK,YAAY,KAAK,WAAW;AACnD,MAAI,CAAC,WAAW,UAAU,CAAE;EAG5B,MAAM,EAAE,MAAM,YAAY,iBADP,aAAa,WAAW,QAAQ,CACG;AAEtD,SAAO,KAAK;GACV,MAAM;GACN,MAAM,KAAK,QAAQ;GACnB,aAAa,KAAK,eAAe;GACjC;GACA,MAAM;GACP,CAAC;;AAGJ,QAAO;;;;;AAMT,SAAgB,SAAS,MAA4B;CACnD,MAAM,YAAY,KAAK,YAAY,MAAM,WAAW;AACpD,KAAI,CAAC,WAAW,UAAU,CAAE,QAAO;CAGnC,MAAM,EAAE,MAAM,YAAY,iBADP,aAAa,WAAW,QAAQ,CACG;AAEtD,QAAO;EACL;EACA,MAAM,KAAK,QAAQ;EACnB,aAAa,KAAK,eAAe;EACjC;EACA,MAAM;EACP;;;;;AAMH,SAAgB,gBAA0B;AACxC,KAAI,CAAC,WAAW,WAAW,CACzB,QAAO,EAAE;AAGX,QAAO,YAAY,YAAY,EAAE,eAAe,MAAM,CAAC,CACpD,QAAO,WAAU,OAAO,aAAa,CAAC,CACtC,QAAO,WAAU,WAAW,KAAK,YAAY,OAAO,MAAM,WAAW,CAAC,CAAC,CACvE,KAAI,WAAU,OAAO,KAAK;;;;;AAM/B,SAAgB,eAAuB;AACrC,QAAO"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { readFileSync, readdirSync, existsSync } from 'node:fs'\nimport { join, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst SKILLS_DIR = join(__dirname, '..', 'skills')\n\nexport interface SkillMetadata {\n name: string\n description: string\n}\n\nexport interface Skill extends SkillMetadata {\n slug: string\n content: string\n path: string\n}\n\n/**\n * Parse SKILL.md frontmatter\n */\nfunction parseFrontmatter(content: string): { data: Record<string, string>; content: string } {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/)\n if (!match) {\n return { data: {}, content }\n }\n\n const data: Record<string, string> = {}\n const lines = match[1].split('\\n')\n for (const line of lines) {\n const colonIndex = line.indexOf(':')\n if (colonIndex > 0) {\n const key = line.slice(0, colonIndex).trim()\n const value = line.slice(colonIndex + 1).trim()\n data[key] = value\n }\n }\n\n return { data, content: match[2] }\n}\n\n/**\n * Get all available skills\n */\nexport function getAllSkills(): Skill[] {\n if (!existsSync(SKILLS_DIR)) {\n return []\n }\n\n const skillDirs = readdirSync(SKILLS_DIR, { withFileTypes: true })\n .filter(dirent => dirent.isDirectory())\n .map(dirent => dirent.name)\n\n const skills: Skill[] = []\n\n for (const dir of skillDirs) {\n const skillPath = join(SKILLS_DIR, dir, 'SKILL.md')\n if (!existsSync(skillPath)) continue\n\n const rawContent = readFileSync(skillPath, 'utf-8')\n const { data, content } = parseFrontmatter(rawContent)\n\n skills.push({\n slug: dir,\n name: data.name || dir,\n description: data.description || '',\n content,\n path: skillPath,\n })\n }\n\n return skills\n}\n\n/**\n * Get a single skill by slug\n */\nexport function getSkill(slug: string): Skill | null {\n const skillPath = join(SKILLS_DIR, slug, 'SKILL.md')\n if (!existsSync(skillPath)) return null\n\n const rawContent = readFileSync(skillPath, 'utf-8')\n const { data, content } = parseFrontmatter(rawContent)\n\n return {\n slug,\n name: data.name || slug,\n description: data.description || '',\n content,\n path: skillPath,\n }\n}\n\n/**\n * Get skill names only (for listing)\n */\nexport function getSkillNames(): string[] {\n if (!existsSync(SKILLS_DIR)) {\n return []\n }\n\n return readdirSync(SKILLS_DIR, { withFileTypes: true })\n .filter(dirent => dirent.isDirectory())\n .filter(dirent => existsSync(join(SKILLS_DIR, dirent.name, 'SKILL.md')))\n .map(dirent => dirent.name)\n}\n\n/**\n * Get the path to skills directory\n */\nexport function getSkillsDir(): string {\n return SKILLS_DIR\n}\n"],"mappings":";;;;AAKA,MAAM,aAAa,KADD,QAAQ,cAAc,OAAO,KAAK,GAAG,CACvB,GAAG,MAAM,QAAQ;;;;AAgBjD,SAAS,iBAAiB,SAAoE;CAC5F,MAAM,QAAQ,QAAQ,MAAM,mCAAmC;CAC/D,IAAI,CAAC,OACH,OAAO;EAAE,MAAM,CAAC;EAAG;CAAQ;CAG7B,MAAM,OAA+B,CAAC;CACtC,MAAM,QAAQ,MAAM,EAAE,CAAC,MAAM,IAAI;CACjC,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,aAAa,KAAK,QAAQ,GAAG;EACnC,IAAI,aAAa,GAAG;GAClB,MAAM,MAAM,KAAK,MAAM,GAAG,UAAU,CAAC,CAAC,KAAK;GAE3C,KAAK,OADS,KAAK,MAAM,aAAa,CAAC,CAAC,CAAC,KACzB;EAClB;CACF;CAEA,OAAO;EAAE;EAAM,SAAS,MAAM;CAAG;AACnC;;;;AAKA,SAAgB,eAAwB;CACtC,IAAI,CAAC,WAAW,UAAU,GACxB,OAAO,CAAC;CAGV,MAAM,YAAY,YAAY,YAAY,EAAE,eAAe,KAAK,CAAC,CAAC,CAC/D,QAAO,WAAU,OAAO,YAAY,CAAC,CAAC,CACtC,KAAI,WAAU,OAAO,IAAI;CAE5B,MAAM,SAAkB,CAAC;CAEzB,KAAK,MAAM,OAAO,WAAW;EAC3B,MAAM,YAAY,KAAK,YAAY,KAAK,UAAU;EAClD,IAAI,CAAC,WAAW,SAAS,GAAG;EAG5B,MAAM,EAAE,MAAM,YAAY,iBADP,aAAa,WAAW,OACS,CAAC;EAErD,OAAO,KAAK;GACV,MAAM;GACN,MAAM,KAAK,QAAQ;GACnB,aAAa,KAAK,eAAe;GACjC;GACA,MAAM;EACR,CAAC;CACH;CAEA,OAAO;AACT;;;;AAKA,SAAgB,SAAS,MAA4B;CACnD,MAAM,YAAY,KAAK,YAAY,MAAM,UAAU;CACnD,IAAI,CAAC,WAAW,SAAS,GAAG,OAAO;CAGnC,MAAM,EAAE,MAAM,YAAY,iBADP,aAAa,WAAW,OACS,CAAC;CAErD,OAAO;EACL;EACA,MAAM,KAAK,QAAQ;EACnB,aAAa,KAAK,eAAe;EACjC;EACA,MAAM;CACR;AACF;;;;AAKA,SAAgB,gBAA0B;CACxC,IAAI,CAAC,WAAW,UAAU,GACxB,OAAO,CAAC;CAGV,OAAO,YAAY,YAAY,EAAE,eAAe,KAAK,CAAC,CAAC,CACpD,QAAO,WAAU,OAAO,YAAY,CAAC,CAAC,CACtC,QAAO,WAAU,WAAW,KAAK,YAAY,OAAO,MAAM,UAAU,CAAC,CAAC,CAAC,CACvE,KAAI,WAAU,OAAO,IAAI;AAC9B;;;;AAKA,SAAgB,eAAuB;CACrC,OAAO;AACT"}
package/mcp.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
3
+ "mcpServers": {
4
+ "recur": {
5
+ "type": "streamable-http",
6
+ "url": "https://mcp.recur.tw/"
7
+ }
8
+ }
9
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "recur-skills",
3
- "version": "0.0.8",
4
- "description": "Claude Code skills for Recur - Taiwan's subscription payment platform",
3
+ "version": "0.0.9",
4
+ "description": "Claude Code skills and MCP plugin for Recur - Taiwan's subscription payment platform",
5
5
  "keywords": [
6
6
  "recur",
7
7
  "claude-code",
@@ -38,38 +38,31 @@
38
38
  "files": [
39
39
  "dist",
40
40
  "skills",
41
- ".claude-plugin"
41
+ ".claude-plugin",
42
+ "plugin.json",
43
+ "mcp.json",
44
+ ".mcp.json"
42
45
  ],
43
- "scripts": {
44
- "build": "tsdown",
45
- "dev": "tsdown --watch",
46
- "lint": "eslint src/",
47
- "typecheck": "tsc --noEmit",
48
- "check:examples": "node scripts/typecheck-examples.mjs",
49
- "prepublishOnly": "pnpm build",
50
- "version": "node scripts/sync-version.js && git add .claude-plugin/marketplace.json skills/*/SKILL.md"
51
- },
52
46
  "dependencies": {
53
47
  "commander": "^12.1.0",
54
48
  "picocolors": "^1.1.1"
55
49
  },
56
50
  "devDependencies": {
57
- "@types/express": "^5.0.6",
58
51
  "@types/node": "^22.0.0",
59
- "@types/react": "^19.2.17",
60
- "@types/react-dom": "^19.2.3",
61
- "express": "^5.2.1",
62
- "next": "^16.2.10",
63
- "react": "^19.2.7",
64
- "react-dom": "^19.2.7",
65
- "recur-tw": "^0.16.1",
66
- "tsdown": "^0.18.4",
67
- "typescript": "^5.7.3"
52
+ "tsdown": "^0.22.12",
53
+ "typescript": "^6.0.3"
68
54
  },
69
55
  "engines": {
70
56
  "node": ">=18"
71
57
  },
72
58
  "publishConfig": {
73
59
  "access": "public"
60
+ },
61
+ "scripts": {
62
+ "build": "tsdown",
63
+ "dev": "tsdown --watch",
64
+ "lint": "eslint src/",
65
+ "typecheck": "tsc --noEmit",
66
+ "version": "node scripts/sync-version.js && git add .claude-plugin/marketplace.json .claude-plugin/plugin.json plugin.json skills/*/SKILL.md"
74
67
  }
75
- }
68
+ }
package/plugin.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
+ "name": "recur-skills",
4
+ "version": "0.0.9",
5
+ "description": "Skills for Recur SDK integration: quickstart, checkout, webhooks, entitlements, and customer portal. Taiwan subscription billing via PAYUNi.",
6
+ "author": {
7
+ "name": "Recur",
8
+ "email": "hi@recur.tw",
9
+ "url": "https://recur.tw"
10
+ },
11
+ "homepage": "https://docs.recur.tw/guides/skills",
12
+ "repository": "https://github.com/recur-tw/skills",
13
+ "license": "MIT",
14
+ "keywords": [
15
+ "recur",
16
+ "payments",
17
+ "taiwan",
18
+ "subscription",
19
+ "checkout",
20
+ "webhooks",
21
+ "payuni"
22
+ ]
23
+ }