Skip to content
Back to skills

Boilerplate

ASecurity

Generate core/ module scaffolds for cli-web-* CLIs. Produces exceptions.py, client.py, helpers.py, config.py, output.py, and (for batchexecute) the rpc/ subpackage. All files use placeholder syntax that the methodology skill fills in.

  • 215 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 27, 2026
developmentpythongobashapisecuritydocumentation

Works with

  • cli
  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add ItamarZand88/CLI-Anything-WEB --skill boilerplate --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Boilerplate?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Boilerplate
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/itamarzand88-boilerplate/badge)](https://www.skillsdirectory.com/skills/itamarzand88-boilerplate)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: boilerplate
version: 0.1.0
description: Generate core/ module scaffolds for cli-web-* CLIs. Produces exceptions.py, client.py, helpers.py, config.py, output.py, and (for batchexecute) the rpc/ subpackage. All files use placeholder syntax that the methodology skill fills in.
user-invocable: false
---

# Boilerplate Generator

> **Preferred method:** Use `scripts/scaffold-cli.py` instead of following this
> skill manually. Run:
> ```bash
> python ${CLAUDE_PLUGIN_ROOT}/scripts/scaffold-cli.py <output-dir> \
>   --app-name <app> --protocol <proto> --http-client <client> --auth-type <auth> \
>   --resources <resources> [--has-polling] [--has-context] [--has-partial-ids]
> ```
> This generates all files below deterministically. Use this skill as fallback
> documentation only if the script is unavailable.

This skill generates the core/ and utils/ module scaffolds that are 80% identical
across all cli-web-* CLIs. The methodology skill invokes this before writing
implementation-specific code.

---

## Step 1: Collect Inputs

Gather these parameters before generating any files. Each has a default or must
be supplied by the calling skill.

| Parameter | Type | Source | Example |
|-----------|------|--------|---------|
| `app_name` | str | From CLI name (`cli-web-<app>` -> `<app>`) | `hackernews` |
| `APP_NAME` | str | UPPER_SNAKE of app_name (replace hyphens with underscores before uppercasing, e.g., `gh-trending` -> `GH_TRENDING`) | `HACKERNEWS` |
| `AppName` | str | PascalCase of app_name | `HackerNews` |
| `protocol` | enum | Traffic analysis: `rest`, `graphql`, `html-scraping`, `batchexecute` | `rest` |
| `http_client` | enum | Traffic analysis: `httpx`, `curl_cffi` | `httpx` |
| `auth_type` | enum | Site profile: `none`, `cookie`, `api-key`, `google-sso` | `cookie` |
| `resources` | list[str] | From `<APP>.md` endpoint groups | `["stories", "users", "search"]` |
| `has_polling` | bool | Any async/long-running operations? | `false` |
| `has_context` | bool | Does the CLI need `use <id>` / `status` context? | `false` |
| `has_partial_ids` | bool | Do resource IDs support prefix matching? | `false` |

---

## Step 2: Decision Matrix

Which parameters affect which files:

| File | Always | Conditional on |
|------|--------|----------------|
| `__init__.py` | Yes | -- |
| `__main__.py` | Yes | -- |
| `core/exceptions.py` | Yes | -- |
| `core/config.py` | Yes | `auth_type` (skip AUTH_FILE/AUTH_ENV_VAR if `none`) |
| `core/client.py` | Yes | `protocol`, `http_client` |
| `core/auth.py` | No | `auth_type != none` |
| `utils/helpers.py` | Yes | `has_polling`, `has_context`, `has_partial_ids` |
| `utils/output.py` | Yes | -- |
| `core/rpc/__init__.py` | No | `protocol == batchexecute` |
| `core/rpc/types.py` | No | `protocol == batchexecute` |
| `core/rpc/encoder.py` | No | `protocol == batchexecute` |
| `core/rpc/decoder.py` | No | `protocol == batchexecute` |

### File Generation Table

Generate each file by copying the template below, replacing all `{app_name}`,
`{APP_NAME}`, and `{AppName}` placeholders with the actual values. Include or
exclude conditional sections as indicated by the decision matrix.

---

## Step 3: Generate Files

### 3.1: `cli_web/{app_name}/__init__.py`

```python
"""cli-web-{app_name}: CLI for {AppName}."""

__version__ = "0.1.0"
```

### 3.2: `cli_web/{app_name}/__main__.py`

```python
"""Allow running as: python -m cli_web.{app_name}"""
from .{app_name}_cli import cli

if __name__ == "__main__":
    cli()
```

### 3.3: `core/exceptions.py`

```python
"""Typed exception hierarchy for cli-web-{app_name}.

Every exception carries enough context for:
- Retry decisions (recoverable flag, retry_after)
- Structured JSON output (to_dict / error_code_for)
- CLI exit codes (auth=1, server=2, network=3)
"""
from __future__ import annotations


class {AppName}Error(Exception):
    """Base exception for all cli-web-{app_name} errors."""

    def to_dict(self) -> dict:
        return {{
            "error": True,
            "code": _error_code_for(self),
            "message": str(self),
        }}


class AuthError({AppName}Error):
    """Authentication failed -- expired cookies, invalid tokens, session timeout.

    Args:
        recoverable: If True, client retries once (token refresh).
                     If False, user must re-login.
    """

    def __init__(self, message: str, recoverable: bool = True):
        self.recoverable = recoverable
        super().__init__(message)


class RateLimitError({AppName}Error):
    """Server returned 429 -- too many requests.

    Args:
        retry_after: Seconds to wait before retrying (from Retry-After header).
    """

    def __init__(self, message: str, retry_after: float | None = None):
        self.retry_after = retry_after
        super().__init__(message)

    def to_dict(self) -> dict:
        d = super().to_dict()
        if self.retry_after is not None:
            d["retry_after"] = self.retry_after
        return d


class NetworkError({AppName}Error):
    """Connection failed -- DNS resolution, TCP connect, TLS handshake."""


class ServerError({AppName}Error):
    """Server returned 5xx -- internal error, bad gateway, service unavailable.

    Args:
        status_code: The HTTP status code (500, 502, 503, etc.)
    """

    def __init__(self, message: str, status_code: int = 500):
        self.status_code = status_code
        super().__init__(message)


class NotFoundError({AppName}Error):
    """Resource not found (HTTP 404)."""


class RPCError({AppName}Error):
    """RPC call failed (batchexecute decode error, unexpected response shape)."""


# --- HTTP status code mapping ---

_CODE_MAP = {{
    401: lambda msg: AuthError(msg, recoverable=True),
    403: lambda msg: AuthError(msg, recoverable=True),
    404: lambda msg: NotFoundError(msg),
    # 429 handled separately below to extract Retry-After header
}}


def _error_code_for(exc: {AppName}Error) -> str:
    """Map exception type to a JSON error code string."""
    mapping = {{
        AuthError: "AUTH_EXPIRED",
        RateLimitError: "RATE_LIMITED",
        NotFoundError: "NOT_FOUND",
        ServerError: "SERVER_ERROR",
        NetworkError: "NETWORK_ERROR",
        RPCError: "RPC_ERROR",
    }}
    for exc_type, code in mapping.items():
        if isinstance(exc, exc_type):
            return code
    return "UNKNOWN_ERROR"


def raise_for_status(response) -> None:
    """Map HTTP response status to a typed exception. Call after every request."""
    if response.status_code < 400:
        return

    text = getattr(response, "text", "")[:200]
    msg = f"HTTP {{response.status_code}}: {{text}}"

    # Specific status codes
    if response.status_code in _CODE_MAP:
        raise _CODE_MAP[response.status_code](msg)

    # Extract Retry-After for 429
    if response.status_code == 429:
        retry_after = None
        if hasattr(response, "headers"):
            raw = response.headers.get("Retry-After")
            if raw:
                retry_after = float(raw)
        raise RateLimitError(msg, retry_after=retry_after)

    # 5xx range
    if 500 <= response.status_code < 600:
        raise ServerError(msg, status_code=response.status_code)

    # 4xx fallback
    raise {AppName}Error(msg)
```

### 3.4: `core/config.py`

```python
"""Configuration constants for cli-web-{app_name}."""
from pathlib import Path

APP_NAME = "cli-web-{app_name}"
CONFIG_DIR = Path.home() / ".config" / APP_NAME
# --- conditional: auth_type != "none" ---
AUTH_FILE = "auth.json"
AUTH_ENV_VAR = "CLI_WEB_{APP_NAME}_AUTH_JSON"
# --- end conditional ---
# --- conditional: has_context ---
CONTEXT_FILE = "context.json"
# --- end conditional ---


def get_config_dir() -> Path:
    """Return (and create) the config directory."""
    CONFIG_DIR.mkdir(parents=True, exist_ok=True)
    return CONFIG_DIR


# --- conditional: auth_type != "none" ---
def get_auth_path() -> Path:
    """Return the path to auth.json, creating config dir if needed."""
    return get_config_dir() / AUTH_FILE
# --- end conditional ---
```

**If `auth_type == "none"`**: Remove the lines between `--- conditional: auth_type != "none" ---`
markers (AUTH_FILE, AUTH_ENV_VAR, and get_auth_path).

**If `has_context == false`**: Remove the CONTEXT_FILE line.

### 3.5: `core/client.py`

Generate ONE of the following variants based on `protocol` and `http_client`.

#### Variant A: REST + httpx

```python
"""HTTP client for cli-web-{app_name}."""
from __future__ import annotations

import httpx

from .exceptions import (
    {AppName}Error,
    AuthError,
    NetworkError,
    raise_for_status,
)


class {AppName}Client:
    """REST client with auth retry and typed exceptions."""

    BASE_URL = "https://FILL_IN_BASE_URL"

    def __init__(self, cookies: dict | None = None, api_key: str | None = None):
        self._cookies = cookies or {{}}
        self._api_key = api_key
        headers = {{"User-Agent": "cli-web-{app_name}/0.1.0"}}
        if self._api_key:
            headers["Authorization"] = f"Bearer {{self._api_key}}"
        self._client = httpx.Client(
            base_url=self.BASE_URL,
            timeout=httpx.Timeout(connect=10.0, read=30.0, write=30.0, pool=30.0),
            headers=headers,
        )

    def _request(
        self,
        method: str,
        path: str,
        *,
        retry_on_auth: bool = True,
        **kwargs,
    ) -> httpx.Response:
        kwargs.setdefault("cookies", self._cookies)
        try:
            resp = self._client.request(method, path, **kwargs)
        except httpx.ConnectError as exc:
            raise NetworkError(f"Connection failed: {{exc}}")
        except httpx.TimeoutException as exc:
            raise NetworkError(f"Request timed out: {{exc}}")

        if resp.status_code in (401, 403) and retry_on_auth:
            self._refresh_auth()
            return self._request(method, path, retry_on_auth=False, **kwargs)

        raise_for_status(resp)
        return resp

    def _refresh_auth(self) -> None:
        """Override to implement token refresh logic."""
        raise AuthError("Auth expired. Run: cli-web-{app_name} auth login", recoverable=False)

    # --- Add endpoint methods here ---
    # def list_items(self) -> list[dict]:
    #     resp = self._request("GET", "/api/items")
    #     return resp.json()

    def close(self):
        self._client.close()

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()
```

#### Variant B: REST + curl_cffi

```python
"""HTTP client for cli-web-{app_name} (curl_cffi for anti-bot bypass)."""
from __future__ import annotations

from curl_cffi import requests as curl_requests

from .exceptions import (
    {AppName}Error,
    AuthError,
    NetworkError,
    raise_for_status,
)


class {AppName}Client:
    """REST client using curl_cffi Chrome TLS impersonation."""

    BASE_URL = "https://FILL_IN_BASE_URL"

    def __init__(self, cookies: dict | None = None):
        self._cookies = cookies or {{}}
        self._session = curl_requests.Session(impersonate="chrome")
        self._session.headers.update({{"User-Agent": "cli-web-{app_name}/0.1.0"}})

    def _request(
        self,
        method: str,
        url: str,
        *,
        retry_on_auth: bool = True,
        **kwargs,
    ):
        if not url.startswith("http"):
            url = self.BASE_URL + url
        kwargs.setdefault("cookies", self._cookies)
        try:
            resp = self._session.request(method, url, **kwargs)
        except Exception as exc:
            raise NetworkError(f"Connection failed: {{exc}}")

        if resp.status_code in (401, 403) and retry_on_auth:
            self._refresh_auth()
            return self._request(method, url, retry_on_auth=False, **kwargs)

        raise_for_status(resp)
        return resp

    def _refresh_auth(self) -> None:
        raise AuthError("Auth expired. Run: cli-web-{app_name} auth login", recoverable=False)

    # --- Add endpoint methods here ---

    def close(self):
        self._session.close()

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()
```

#### Variant C: HTML Scraping (httpx or curl_cffi)

Use Variant A or B as the base, then add this import and method:

```python
from bs4 import BeautifulSoup

# Add to class body:
def _parse_html(self, html: str) -> BeautifulSoup:
    """Parse HTML response into a BeautifulSoup tree."""
    return BeautifulSoup(html, "html.parser")
```

#### Variant D: GraphQL (httpx or curl_cffi)

Use Variant A or B as the base, then add this method:

```python
# Add to class body:
def _graphql(self, query: str, variables: dict | None = None) -> dict:
    """Execute a GraphQL query and return the data payload."""
    payload = {{"query": query}}
    if variables:
        payload["variables"] = variables
    resp = self._request("POST", "/graphql", json=payload)
    body = resp.json()
    if "errors" in body:
        raise {AppName}Error(f"GraphQL error: {{body['errors'][0].get('message', body['errors'])}}")
    return body.get("data", {{}})
```

#### Variant E: batchexecute

```python
"""HTTP client for cli-web-{app_name} (Google batchexecute RPC)."""
from __future__ import annotations

import httpx

from .exceptions import (
    {AppName}Error,
    AuthError,
    NetworkError,
    RPCError,
    raise_for_status,
)
from .rpc.encoder import encode_rpc
from .rpc.decoder import decode_response
from .rpc.types import RPCMethod


class {AppName}Client:
    """Google batchexecute RPC client."""

    BASE_URL = "https://FILL_IN_BASE_URL"
    BATCHEXECUTE_PATH = "/_/FILL_IN_SERVICE/data/batchexecute"

    def __init__(self, cookies: dict | None = None):
        self._cookies = cookies or {{}}
        self._csrf_token: str | None = None
        self._session_id: str | None = None
        self._client = httpx.Client(
            base_url=self.BASE_URL,
            timeout=httpx.Timeout(connect=10.0, read=60.0, write=30.0, pool=30.0),
            headers={{"User-Agent": "cli-web-{app_name}/0.1.0"}},
        )

    def _rpc(self, method: RPCMethod, params: list) -> list:
        """Execute an RPC call and return the decoded response."""
        body = encode_rpc(method, params, csrf_token=self._csrf_token)
        try:
            resp = self._client.post(
                self.BATCHEXECUTE_PATH,
                data=body,
                cookies=self._cookies,
            )
        except httpx.ConnectError as exc:
            raise NetworkError(f"Connection failed: {{exc}}")
        except httpx.TimeoutException as exc:
            raise NetworkError(f"Request timed out: {{exc}}")

        raise_for_status(resp)
        return decode_response(resp.text, method)

    def _refresh_tokens(self) -> None:
        """Fetch homepage to extract fresh CSRF/session tokens."""
        import re
        resp = self._client.get("/", cookies=self._cookies, follow_redirects=True)
        if resp.status_code != 200:
            raise AuthError("Token refresh failed. Run: cli-web-{app_name} auth login", recoverable=False)
        # Customize these regex patterns for the target app
        html = resp.text
        m = re.search(r'"SNlM0e"\s*:\s*"([^"]+)"', html)
        if m:
            self._csrf_token = m.group(1)

    # --- Add RPC method wrappers here ---

    def close(self):
        self._client.close()

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()
```

### 3.6: `utils/helpers.py`

```python
"""Shared helpers for cli-web-{app_name}."""
from __future__ import annotations

import io
import json
import sys
from contextlib import contextmanager

import click

from ..core.exceptions import {AppName}Error, _error_code_for


# --- Windows UTF-8 fix (always include) ---
def ensure_utf8() -> None:
    """Force UTF-8 on stdout and stderr for Windows compatibility."""
    if sys.platform == "win32":
        if hasattr(sys.stdout, "reconfigure"):
            sys.stdout.reconfigure(encoding="utf-8", errors="replace")
        else:
            sys.stdout = io.TextIOWrapper(
                sys.stdout.buffer, encoding="utf-8", errors="replace"
            )
        if hasattr(sys.stderr, "reconfigure"):
            sys.stderr.reconfigure(encoding="utf-8", errors="replace")
        else:
            sys.stderr = io.TextIOWrapper(
                sys.stderr.buffer, encoding="utf-8", errors="replace"
            )


# --- Structured error handler ---
@contextmanager
def handle_errors(json_mode: bool = False):
    """Catch domain exceptions and emit structured output or Rich errors.

    Usage:
        with handle_errors(json_mode=ctx.obj.get("json")):
            do_something()
    """
    try:
        yield
    except KeyboardInterrupt:
        raise SystemExit(130)
    except (click.exceptions.Exit, click.UsageError):
        raise
    except {AppName}Error as exc:
        if json_mode:
            print_json(exc.to_dict())
        else:
            click.secho(f"Error: {{exc}}", fg="red", err=True)
        raise SystemExit(1)
    except Exception as exc:
        if json_mode:
            print_json({{"error": True, "code": "INTERNAL_ERROR", "message": str(exc)}})
        else:
            click.secho(f"Error: {{exc}}", fg="red", err=True)
        raise SystemExit(2)


def print_json(data) -> None:
    """Print data as formatted JSON to stdout."""
    print(json.dumps(data, indent=2, ensure_ascii=False, default=str))


# --- conditional: has_partial_ids ---
def resolve_partial_id(partial: str, items: list[dict], key: str = "id") -> dict:
    """Resolve a partial ID prefix to a single item.

    Raises {AppName}Error if zero or multiple matches.
    """
    matches = [item for item in items if str(item.get(key, "")).startswith(partial)]
    if len(matches) == 0:
        raise {AppName}Error(f"No item found matching '{{partial}}'")
    if len(matches) > 1:
        ids = [str(m.get(key, "")) for m in matches[:5]]
        raise {AppName}Error(f"Ambiguous ID '{{partial}}', matches: {{', '.join(ids)}}")
    return matches[0]
# --- end conditional: has_partial_ids ---


# --- conditional: has_polling ---
def poll_until_complete(
    check_fn,
    *,
    timeout: float = 300.0,
    initial_delay: float = 2.0,
    max_delay: float = 10.0,
    backoff_factor: float = 1.5,
):
    """Poll check_fn with exponential backoff until it returns a truthy value.

    Args:
        check_fn: Callable that returns a result (truthy = done) or None/falsy.
        timeout: Maximum total wait time in seconds.
        initial_delay: First sleep interval.
        max_delay: Cap on sleep interval.
        backoff_factor: Multiplier per iteration.

    Returns:
        The truthy result from check_fn.

    Raises:
        {AppName}Error if timeout is exceeded.
    """
    import time

    elapsed = 0.0
    delay = initial_delay
    while elapsed < timeout:
        result = check_fn()
        if result:
            return result
        time.sleep(delay)
        elapsed += delay
        delay = min(delay * backoff_factor, max_delay)
    raise {AppName}Error(f"Operation timed out after {{timeout}}s")
# --- end conditional: has_polling ---


# --- conditional: has_context ---
def get_context_value(key: str) -> str | None:
    """Read a value from the persistent context file."""
    from ..core.config import CONFIG_DIR, CONTEXT_FILE

    path = CONFIG_DIR / CONTEXT_FILE
    if not path.exists():
        return None
    data = json.loads(path.read_text())
    return data.get(key)


def set_context_value(key: str, value: str) -> None:
    """Write a value to the persistent context file."""
    from ..core.config import CONFIG_DIR, CONTEXT_FILE

    path = CONFIG_DIR / CONTEXT_FILE
    CONFIG_DIR.mkdir(parents=True, exist_ok=True)
    data = {{}}
    if path.exists():
        data = json.loads(path.read_text())
    data[key] = value
    path.write_text(json.dumps(data, indent=2))
# --- end conditional: has_context ---
```

**Conditional sections**: Remove the blocks between `--- conditional: ... ---` /
`--- end conditional: ... ---` markers when the corresponding flag is `false`.

### 3.7: `utils/output.py`

```python
"""Structured JSON output helpers for cli-web-{app_name}."""
from __future__ import annotations

import json


def json_success(data, **extra) -> str:
    """Format a successful result as JSON string."""
    payload = {{"success": True, "data": data}}
    payload.update(extra)
    return json.dumps(payload, indent=2, ensure_ascii=False, default=str)


def json_error(code: str, message: str, **extra) -> str:
    """Format an error result as JSON string."""
    payload = {{"error": True, "code": code, "message": message}}
    payload.update(extra)
    return json.dumps(payload, indent=2, ensure_ascii=False, default=str)
```

### 3.8: `core/rpc/` (batchexecute only)

Only generate these files when `protocol == "batchexecute"`.

#### `core/rpc/__init__.py`

```python
"""RPC encoding/decoding for Google batchexecute protocol."""
```

#### `core/rpc/types.py`

```python
"""RPC method definitions for cli-web-{app_name}.

Each method maps to a batchexecute RPC ID discovered from traffic capture.
IMPORTANT: Verify every RPC ID against captured traffic. The same endpoint
may use different param structures for different operations.
"""
from __future__ import annotations

from enum import Enum


class RPCMethod(Enum):
    """Known RPC methods.

    Format: NAME = ("rpc_id", "human_description")
    Fill in from <APP>.md after traffic analysis.
    """

    # EXAMPLE = ("AbCdEf", "Example operation description")
    pass
```

#### `core/rpc/encoder.py`

```python
"""Encode RPC requests for Google batchexecute.

Builds the f.req form body expected by /_/SERVICE/data/batchexecute.
"""
from __future__ import annotations

import json

from .types import RPCMethod


def encode_rpc(
    method: RPCMethod,
    params: list,
    *,
    csrf_token: str | None = None,
) -> dict:
    """Encode an RPC call into a batchexecute form body.

    Returns a dict suitable for httpx data= parameter.
    """
    rpc_id = method.value[0]
    inner = json.dumps(params, separators=(",", ":"))
    req_body = json.dumps([[
        [rpc_id, inner, None, "generic"],
    ]], separators=(",", ":"))
    body = {{"f.req": req_body}}
    if csrf_token:
        body["at"] = csrf_token
    return body
```

#### `core/rpc/decoder.py`

```python
"""Decode batchexecute RPC responses.

Google batchexecute responses have a prefix line (e.g., )]}'\\n) followed
by length-prefixed JSON arrays. This module strips the prefix and parses
the inner payload.
"""
from __future__ import annotations

import json

from ..exceptions import RPCError
from .types import RPCMethod


def decode_response(raw: str, method: RPCMethod) -> list:
    """Decode a batchexecute response and return the inner payload.

    Args:
        raw: The full response text from batchexecute endpoint.
        method: The RPC method that was called (for error context).

    Returns:
        Parsed inner JSON array from the RPC response.

    Raises:
        RPCError: If the response cannot be parsed.
    """
    # Strip the security prefix
    lines = raw.split("\\n")
    for i, line in enumerate(lines):
        if line.strip().startswith("[["):
            break
    else:
        raise RPCError(f"Cannot parse batchexecute response for {{method.value[0]}}")

    try:
        outer = json.loads(lines[i])
    except json.JSONDecodeError as exc:
        raise RPCError(f"JSON decode failed for {{method.value[0]}}: {{exc}}")

    # Navigate to inner payload: outer[0][2] contains the JSON string
    try:
        inner_str = outer[0][2]
        if isinstance(inner_str, str):
            return json.loads(inner_str)
        return inner_str
    except (IndexError, TypeError, json.JSONDecodeError) as exc:
        raise RPCError(f"Inner payload extraction failed for {{method.value[0]}}: {{exc}}")
```

---

## Step 4: Post-Generation Checklist

After generating all files, verify:

- [ ] `cli_web/` directory has NO `__init__.py` (namespace package)
- [ ] `cli_web/{app_name}/` directory HAS `__init__.py` (sub-package)
- [ ] All `{app_name}`, `{APP_NAME}`, `{AppName}` placeholders are replaced with actual values
- [ ] `FILL_IN_BASE_URL` is noted as requiring replacement during implementation
- [ ] `core/exceptions.py` has `to_dict()` on base class and override on `RateLimitError`
- [ ] `utils/helpers.py` Windows UTF-8 fix covers BOTH stdout AND stderr
- [ ] Conditional sections (polling, context, partial IDs) match the collected inputs
- [ ] For `batchexecute` protocol: `core/rpc/` directory exists with all four files
- [ ] For non-`batchexecute` protocol: `core/rpc/` directory does NOT exist
- [ ] `setup.py` uses `find_namespace_packages(include=["cli_web.*"])`

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.