From e430327d24e5be4df31d1fd55cba5cf2fff13de3 Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Wed, 25 Mar 2026 21:42:23 +0000 Subject: [PATCH 1/2] docs: add agent guidance to TMA template --- AGENTS.md | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 6 ++++++ 2 files changed, 66 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..1562901 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,60 @@ +# SpawnDock TMA Template + +You are an AI agent working inside the SpawnDock Telegram Mini App template. + +Your job is to build and improve a production-ready Telegram Mini App in this repository. Treat this as a real TMA project, not as a generic web app and not as a docs-only exercise. + +## Default Mode + +- Work end-to-end inside the repo: design, implement, and validate when the user is asking for a result. +- Prefer concrete code and file changes over abstract advice unless the user explicitly wants brainstorming only. +- If requirements are incomplete, choose the smallest sensible TMA-first default and keep moving. +- Ask a question only when missing information would materially change the product flow or create a high risk of doing the wrong work. + +## Project Contract + +- Preserve the current stack: Next.js, App Router, TypeScript, SpawnDock scripts, and the existing Telegram/TMA integrations. +- Do not replace the framework, routing model, or core dev workflow unless the user explicitly asks for that change. +- Prefer changes that fit the existing `src/app` structure, shared styling approach, and `spawndock/*.mjs` wrappers. +- Use `pnpm` commands, not `npm`, unless a task explicitly requires otherwise. + +## TMA Rules + +- Always treat the app as a Telegram Mini App first. +- Prefer mobile-first, touch-first UX and layouts that work well inside Telegram WebView. +- Use Telegram WebApp APIs where appropriate: `ready`, `expand`, `MainButton`, `BackButton`, `themeParams`, `HapticFeedback`, `openLink`, `openTelegramLink`, `sendData`, and viewport APIs. +- Respect Telegram theming. Do not hardcode colors where Telegram theme params should drive the UI. +- Avoid browser patterns that are fragile inside Telegram WebView: `alert()`, `confirm()`, `prompt()`, `target=\"_blank\"`, `window.open()`, and desktop-first navigation patterns. +- Do not introduce `BrowserRouter`-style assumptions for TMA navigation. + +## Dev Flow + +- `pnpm run dev` is the primary local workflow. It starts the Next.js dev server and the SpawnDock dev tunnel together. +- `pnpm run dev:next` starts only the local Next.js server. +- `pnpm run dev:tunnel` starts only the SpawnDock tunnel client. +- `pnpm run agent` starts the Next.js server, the tunnel, and the local agent runtime launcher. +- If the user asks to run the project, preview the app, or get a tunnel URL, prefer `pnpm run dev`. +- Do not describe `pnpm run dev` as “just Next.js dev”; in this template it is the combined app-plus-tunnel flow. +- If `spawndock.config.json` or `spawndock.dev-tunnel.json` is missing or invalid, the project is probably not fully bootstrapped yet. + +## Implementation Expectations + +- Build only what is needed for the requested feature set. +- Keep the architecture simple, shippable, and easy to extend. +- Reuse existing components and patterns before introducing new abstractions. +- Add loading, empty, and error states for user-facing flows when they matter. +- Make the smallest set of changes that fully solves the task. + +## Validation + +- Run the narrowest relevant checks after changes. +- Prefer `pnpm run build` for code validation. +- Use `pnpm run dev` when the task depends on runtime behavior, preview behavior, or the SpawnDock tunnel. +- If you could not run a relevant check, say so explicitly. + +## Success Criteria + +- The result fits the current template and preserves its workflow. +- The app behaves like a proper Telegram Mini App and respects Telegram constraints. +- The main local development flow, especially `pnpm run dev`, remains intact. +- The code is ready for real iteration, not just for demo output. diff --git a/README.md b/README.md index 3982899..7479633 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,12 @@ npx -y @spawn-dock/create@beta --token [project-dir] - `pnpm run publish:github-pages` exports the app and deploys it to GitHub Pages. - `pnpm run start` starts the production Next.js server. +## Agent Guidance + +- `AGENTS.md` contains the base system instructions for AI agents working inside this template. +- Agents should treat `pnpm run dev` as the main local flow because it starts both Next.js and the SpawnDock dev tunnel. +- Use `pnpm run dev:next` only when you explicitly want the local app server without the tunnel. + ## SpawnDock Flow The starter expects a bootstrap step that writes `spawndock.config.json` and From 9861700e29d466db763bf01488cdb3a8c7378123 Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Wed, 25 Mar 2026 22:23:34 +0000 Subject: [PATCH 2/2] feat(template): add TMA knowledge skill and agent memory --- .agents/skills/tma-knowledge-search/SKILL.md | 35 ++++ .../tma-knowledge-search/agents/openai.yaml | 7 + .../tma-knowledge-search/references/api.md | 44 +++++ .../scripts/search_tma_knowledge.py | 183 ++++++++++++++++++ AGENTS.md | 8 + CLAUDE.md | 6 + README.md | 5 + spawndock.config.json | 3 +- 8 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 .agents/skills/tma-knowledge-search/SKILL.md create mode 100644 .agents/skills/tma-knowledge-search/agents/openai.yaml create mode 100644 .agents/skills/tma-knowledge-search/references/api.md create mode 100755 .agents/skills/tma-knowledge-search/scripts/search_tma_knowledge.py create mode 100644 CLAUDE.md diff --git a/.agents/skills/tma-knowledge-search/SKILL.md b/.agents/skills/tma-knowledge-search/SKILL.md new file mode 100644 index 0000000..b5afa1c --- /dev/null +++ b/.agents/skills/tma-knowledge-search/SKILL.md @@ -0,0 +1,35 @@ +--- +name: tma-knowledge-search +description: Search the SpawnDock TMA knowledge API for Telegram Mini App and SpawnDock-specific implementation guidance. Use when Codex needs authoritative TMA workflow details, Telegram WebApp API usage, SpawnDock TMA template behavior, or wants to verify how a feature should be built for Telegram Mini Apps before answering or coding. +--- + +# TMA Knowledge Search + +Use this skill when local repo context is not enough for a Telegram Mini App question and the answer should come from the SpawnDock TMA knowledge base. + +## Workflow + +1. Form a focused English query about the TMA implementation detail you need. +2. Run `scripts/search_tma_knowledge.py ""`. +3. Read the returned `answer` first, then inspect any `sources`. +4. Use the API result as the primary TMA-specific reference in your answer or implementation plan. + +## Query Rules + +- Prefer English queries even if the user writes in another language. +- Ask about one concrete problem at a time. +- Include key TMA terms in the query: `Telegram Mini App`, `WebApp`, `MainButton`, `theme`, `viewport`, `SpawnDock`, `Next.js template`, and similar domain words when relevant. +- Re-query with a narrower prompt if the first result is generic. +- Avoid unnecessary repeat calls: the endpoint can rate-limit quickly on the free tier. + +## Output Handling + +- Treat the API response as TMA-specific guidance, not as a generic web best-practices source. +- If the API returns no useful sources, say that clearly and fall back to repo code or official Telegram docs as needed. +- Keep citations lightweight: mention the knowledge API result and summarize the relevant guidance rather than dumping raw JSON. + +## Resources + +- `scripts/search_tma_knowledge.py`: sends the POST request and prints a readable summary or raw JSON. +- The script automatically uses `SPAWNDOCK_API_TOKEN`, `API_TOKEN`, or the nearest `spawndock.config.json` `apiToken` when available. +- `references/api.md`: request and response contract for the knowledge endpoint. diff --git a/.agents/skills/tma-knowledge-search/agents/openai.yaml b/.agents/skills/tma-knowledge-search/agents/openai.yaml new file mode 100644 index 0000000..56958bd --- /dev/null +++ b/.agents/skills/tma-knowledge-search/agents/openai.yaml @@ -0,0 +1,7 @@ +interface: + display_name: "TMA Knowledge Search" + short_description: "Search TMA implementation knowledge" + default_prompt: "Use $tma-knowledge-search to look up Telegram Mini App and SpawnDock TMA implementation details before answering." + +policy: + allow_implicit_invocation: true diff --git a/.agents/skills/tma-knowledge-search/references/api.md b/.agents/skills/tma-knowledge-search/references/api.md new file mode 100644 index 0000000..83e555c --- /dev/null +++ b/.agents/skills/tma-knowledge-search/references/api.md @@ -0,0 +1,44 @@ +# SpawnDock TMA Knowledge API + +Use this endpoint when you need Telegram Mini App or SpawnDock-specific implementation guidance: + +```bash +curl -X POST \ + 'https://spawn-dock.w3voice.net/knowledge/api/v1/search' \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "How do I use MainButton in a Telegram Mini App?", + "locale": "en" + }' +``` + +## Request + +- Method: `POST` +- URL: `https://spawn-dock.w3voice.net/knowledge/api/v1/search` +- Content-Type: `application/json` +- Body fields: + - `query` string, required + - `locale` string, optional in practice for the script, default `en` + +## Observed response shape + +```json +{ + "answer": "Human-readable answer", + "sources": [], + "meta": { + "locale_requested": "en" + } +} +``` + +## Notes + +- The skill defaults to `locale=en`. +- `Authorization: Bearer ` is optional and enables the higher-tier limits when the token is valid. +- SpawnDock bootstrap can write that token into `spawndock.config.json` as `apiToken` and into `.env.local` as `SPAWNDOCK_API_TOKEN`. +- The API can return an empty `sources` array. +- Use the answer as TMA-specific guidance, then inspect `sources` when present. +- The endpoint can return `429 rate_limit exceeded (minute)` after a small number of requests on the free tier. diff --git a/.agents/skills/tma-knowledge-search/scripts/search_tma_knowledge.py b/.agents/skills/tma-knowledge-search/scripts/search_tma_knowledge.py new file mode 100755 index 0000000..ecedfec --- /dev/null +++ b/.agents/skills/tma-knowledge-search/scripts/search_tma_knowledge.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +import argparse +import json +import os +import sys +import time +import urllib.error +import urllib.request +from pathlib import Path + + +API_URL = "https://spawn-dock.w3voice.net/knowledge/api/v1/search" + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Query the SpawnDock TMA knowledge API." + ) + parser.add_argument("query", help="Knowledge search query") + parser.add_argument("--locale", default="en", help="Response locale (default: en)") + parser.add_argument( + "--api-token", + help="Optional Bearer token override. Defaults to SPAWNDOCK_API_TOKEN/API_TOKEN or spawndock.config.json", + ) + parser.add_argument( + "--config", + help="Optional path to spawndock.config.json. Defaults to the nearest config found from cwd upward.", + ) + parser.add_argument( + "--timeout", + type=float, + default=20.0, + help="HTTP timeout in seconds (default: 20)", + ) + parser.add_argument( + "--raw", + action="store_true", + help="Print raw JSON response instead of a formatted summary", + ) + parser.add_argument( + "--retries", + type=int, + default=2, + help="Retry count for transient HTTP 5xx failures (default: 2)", + ) + return parser.parse_args() + + +def find_config_path(explicit_path: str | None) -> Path | None: + if explicit_path: + path = Path(explicit_path).expanduser() + return path if path.is_file() else None + + for base in [Path.cwd(), *Path.cwd().parents]: + candidate = base / "spawndock.config.json" + if candidate.is_file(): + return candidate + + return None + + +def read_config_api_token(config_path: Path | None) -> str | None: + if config_path is None: + return None + + try: + data = json.loads(config_path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError): + return None + + token = data.get("apiToken") + return token.strip() if isinstance(token, str) and token.strip() else None + + +def resolve_api_token(cli_token: str | None, config_path: Path | None) -> str | None: + if cli_token and cli_token.strip(): + return cli_token.strip() + + for key in ("SPAWNDOCK_API_TOKEN", "API_TOKEN"): + value = os.environ.get(key, "").strip() + if value: + return value + + return read_config_api_token(config_path) + + +def request_knowledge( + query: str, + locale: str, + timeout: float, + retries: int, + api_token: str | None, +) -> dict: + payload = json.dumps({"query": query, "locale": locale}).encode("utf-8") + for attempt in range(retries + 1): + headers = { + "accept": "application/json", + "content-type": "application/json", + } + if api_token: + headers["authorization"] = f"Bearer {api_token}" + + req = urllib.request.Request( + API_URL, + data=payload, + headers=headers, + method="POST", + ) + try: + with urllib.request.urlopen(req, timeout=timeout) as response: + charset = response.headers.get_content_charset() or "utf-8" + return json.loads(response.read().decode(charset)) + except urllib.error.HTTPError as exc: + if exc.code < 500 or attempt == retries: + raise + time.sleep(min(2**attempt, 5)) + + raise RuntimeError("Unreachable retry loop") + + +def format_response(data: dict) -> str: + lines: list[str] = [] + answer = data.get("answer") + sources = data.get("sources") or [] + meta = data.get("meta") or {} + + lines.append("Answer:") + lines.append(answer if answer else "(empty)") + + if sources: + lines.append("") + lines.append("Sources:") + for idx, source in enumerate(sources, start=1): + if isinstance(source, dict): + title = source.get("title") or source.get("name") or f"Source {idx}" + url = source.get("url") or source.get("href") or "" + snippet = source.get("snippet") or source.get("text") or "" + line = f"{idx}. {title}" + if url: + line += f" - {url}" + lines.append(line) + if snippet: + lines.append(f" {snippet}") + else: + lines.append(f"{idx}. {source}") + + if meta: + lines.append("") + lines.append("Meta:") + lines.append(json.dumps(meta, ensure_ascii=False, sort_keys=True)) + + return "\n".join(lines) + + +def main() -> int: + args = parse_args() + config_path = find_config_path(args.config) + api_token = resolve_api_token(args.api_token, config_path) + try: + data = request_knowledge( + args.query, + args.locale, + args.timeout, + args.retries, + api_token, + ) + except urllib.error.HTTPError as exc: + body = exc.read().decode("utf-8", errors="replace") + print(f"HTTP error: {exc.code}\n{body}", file=sys.stderr) + return 1 + except urllib.error.URLError as exc: + print(f"Request failed: {exc}", file=sys.stderr) + return 1 + + if args.raw: + print(json.dumps(data, ensure_ascii=False, indent=2, sort_keys=True)) + else: + print(format_response(data)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/AGENTS.md b/AGENTS.md index 1562901..d297fc8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,6 +27,14 @@ Your job is to build and improve a production-ready Telegram Mini App in this re - Avoid browser patterns that are fragile inside Telegram WebView: `alert()`, `confirm()`, `prompt()`, `target=\"_blank\"`, `window.open()`, and desktop-first navigation patterns. - Do not introduce `BrowserRouter`-style assumptions for TMA navigation. +## TMA Knowledge Search + +- When local repo context is not enough for a Telegram Mini App or SpawnDock-specific implementation question, use the local `tma-knowledge-search` skill before generic web search. +- In generated projects the local skill lives at `.agents/skills/tma-knowledge-search`. +- Query the skill in English and ask one focused implementation question at a time. +- Read the returned `answer` first, then inspect `sources` when they are present. +- SpawnDock bootstrap also mirrors the same skill into `~/.codex/skills/tma-knowledge-search` when possible so Codex can discover it natively. + ## Dev Flow - `pnpm run dev` is the primary local workflow. It starts the Next.js dev server and the SpawnDock dev tunnel together. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..26af1f2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,6 @@ +# SpawnDock TMA Template + +- Treat this repository as a Telegram Mini App project first, not as a generic web app. +- Prefer `pnpm run dev` for the main local workflow because it starts both Next.js and the SpawnDock dev tunnel. +- When local repo context is not enough for a Telegram Mini App or SpawnDock-specific implementation question, use the local `tma-knowledge-search` skill at `.agents/skills/tma-knowledge-search`. +- Query that skill in English, keep the question focused, and use its answer before falling back to generic web guidance. diff --git a/README.md b/README.md index 7479633..eaf6269 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,11 @@ npx -y @spawn-dock/create@beta --token [project-dir] ## Agent Guidance - `AGENTS.md` contains the base system instructions for AI agents working inside this template. +- `CLAUDE.md` provides Claude Code project memory and points it to the local TMA knowledge skill. +- `.agents/skills/tma-knowledge-search` contains the local Telegram Mini App / SpawnDock knowledge-search skill used by compatible agents. - Agents should treat `pnpm run dev` as the main local flow because it starts both Next.js and the SpawnDock dev tunnel. - Use `pnpm run dev:next` only when you explicitly want the local app server without the tunnel. +- SpawnDock bootstrap also mirrors the same skill into `~/.codex/skills/tma-knowledge-search` when preparing a local Codex setup. ## SpawnDock Flow @@ -48,7 +51,9 @@ ready to connect to `@spawn-dock/mcp` via `/mcp/sse`. ## Local Config - `spawndock.config.json` contains preview/runtime data for the app. +- `spawndock.config.json` can also include `apiToken` for the TMA knowledge-search skill. - `spawndock.dev-tunnel.json` contains tunnel connection data. +- `.env.local` can include `SPAWNDOCK_API_TOKEN` for the same knowledge-search access. - `opencode.json` is generated during bootstrap for OpenCode. - `.mcp.json` is generated during bootstrap for Claude Code. - `spawndock/mcp.mjs` resolves `mcpServerUrl` from `spawndock.config.json`. diff --git a/spawndock.config.json b/spawndock.config.json index 2860473..5a246e7 100644 --- a/spawndock.config.json +++ b/spawndock.config.json @@ -10,5 +10,6 @@ "previewHost": "", "localPort": 3000, "mcpServerUrl": "", - "mcpApiKey": "" + "mcpApiKey": "", + "apiToken": "" }