[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M72ZMS3ZRQY9PCRPRVWY040M":3,"skill-same:01M72ZMS3ZRQY9PCRPRVWY040M":44},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":18,"linkOnly":19,"markdownContent":20,"markdownBody":21,"frontMatter":22,"refName":23,"commitSha":24,"skillDirectory":25,"forksCount":26,"repositoryHtmlUrl":27,"repositoryHomepageUrl":6,"ownerHtmlUrl":28,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":29,"install":30,"resources":43},"01M72ZMS3ZRQY9PCRPRVWY040M","pinme",null,"Use this skill when the user mentions \"pinme\", or needs to upload files, store to IPFS, create\u002Fpublish\u002Fdeploy websites or full-stack services (including frontend pages, backend APIs, database storage, email sending, etc.), or any feature requiring backend database\u002Fserver support.","developAgent\u002Fbmad-start","developAgent","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F264530239?v=4","01M71J8X6CY24V5ZZHFGETRMSW","01M71J8X739FS28DGQAAY7V4QR",1,"MIT","https:\u002F\u002Fgithub.com\u002FdevelopAgent\u002Fbmad-start\u002Fblob\u002Fmain\u002Fskills\u002Fpinme\u002FSKILL.md",[],[],"2026-11-11 15:11:14",false,"---\nname: pinme\ndescription: Use this skill when the user mentions \"pinme\", or needs to upload files, store to IPFS, create\u002Fpublish\u002Fdeploy websites or full-stack services (including frontend pages, backend APIs, database storage, email sending, etc.), or any feature requiring backend database\u002Fserver support.\n---\n\n# PinMe\n\nZero-config deployment tool: upload static files to IPFS, or create and deploy full-stack web projects (React+Vite + Cloudflare Worker + D1 database). Workers also support sending emails via the PinMe platform API.\n\n## When to Use\n\n```dot\ndigraph pinme_decision {\n    \"User Request\" [shape=doublecircle];\n    \"Needs backend API or database?\" [shape=diamond];\n    \"Upload Files (Path 1)\" [shape=box];\n    \"Full-Stack Project (Path 2)\" [shape=box];\n\n    \"User Request\" -> \"Needs backend API or database?\";\n    \"Needs backend API or database?\" -> \"Upload Files (Path 1)\" [label=\"No\"];\n    \"Needs backend API or database?\" -> \"Full-Stack Project (Path 2)\" [label=\"Yes\"];\n}\n```\n\n## Path 1: Upload Files \u002F Static Sites\n\n> Login required. Use `pinme login` or `pinme set-appkey \u003CAppKey>` before `pinme upload` or `pinme import`.\n\n```dot\ndigraph upload_flow {\n    \"Install\u002Fupdate pinme to latest\" [shape=box];\n    \"Authenticate\" [shape=box];\n    \"Determine build artifacts\" [shape=box];\n    \"pinme upload \u003Cpath>\" [shape=box];\n    \"Return preview URL\" [shape=doublecircle];\n\n    \"Install\u002Fupdate pinme to latest\" -> \"Authenticate\";\n    \"Authenticate\" -> \"Determine build artifacts\";\n    \"Determine build artifacts\" -> \"pinme upload \u003Cpath>\";\n    \"pinme upload \u003Cpath>\" -> \"Return preview URL\";\n}\n```\n\n**1. Check installation and update to latest:**\n```bash\nLOCAL=$(pinme --version 2>\u002Fdev\u002Fnull || echo \"0.0.0\")\nLATEST=$(npm view pinme version)\n[ \"$LOCAL\" != \"$LATEST\" ] && npm install -g pinme@latest || echo \"pinme is up to date ($LOCAL)\"\n```\n\n**2. Authenticate:**\n```bash\npinme login\n# or: pinme set-appkey \u003CAppKey>\n```\n\n**3. Determine upload target** (priority order):\n1. `dist\u002F` — Vite \u002F Vue \u002F React\n2. `build\u002F` — Create React App\n3. `out\u002F` — Next.js static export\n4. `public\u002F` — Plain static files\n\n**4. Upload:**\n```bash\npinme upload \u003Cpath>\npinme upload .\u002Fdist --domain my-site  # Optional: bind subdomain (wallet balance required)\n```\n\n**5. Return** the final URL printed by PinMe to the user. URL priority is: DNS domain > PinMe subdomain > short URL > preview URL. If it falls back to preview, return the **full URL** including all hash characters — do not truncate.\n\n### Common Examples\n\n```bash\npinme upload .\u002Fdocument.pdf          # Single file\npinme upload .\u002Fmy-folder             # Folder\npinme upload dist                    # Vite\u002FVue build artifacts\npinme upload build                   # CRA build artifacts\npinme upload out                     # Next.js static export\npinme upload .\u002Fdist --domain my-site # Bind PinMe subdomain (wallet balance required)\npinme import .\u002Fmy-archive.car        # Import CAR file\n```\n\n### Do NOT Upload\n- `node_modules\u002F`, `.env`, `.git\u002F`, `src\u002F`\n- Only upload build artifacts, never upload source code\n\n---\n\n## Path 2: Full-Stack Project\n\n> Login required. Uses React+Vite frontend + Cloudflare Worker backend + D1 SQLite database.\n> When designing frontend projects, use Ant Design as the primary design reference, and prioritize following its conventions for layout, components, spacing, and interaction patterns.\n\n```dot\ndigraph fullstack_flow {\n    \"Install\u002Fupdate pinme to latest\" [shape=box];\n    \"pinme login\" [shape=box];\n    \"pinme create \u003Cname>\" [shape=box];\n    \"Modify template code\" [shape=box];\n    \"pinme save\" [shape=box];\n    \"Return preview URL\" [shape=doublecircle];\n\n    \"Install\u002Fupdate pinme to latest\" -> \"pinme login\";\n    \"pinme login\" -> \"pinme create \u003Cname>\";\n    \"pinme create \u003Cname>\" -> \"Modify template code\";\n    \"Modify template code\" -> \"pinme save\";\n    \"pinme save\" -> \"Return preview URL\";\n}\n```\n\n### Architecture\n\n| Layer | Tech Stack | Deploy Target |\n|-------|-----------|---------------|\n| Frontend | React + Vite (`frontend\u002F`) | IPFS |\n| Backend | Cloudflare Worker (`backend\u002Fsrc\u002Fworker.ts`) | `{name}.pinme.pro` |\n| Database | D1 SQLite (`db\u002F*.sql`) | Cloudflare D1 |\n\n### Core Commands\n\n```bash\npinme login                  # Login (only needed once)\npinme create \u003CdirName>       # Clone template and create project (auto-fills API URL)\npinme save                   # First deploy \u002F full update (frontend + backend + database, single command)\npinme update-worker          # Update backend only (when only backend\u002Fsrc\u002Fworker.ts was modified)\npinme update-web             # Update frontend only (when only frontend\u002Fsrc\u002F was modified)\npinme update-db              # Run SQL migrations only (when only db\u002F was modified)\n```\n\n> `pinme save` deploys frontend + backend + database all at once. Only use `pinme update-*` when you're certain only one part was modified.\n\n### Project Structure\n\n```\n{project}\u002F\n├── pinme.toml              # Root config (auto-generated, do not modify)\n├── package.json            # Monorepo root (workspaces: frontend + backend)\n├── backend\u002F\n│   ├── wrangler.toml       # Worker config (auto-generated, do not modify)\n│   ├── package.json\n│   └── src\u002F\n│       └── worker.ts       # Backend entry — primarily used for JSON APIs in this template\n├── db\u002F\n│   └── 001_init.sql        # SQL table definitions\n├── frontend\u002F\n│   ├── package.json\n│   ├── vite.config.ts      # Dev proxy: \u002Fapi → localhost:8787\n│   ├── index.html\n│   ├── .env                # Auto-generated: VITE_API_URL (do not modify)\n│   └── src\u002F\n│       ├── main.tsx\n│       ├── App.tsx\n│       ├── utils\u002F\n│       │   ├── api.ts      # export const API = import.meta.env.VITE_WORKER_URL || ''\n│       │   └── config.ts   # Auto-generated: public_client_config (only when auth is enabled)\n│       └── pages\u002F\n│           └── Home\u002F\n│               └── index.tsx\n└── .gitignore\n```\n\n### First Deployment\n\n```bash\nLOCAL=$(pinme --version 2>\u002Fdev\u002Fnull || echo \"0.0.0\")\nLATEST=$(npm view pinme version)\n[ \"$LOCAL\" != \"$LATEST\" ] && npm install -g pinme@latest\npinme login\npinme create my-app\ncd my-app\n```\n\n`pinme create` generates a working Hello World template (includes frontend page + backend API routes + database schema). **Modify the template** to match the user's business logic — do not write from scratch:\n\n- Modify `backend\u002Fsrc\u002Fworker.ts` — replace API routes\n- Modify `frontend\u002Fsrc\u002Fpages\u002F` — replace page components\n- Modify `db\u002F001_init.sql` — replace table definitions\n\n```bash\npinme save\n# Single command deploys frontend + backend + database\n# Outputs preview URL: https:\u002F\u002Fpinme.eth.limo\u002F#\u002Fpreview\u002F{CID}\n```\n\n**Return** the preview URL to the user. Note: return the **full URL** including all hash characters — do not truncate.\n\nThe backend Worker is deployed at `https:\u002F\u002F{name}.pinme.pro`. Frontend API requests are automatically configured to point to that address — no manual setup needed.\n\n### Subsequent Updates\n\n| Changes | Command | Notes |\n|---------|---------|-------|\n| Backend only (`backend\u002Fsrc\u002Fworker.ts`) | `pinme update-worker` | Faster |\n| Frontend only (`frontend\u002Fsrc\u002F`) | `pinme update-web` | Generates new CID |\n| Database only (`db\u002F`) | `pinme update-db` | Runs new migrations |\n| Multiple changes or uncertain | `pinme save` | Safe full deployment |\n\n> Each frontend deployment generates a new CID and preview URL. Old URLs remain accessible.\n\n---\n\n## Worker Code Patterns (`backend\u002Fsrc\u002Fworker.ts`)\n\nIn this template, the Worker backend is primarily used for JSON APIs. Prefer standard Web APIs and simple manual routing by default. Worker-compatible libraries can be added when needed, but the default template does not rely on extra frameworks. Avoid packages that depend on a full Node.js runtime, a persistent local filesystem, native binaries, or child processes.\n\n```typescript\nexport interface Env {\n  DB: D1Database;           \u002F\u002F When using database\n  API_KEY?: string;         \u002F\u002F When using email sending\n  JWT_SECRET: string;       \u002F\u002F When using JWT auth\n  ADMIN_PASSWORD: string;   \u002F\u002F When using password auth\n}\n\nconst CORS_HEADERS = {\n  'Access-Control-Allow-Origin': '*',\n  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n  'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-API-Key',\n};\n\nfunction json(data: unknown, status = 200): Response {\n  return Response.json(data, { status, headers: CORS_HEADERS });\n}\n\nexport default {\n  async fetch(request: Request, env: Env): Promise\u003CResponse> {\n    const { pathname } = new URL(request.url);\n    const method = request.method;\n\n    if (method === 'OPTIONS') return new Response(null, { status: 204, headers: CORS_HEADERS });\n\n    try {\n      if (pathname === '\u002Fapi\u002Fitems' && method === 'GET')  return handleGetItems(env);\n      if (pathname === '\u002Fapi\u002Fitems' && method === 'POST') return handleCreateItem(request, env);\n      return json({ error: 'Not found' }, 404);\n    } catch {\n      return json({ error: 'Internal server error' }, 500);\n    }\n  },\n};\n```\n\n### Worker Constraints and Default Conventions\n\n| Item | Notes |\n|------|------|\n| Dependency choice | Prefer standard Web APIs and simple manual routing by default. If extra dependencies are needed, prefer Worker-compatible libraries. |\n| Node.js capability | Workers now support part of Node.js compatibility, but they are not a full Node.js runtime. Do not assume all Node.js built-in modules are available or behave exactly the same. |\n| Filesystem | Do not treat a Worker like a server with a persistent local disk. Even if some `fs` capabilities are available, do not rely on persistence across requests. |\n| Response types | This template mainly uses the Worker for JSON APIs. If there is a clear need, it can also be adapted to return HTML or other content. |\n| Password storage | Never store passwords in plaintext. Use a dedicated password hashing algorithm such as bcrypt, scrypt, or Argon2. |\n| SQL | Do not build SQL by string concatenation. Use parameterized queries such as `.bind()`. |\n\n### Email API Reference (for Worker Backend)\n\nWhen the backend needs email sending, use the PinMe platform API (`https:\u002F\u002Fpinme.cloud\u002Fapi\u002Fv4\u002Fsend_email`).\n\n**1. Configure API_KEY**\n\nAdd to the `Env` interface:\n\n```typescript\nexport interface Env {\n  DB: D1Database;\n  API_KEY?: string;  \u002F\u002F Required for email sending\n}\n```\n\n**2. Email Handler Code**\n\n```typescript\nasync function handleSendEmail(request: Request, env: Env): Promise\u003CResponse> {\n  const apiKey = env.API_KEY;\n  if (!apiKey) {\n    return json({ error: 'API_KEY not configured' }, 500);\n  }\n\n  const body = await request.json() as {\n    to?: string;\n    subject?: string;\n    html?: string;\n  };\n\n  if (!body.to) return json({ error: 'Email address is required' }, 400);\n  if (!body.subject) return json({ error: 'Subject is required' }, 400);\n  if (!body.html) return json({ error: 'HTML content is required' }, 400);\n\n  const emailRegex = \u002F^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\u002F;\n  if (!emailRegex.test(body.to)) {\n    return json({ error: 'Invalid email address' }, 400);\n  }\n\n  const response = await fetch('https:\u002F\u002Fpinme.cloud\u002Fapi\u002Fv4\u002Fsend_email', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application\u002Fjson',\n      'X-API-Key': apiKey,\n    },\n    body: JSON.stringify({\n      to: body.to,\n      subject: body.subject,\n      html: body.html,\n    }),\n  });\n\n  const result = await response.json();\n  return json(result);\n}\n```\n\n## Frontend API Utility (frontend\u002Fsrc\u002Futils\u002Fapi.ts)\n\n```typescript\n\u002F\u002F Development: Vite proxies \u002Fapi to localhost:8787\n\u002F\u002F Production: VITE_API_URL is auto-injected by pinme create\nexport const API = import.meta.env.VITE_API_URL || '';\n\nexport function getApiUrl(path: string): string {\n  return API ? `${API}${path}` : path;\n}\n```\n\n## D1 Database Operations\n\n```typescript\n\u002F\u002F Query multiple rows\nconst { results } = await env.DB.prepare('SELECT * FROM t WHERE x = ?').bind(val).all();\n\n\u002F\u002F Query single row (returns null if not found)\nconst row = await env.DB.prepare('SELECT * FROM t WHERE id = ?').bind(id).first();\n\n\u002F\u002F Insert and return new row\nconst row = await env.DB.prepare('INSERT INTO t (a, b) VALUES (?, ?) RETURNING *').bind(a, b).first();\n\n\u002F\u002F Update\nawait env.DB.prepare('UPDATE t SET a = ? WHERE id = ?').bind(val, id).run();\n\n\u002F\u002F Delete (check if affected)\nconst { meta } = await env.DB.prepare('DELETE FROM t WHERE id = ?').bind(id).run();\nif (meta.changes === 0) return json({ error: 'Not found' }, 404);\n```\n\n### SQL Migration Files\n\n**Format:** `db\u002FNNN_description.sql` (for example, `001_init.sql`). Files are executed in filename order.\n\n**SQLite Type Constraints:**\n\n| Do Not Use | Alternative |\n|-----------|-------------|\n| `BOOLEAN` | `INTEGER` (0 = false, 1 = true) |\n| `DATETIME` \u002F `TIMESTAMP` | `TEXT`, stored as ISO 8601 (default: `datetime('now')`) |\n| `JSON` type | `TEXT`, using `JSON.stringify()` \u002F `JSON.parse()` |\n| `VARCHAR(n)` | `TEXT` |\n\n## Template Architecture Suggestions\n\n| Scenario | Default Suggestion |\n|-----------|-------------|\n| File storage (image uploads) | Store external image URLs, or upload with `pinme upload` first and then store the resulting link |\n| Real-time communication | This template defaults to regular HTTP APIs. If there is no clear real-time requirement, start with polling |\n| Multiple Workers | This template defaults to combining functionality into a single Worker and separating routes by prefix |\n| Multiple databases | This template defaults to combining data into one D1 database and only splitting when isolation is truly needed |\n\n## Important Notes\n\n- `pinme.toml`, `backend\u002Fwrangler.toml`, and `frontend\u002F.env` are generated by PinMe. Do not edit them manually by default. If extra runtime configuration is truly needed, prefer doing it through PinMe-supported mechanisms.\n- Obtain the frontend API URL from the `VITE_API_URL` environment variable. Do not hardcode it.\n- Passwords, tokens, and API keys must be stored in secrets. Never put them in config files.\n\n## Common Errors\n\n| Error | Solution |\n|-------|----------|\n| `command not found: pinme` | `npm install -g pinme` |\n| `No such file or directory` | Verify that the path exists |\n| `Permission denied` | Check file or directory permissions |\n| Upload failed | Check the network connection and retry |\n| Not logged in | Run `pinme login` first |\n\n## Other Commands\n\n```bash\npinme list \u002F pinme ls -l 5     # View upload history\npinme list -c                  # Clear upload history\npinme rm \u003Chash>                # Delete uploaded content\npinme bind \u003Cpath> --domain \u003Cdomain>  # Bind domain (VIP + AppKey required)\npinme export \u003CCID>             # Export as CAR file\npinme set-appkey               # Set\u002Fview AppKey\npinme my-domains               # List bound domains\npinme delete \u003Cproject>          # Delete project (Worker + domain + D1)\npinme logout                   # Log out\n```\n","# PinMe\n\nZero-config deployment tool: upload static files to IPFS, or create and deploy full-stack web projects (React+Vite + Cloudflare Worker + D1 database). Workers also support sending emails via the PinMe platform API.\n\n## When to Use\n\n```dot\ndigraph pinme_decision {\n    \"User Request\" [shape=doublecircle];\n    \"Needs backend API or database?\" [shape=diamond];\n    \"Upload Files (Path 1)\" [shape=box];\n    \"Full-Stack Project (Path 2)\" [shape=box];\n\n    \"User Request\" -> \"Needs backend API or database?\";\n    \"Needs backend API or database?\" -> \"Upload Files (Path 1)\" [label=\"No\"];\n    \"Needs backend API or database?\" -> \"Full-Stack Project (Path 2)\" [label=\"Yes\"];\n}\n```\n\n## Path 1: Upload Files \u002F Static Sites\n\n> Login required. Use `pinme login` or `pinme set-appkey \u003CAppKey>` before `pinme upload` or `pinme import`.\n\n```dot\ndigraph upload_flow {\n    \"Install\u002Fupdate pinme to latest\" [shape=box];\n    \"Authenticate\" [shape=box];\n    \"Determine build artifacts\" [shape=box];\n    \"pinme upload \u003Cpath>\" [shape=box];\n    \"Return preview URL\" [shape=doublecircle];\n\n    \"Install\u002Fupdate pinme to latest\" -> \"Authenticate\";\n    \"Authenticate\" -> \"Determine build artifacts\";\n    \"Determine build artifacts\" -> \"pinme upload \u003Cpath>\";\n    \"pinme upload \u003Cpath>\" -> \"Return preview URL\";\n}\n```\n\n**1. Check installation and update to latest:**\n```bash\nLOCAL=$(pinme --version 2>\u002Fdev\u002Fnull || echo \"0.0.0\")\nLATEST=$(npm view pinme version)\n[ \"$LOCAL\" != \"$LATEST\" ] && npm install -g pinme@latest || echo \"pinme is up to date ($LOCAL)\"\n```\n\n**2. Authenticate:**\n```bash\npinme login\n# or: pinme set-appkey \u003CAppKey>\n```\n\n**3. Determine upload target** (priority order):\n1. `dist\u002F` — Vite \u002F Vue \u002F React\n2. `build\u002F` — Create React App\n3. `out\u002F` — Next.js static export\n4. `public\u002F` — Plain static files\n\n**4. Upload:**\n```bash\npinme upload \u003Cpath>\npinme upload .\u002Fdist --domain my-site  # Optional: bind subdomain (wallet balance required)\n```\n\n**5. Return** the final URL printed by PinMe to the user. URL priority is: DNS domain > PinMe subdomain > short URL > preview URL. If it falls back to preview, return the **full URL** including all hash characters — do not truncate.\n\n### Common Examples\n\n```bash\npinme upload .\u002Fdocument.pdf          # Single file\npinme upload .\u002Fmy-folder             # Folder\npinme upload dist                    # Vite\u002FVue build artifacts\npinme upload build                   # CRA build artifacts\npinme upload out                     # Next.js static export\npinme upload .\u002Fdist --domain my-site # Bind PinMe subdomain (wallet balance required)\npinme import .\u002Fmy-archive.car        # Import CAR file\n```\n\n### Do NOT Upload\n- `node_modules\u002F`, `.env`, `.git\u002F`, `src\u002F`\n- Only upload build artifacts, never upload source code\n\n---\n\n## Path 2: Full-Stack Project\n\n> Login required. Uses React+Vite frontend + Cloudflare Worker backend + D1 SQLite database.\n> When designing frontend projects, use Ant Design as the primary design reference, and prioritize following its conventions for layout, components, spacing, and interaction patterns.\n\n```dot\ndigraph fullstack_flow {\n    \"Install\u002Fupdate pinme to latest\" [shape=box];\n    \"pinme login\" [shape=box];\n    \"pinme create \u003Cname>\" [shape=box];\n    \"Modify template code\" [shape=box];\n    \"pinme save\" [shape=box];\n    \"Return preview URL\" [shape=doublecircle];\n\n    \"Install\u002Fupdate pinme to latest\" -> \"pinme login\";\n    \"pinme login\" -> \"pinme create \u003Cname>\";\n    \"pinme create \u003Cname>\" -> \"Modify template code\";\n    \"Modify template code\" -> \"pinme save\";\n    \"pinme save\" -> \"Return preview URL\";\n}\n```\n\n### Architecture\n\n| Layer | Tech Stack | Deploy Target |\n|-------|-----------|---------------|\n| Frontend | React + Vite (`frontend\u002F`) | IPFS |\n| Backend | Cloudflare Worker (`backend\u002Fsrc\u002Fworker.ts`) | `{name}.pinme.pro` |\n| Database | D1 SQLite (`db\u002F*.sql`) | Cloudflare D1 |\n\n### Core Commands\n\n```bash\npinme login                  # Login (only needed once)\npinme create \u003CdirName>       # Clone template and create project (auto-fills API URL)\npinme save                   # First deploy \u002F full update (frontend + backend + database, single command)\npinme update-worker          # Update backend only (when only backend\u002Fsrc\u002Fworker.ts was modified)\npinme update-web             # Update frontend only (when only frontend\u002Fsrc\u002F was modified)\npinme update-db              # Run SQL migrations only (when only db\u002F was modified)\n```\n\n> `pinme save` deploys frontend + backend + database all at once. Only use `pinme update-*` when you're certain only one part was modified.\n\n### Project Structure\n\n```\n{project}\u002F\n├── pinme.toml              # Root config (auto-generated, do not modify)\n├── package.json            # Monorepo root (workspaces: frontend + backend)\n├── backend\u002F\n│   ├── wrangler.toml       # Worker config (auto-generated, do not modify)\n│   ├── package.json\n│   └── src\u002F\n│       └── worker.ts       # Backend entry — primarily used for JSON APIs in this template\n├── db\u002F\n│   └── 001_init.sql        # SQL table definitions\n├── frontend\u002F\n│   ├── package.json\n│   ├── vite.config.ts      # Dev proxy: \u002Fapi → localhost:8787\n│   ├── index.html\n│   ├── .env                # Auto-generated: VITE_API_URL (do not modify)\n│   └── src\u002F\n│       ├── main.tsx\n│       ├── App.tsx\n│       ├── utils\u002F\n│       │   ├── api.ts      # export const API = import.meta.env.VITE_WORKER_URL || ''\n│       │   └── config.ts   # Auto-generated: public_client_config (only when auth is enabled)\n│       └── pages\u002F\n│           └── Home\u002F\n│               └── index.tsx\n└── .gitignore\n```\n\n### First Deployment\n\n```bash\nLOCAL=$(pinme --version 2>\u002Fdev\u002Fnull || echo \"0.0.0\")\nLATEST=$(npm view pinme version)\n[ \"$LOCAL\" != \"$LATEST\" ] && npm install -g pinme@latest\npinme login\npinme create my-app\ncd my-app\n```\n\n`pinme create` generates a working Hello World template (includes frontend page + backend API routes + database schema). **Modify the template** to match the user's business logic — do not write from scratch:\n\n- Modify `backend\u002Fsrc\u002Fworker.ts` — replace API routes\n- Modify `frontend\u002Fsrc\u002Fpages\u002F` — replace page components\n- Modify `db\u002F001_init.sql` — replace table definitions\n\n```bash\npinme save\n# Single command deploys frontend + backend + database\n# Outputs preview URL: https:\u002F\u002Fpinme.eth.limo\u002F#\u002Fpreview\u002F{CID}\n```\n\n**Return** the preview URL to the user. Note: return the **full URL** including all hash characters — do not truncate.\n\nThe backend Worker is deployed at `https:\u002F\u002F{name}.pinme.pro`. Frontend API requests are automatically configured to point to that address — no manual setup needed.\n\n### Subsequent Updates\n\n| Changes | Command | Notes |\n|---------|---------|-------|\n| Backend only (`backend\u002Fsrc\u002Fworker.ts`) | `pinme update-worker` | Faster |\n| Frontend only (`frontend\u002Fsrc\u002F`) | `pinme update-web` | Generates new CID |\n| Database only (`db\u002F`) | `pinme update-db` | Runs new migrations |\n| Multiple changes or uncertain | `pinme save` | Safe full deployment |\n\n> Each frontend deployment generates a new CID and preview URL. Old URLs remain accessible.\n\n---\n\n## Worker Code Patterns (`backend\u002Fsrc\u002Fworker.ts`)\n\nIn this template, the Worker backend is primarily used for JSON APIs. Prefer standard Web APIs and simple manual routing by default. Worker-compatible libraries can be added when needed, but the default template does not rely on extra frameworks. Avoid packages that depend on a full Node.js runtime, a persistent local filesystem, native binaries, or child processes.\n\n```typescript\nexport interface Env {\n  DB: D1Database;           \u002F\u002F When using database\n  API_KEY?: string;         \u002F\u002F When using email sending\n  JWT_SECRET: string;       \u002F\u002F When using JWT auth\n  ADMIN_PASSWORD: string;   \u002F\u002F When using password auth\n}\n\nconst CORS_HEADERS = {\n  'Access-Control-Allow-Origin': '*',\n  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n  'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-API-Key',\n};\n\nfunction json(data: unknown, status = 200): Response {\n  return Response.json(data, { status, headers: CORS_HEADERS });\n}\n\nexport default {\n  async fetch(request: Request, env: Env): Promise\u003CResponse> {\n    const { pathname } = new URL(request.url);\n    const method = request.method;\n\n    if (method === 'OPTIONS') return new Response(null, { status: 204, headers: CORS_HEADERS });\n\n    try {\n      if (pathname === '\u002Fapi\u002Fitems' && method === 'GET')  return handleGetItems(env);\n      if (pathname === '\u002Fapi\u002Fitems' && method === 'POST') return handleCreateItem(request, env);\n      return json({ error: 'Not found' }, 404);\n    } catch {\n      return json({ error: 'Internal server error' }, 500);\n    }\n  },\n};\n```\n\n### Worker Constraints and Default Conventions\n\n| Item | Notes |\n|------|------|\n| Dependency choice | Prefer standard Web APIs and simple manual routing by default. If extra dependencies are needed, prefer Worker-compatible libraries. |\n| Node.js capability | Workers now support part of Node.js compatibility, but they are not a full Node.js runtime. Do not assume all Node.js built-in modules are available or behave exactly the same. |\n| Filesystem | Do not treat a Worker like a server with a persistent local disk. Even if some `fs` capabilities are available, do not rely on persistence across requests. |\n| Response types | This template mainly uses the Worker for JSON APIs. If there is a clear need, it can also be adapted to return HTML or other content. |\n| Password storage | Never store passwords in plaintext. Use a dedicated password hashing algorithm such as bcrypt, scrypt, or Argon2. |\n| SQL | Do not build SQL by string concatenation. Use parameterized queries such as `.bind()`. |\n\n### Email API Reference (for Worker Backend)\n\nWhen the backend needs email sending, use the PinMe platform API (`https:\u002F\u002Fpinme.cloud\u002Fapi\u002Fv4\u002Fsend_email`).\n\n**1. Configure API_KEY**\n\nAdd to the `Env` interface:\n\n```typescript\nexport interface Env {\n  DB: D1Database;\n  API_KEY?: string;  \u002F\u002F Required for email sending\n}\n```\n\n**2. Email Handler Code**\n\n```typescript\nasync function handleSendEmail(request: Request, env: Env): Promise\u003CResponse> {\n  const apiKey = env.API_KEY;\n  if (!apiKey) {\n    return json({ error: 'API_KEY not configured' }, 500);\n  }\n\n  const body = await request.json() as {\n    to?: string;\n    subject?: string;\n    html?: string;\n  };\n\n  if (!body.to) return json({ error: 'Email address is required' }, 400);\n  if (!body.subject) return json({ error: 'Subject is required' }, 400);\n  if (!body.html) return json({ error: 'HTML content is required' }, 400);\n\n  const emailRegex = \u002F^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\u002F;\n  if (!emailRegex.test(body.to)) {\n    return json({ error: 'Invalid email address' }, 400);\n  }\n\n  const response = await fetch('https:\u002F\u002Fpinme.cloud\u002Fapi\u002Fv4\u002Fsend_email', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application\u002Fjson',\n      'X-API-Key': apiKey,\n    },\n    body: JSON.stringify({\n      to: body.to,\n      subject: body.subject,\n      html: body.html,\n    }),\n  });\n\n  const result = await response.json();\n  return json(result);\n}\n```\n\n## Frontend API Utility (frontend\u002Fsrc\u002Futils\u002Fapi.ts)\n\n```typescript\n\u002F\u002F Development: Vite proxies \u002Fapi to localhost:8787\n\u002F\u002F Production: VITE_API_URL is auto-injected by pinme create\nexport const API = import.meta.env.VITE_API_URL || '';\n\nexport function getApiUrl(path: string): string {\n  return API ? `${API}${path}` : path;\n}\n```\n\n## D1 Database Operations\n\n```typescript\n\u002F\u002F Query multiple rows\nconst { results } = await env.DB.prepare('SELECT * FROM t WHERE x = ?').bind(val).all();\n\n\u002F\u002F Query single row (returns null if not found)\nconst row = await env.DB.prepare('SELECT * FROM t WHERE id = ?').bind(id).first();\n\n\u002F\u002F Insert and return new row\nconst row = await env.DB.prepare('INSERT INTO t (a, b) VALUES (?, ?) RETURNING *').bind(a, b).first();\n\n\u002F\u002F Update\nawait env.DB.prepare('UPDATE t SET a = ? WHERE id = ?').bind(val, id).run();\n\n\u002F\u002F Delete (check if affected)\nconst { meta } = await env.DB.prepare('DELETE FROM t WHERE id = ?').bind(id).run();\nif (meta.changes === 0) return json({ error: 'Not found' }, 404);\n```\n\n### SQL Migration Files\n\n**Format:** `db\u002FNNN_description.sql` (for example, `001_init.sql`). Files are executed in filename order.\n\n**SQLite Type Constraints:**\n\n| Do Not Use | Alternative |\n|-----------|-------------|\n| `BOOLEAN` | `INTEGER` (0 = false, 1 = true) |\n| `DATETIME` \u002F `TIMESTAMP` | `TEXT`, stored as ISO 8601 (default: `datetime('now')`) |\n| `JSON` type | `TEXT`, using `JSON.stringify()` \u002F `JSON.parse()` |\n| `VARCHAR(n)` | `TEXT` |\n\n## Template Architecture Suggestions\n\n| Scenario | Default Suggestion |\n|-----------|-------------|\n| File storage (image uploads) | Store external image URLs, or upload with `pinme upload` first and then store the resulting link |\n| Real-time communication | This template defaults to regular HTTP APIs. If there is no clear real-time requirement, start with polling |\n| Multiple Workers | This template defaults to combining functionality into a single Worker and separating routes by prefix |\n| Multiple databases | This template defaults to combining data into one D1 database and only splitting when isolation is truly needed |\n\n## Important Notes\n\n- `pinme.toml`, `backend\u002Fwrangler.toml`, and `frontend\u002F.env` are generated by PinMe. Do not edit them manually by default. If extra runtime configuration is truly needed, prefer doing it through PinMe-supported mechanisms.\n- Obtain the frontend API URL from the `VITE_API_URL` environment variable. Do not hardcode it.\n- Passwords, tokens, and API keys must be stored in secrets. Never put them in config files.\n\n## Common Errors\n\n| Error | Solution |\n|-------|----------|\n| `command not found: pinme` | `npm install -g pinme` |\n| `No such file or directory` | Verify that the path exists |\n| `Permission denied` | Check file or directory permissions |\n| Upload failed | Check the network connection and retry |\n| Not logged in | Run `pinme login` first |\n\n## Other Commands\n\n```bash\npinme list \u002F pinme ls -l 5     # View upload history\npinme list -c                  # Clear upload history\npinme rm \u003Chash>                # Delete uploaded content\npinme bind \u003Cpath> --domain \u003Cdomain>  # Bind domain (VIP + AppKey required)\npinme export \u003CCID>             # Export as CAR file\npinme set-appkey               # Set\u002Fview AppKey\npinme my-domains               # List bound domains\npinme delete \u003Cproject>          # Delete project (Worker + domain + D1)\npinme logout                   # Log out\n```\n",{"name":5,"description":7},"main","1907bbe850fb749efa8b499b8da7ebb78a663519","skills\u002Fpinme",0,"https:\u002F\u002Fgithub.com\u002FdevelopAgent\u002Fbmad-start","https:\u002F\u002Fgithub.com\u002FdevelopAgent","2026-07-19 05:19:22",{"source":8,"skillName":5,"commands":31,"prompt":41,"zipAvailable":42},[32,35,38],{"tool":33,"command":34},"npx","npx skills add developAgent\u002Fbmad-start --skill pinme",{"tool":36,"command":37},"bunx","bunx skills add developAgent\u002Fbmad-start --skill pinme",{"tool":39,"command":40},"pnpm","pnpm dlx skills add developAgent\u002Fbmad-start --skill pinme","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FdevelopAgent\u002Fbmad-start\u002Fblob\u002Fmain\u002Fskills\u002Fpinme\u002FSKILL.md",true,[],[45,52,59,66,73,80],{"skillNo":46,"name":47,"nameZh":6,"description":48,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":49,"categories":50,"keywords":51,"publishedAt":18},"01M72ZN1RJ28HE3TG8CK5VMDZ0","writing-plans","Use when you have a spec or requirements for a multi-step task, before touching code","https:\u002F\u002Fgithub.com\u002FdevelopAgent\u002Fbmad-start\u002Fblob\u002Fmain\u002Fskills\u002Fwriting-plans\u002FSKILL.md",[],[],{"skillNo":53,"name":54,"nameZh":6,"description":55,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":56,"categories":57,"keywords":58,"publishedAt":18},"01M72ZMZG4KQAFDEHG6KABQW8R","to-prd","Turn the current conversation into a PRD and publish it to the project issue tracker — no interview, just synthesis of what you've already discussed.","https:\u002F\u002Fgithub.com\u002FdevelopAgent\u002Fbmad-start\u002Fblob\u002Fmain\u002Fskills\u002Fto-prd\u002FSKILL.md",[],[],{"skillNo":60,"name":61,"nameZh":6,"description":62,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":63,"categories":64,"keywords":65,"publishedAt":18},"01M72ZMWB9ZNA0Y5KGAHTW89E8","steve-jobs-perspective","史蒂夫·乔布斯(Steve Jobs)的思维框架与表达方式。基于Isaacson授权传记、Stanford演讲、\nLost Interview、D Conference系列、Make Something Wonderful、30+一手来源的深度调研，\n提炼6个核心心智模型、8条决策启发式和完整的表达DNA。\n用途：作为思维顾问，用乔布斯的视角分析产品、审视决策、提供反馈。\n当用户提到「用乔布斯的视角」「乔布斯会怎么看」「Jobs模式」「steve jobs perspective」时使用。\n即使用户只是说「帮我用乔布斯的角度想想」「如果乔布斯会怎么做」「切换到乔布斯」也应触发。","https:\u002F\u002Fgithub.com\u002FdevelopAgent\u002Fbmad-start\u002Fblob\u002Fmain\u002Fskills\u002Fsteve-jobs-perspective\u002FSKILL.md",[],[],{"skillNo":67,"name":68,"nameZh":6,"description":69,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":70,"categories":71,"keywords":72,"publishedAt":18},"01M72ZMPJHQ8NHC9WSH41DKV1F","pinme-share","Use this skill when the user wants to share, publish, or upload a static result through PinMe, especially by generating a static HTML share page for a PinMe project link, deployed full-stack app, Codex conversation summary, report, file, demo, or any 分享\u002F发布\u002F上传分享页 request that should end with `pinme upload`.","https:\u002F\u002Fgithub.com\u002FdevelopAgent\u002Fbmad-start\u002Fblob\u002Fmain\u002Fskills\u002Fpinme-share\u002FSKILL.md",[],[],{"skillNo":74,"name":75,"nameZh":6,"description":76,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":77,"categories":78,"keywords":79,"publishedAt":18},"01M72ZMM4QBVWMAW0YYM7H0Y6P","pinme-llm","Use this skill when a PinMe project (Worker TypeScript) needs to call OpenRouter-backed LLM APIs, including models, chat\u002Fcompletions, streaming, or OpenRouter web search. Guides AI to generate correct Worker TS code.","https:\u002F\u002Fgithub.com\u002FdevelopAgent\u002Fbmad-start\u002Fblob\u002Fmain\u002Fskills\u002Fpinme-llm\u002FSKILL.md",[],[],{"skillNo":81,"name":82,"nameZh":6,"description":83,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":84,"categories":85,"keywords":86,"publishedAt":18},"01M72ZMHTK2ZBW9P79EPPNVGH0","pinme-email","Use this skill when a PinMe project (Worker TypeScript) needs to integrate email sending (send_email). Guides AI to generate correct Worker TS code.","https:\u002F\u002Fgithub.com\u002FdevelopAgent\u002Fbmad-start\u002Fblob\u002Fmain\u002Fskills\u002Fpinme-email\u002FSKILL.md",[],[]]