Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Nested swiss-knife reference for creating and operating a headless LingTai bot project without opening the TUI. Start from `lingtai-tui spawn`, then apply the relevant addon/MCP wiring, keep secrets in sidecar files, copy preset policy by referencing existing presets instead of copying preset JSON files, refresh or relaunch, and verify the bot safely. The bundled helper currently implements the Telegram MCP case.
.claude/skills/lingtai-ai-headless-bot/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 68% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 107% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 68% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 84% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 13% | 0% |
Use this reference when a human asks for a LingTai-backed bot that should run as its own project without stepping through the interactive TUI. The pattern is channel-agnostic:
lingtai-tui spawn.init.json.manifest.preset) byreferencing the intended saved/template presets.
Do not fabricate .lingtai/ by hand. The supported bootstrap entrypoint remains lingtai-tui spawn. The helper bundled here currently provisions Telegram bots; future helpers for other chat surfaces should live under this same generic reference rather than creating channel-specific top-level skill names.
Common inputs:
PROJECT_DIR: new project directory. It must not already contain .lingtai/.PRESET: saved or template preset name for the initial lingtai-tui spawn.AGENT_NAME: agent directory/name to create under .lingtai/.LANGUAGE: one of en, zh, or wen.init.json if the new bot should inherit the samemanifest.preset policy as an existing orchestrator.
Telegram-specific inputs:
TELEGRAM_BOT_TOKEN: bot token from @BotFather. Keep it in the environmentor a local prompt, never in shell history, logs, git, issues, or docs.
ALLOWED_USERS: comma-separated Telegram numeric user IDs allowed to talk tothe bot.
Run the bundled helper. Use the installed-bundle path ~/.lingtai-tui/utilities/swiss-knife/reference/headless-bot/scripts/create_telegram_bot_project.py, or the source-checkout path tui/internal/preset/skills/swiss-knife/reference/headless-bot/scripts/create_telegram_bot_project.py:
bashread -rsp 'Telegram bot token: ' TELEGRAM_BOT_TOKEN; export TELEGRAM_BOT_TOKEN; echo python3 <helper-path>/create_telegram_bot_project.py \ --project-dir /path/to/new-project \ --preset minimax \ --agent-name my-bot-agent \ --language en \ --allowed-users 123456789 \ --preset-policy-from /path/to/source-agent/init.json
The helper:
lingtai-tui spawn <dir> --preset <name> --agent-name <name> --language <code>.<agent_dir>/.secrets/telegram.json with mode 0600.telegram to top-level addons.mcp.telegram using the local LingTai runtime Python andinvokes lingtai.mcp_servers.telegram with args: ["-m", "lingtai.mcp_servers.telegram"]; it passes LINGTAI_TELEGRAM_CONFIG=.secrets/telegram.json.
manifest.preset from --preset-policy-from into the newagent's init.json without copying any preset JSON files.
<agent_dir>/.refresh so a running agent reloads the updated config.If refresh is not enough for the installed runtime, relaunch or CPR the agent.
The helper intentionally reads TELEGRAM_BOT_TOKEN from the environment instead of a command-line flag, because argv can be captured by shell history and process monitors. Its output redacts token-like values. The --preset-policy-from flag is optional; use it when the bot should inherit an existing agent's preset access policy.
Headless bots often need access to the same model/preset choices as an existing orchestrator. Do not grant that access by manually copying saved preset JSON files into the new project's .lingtai/presets/saved/ directory. That creates stale, duplicated configuration and can diverge from the user's real saved presets.
Instead, patch the new agent's init.json so its manifest.preset block points to the intended saved or template presets. If the bot should mirror an existing agent, copy only that source agent's manifest.preset policy block:
bashpython3 - <<'PY' import json from pathlib import Path source_init = Path('/path/to/source-agent/init.json') target_init = Path('/path/to/new-project/.lingtai/<agent-name>/init.json') source = json.loads(source_init.read_text()) target = json.loads(target_init.read_text()) preset_policy = source.get('manifest', {}).get('preset') if not isinstance(preset_policy, dict): raise SystemExit(f'{source_init} has no manifest.preset policy object') target.setdefault('manifest', {})['preset'] = preset_policy target_init.write_text(json.dumps(target, indent=2, ensure_ascii=False) + '\n') PY
Expected shape:
json{ "manifest": { "preset": { "active": "~/.lingtai-tui/presets/saved/codex.json", "default": "~/.lingtai-tui/presets/saved/codex.json", "allowed": [ "~/.lingtai-tui/presets/saved/codex.json", "~/.lingtai-tui/presets/saved/minimax_cn.json" ] } } }
Keep this separate from secrets and MCP credentials. After changing the preset policy, refresh or restart the agent and confirm the active preset in .agent.json or the runtime logs.
Use this when you need to inspect or adapt each step. Replace placeholders, but never paste a real token into committed files or chat transcripts.
bashlingtai-tui spawn "$PROJECT_DIR" \ --preset "$PRESET" \ --agent-name "$AGENT_NAME" \ --language "$LANGUAGE"
The command prints JSON containing agent_dir. Treat that as the only supported source for the agent path.
Create the sidecar secret:
json{ "accounts": [ { "alias": "main", "bot_token": "<redacted-telegram-bot-token>", "allowed_users": [123456789] } ], "poll_interval": 1.0 }
Save it as:
text<agent_dir>/.secrets/telegram.json
Then set restrictive permissions:
bashchmod 700 "<agent_dir>/.secrets" chmod 600 "<agent_dir>/.secrets/telegram.json"
Patch <agent_dir>/init.json so these top-level keys exist:
json{ "addons": ["telegram"], "mcp": { "telegram": { "type": "stdio", "command": "~/.lingtai-tui/runtime/venv/bin/python", "args": ["-m", "lingtai.mcp_servers.telegram"], "env": { "LINGTAI_TELEGRAM_CONFIG": ".secrets/telegram.json" } } } }
If addons or mcp already exists, merge instead of replacing unrelated entries. The command should point to the local LingTai runtime Python. On unusual installs, resolve it with:
bashpython3 - <<'PY' from pathlib import Path print(Path.home() / ".lingtai-tui" / "runtime" / "venv" / "bin" / "python") PY
After writing init.json and channel secret files, refresh the running agent:
bashtouch "<agent_dir>/.refresh"
If the listener does not appear in logs within a minute, inspect process state before starting anything else:
bashlingtai-tui list "$PROJECT_DIR"
If the agent is stuck in a relaunch/dead loop, use the runtime lifecycle controls available in your environment (for example suspend/CPR from an orchestrator) or relaunch from the project. Do not start a second copy of the same agent without checking lingtai-tui list first.
Run these checks before handing the bot to a user:
lingtai-tui list "$PROJECT_DIR" shows the new agent and no duplicate copy.getMe succeeds. Redact the token in commands and logs:bashpython3 - <<'PY' import json, os, urllib.request token = os.environ["TELEGRAM_BOT_TOKEN"] with urllib.request.urlopen(f"https://api.telegram.org/bot{token}/getMe", timeout=15) as r: data = json.load(r) print(json.dumps({"ok": data.get("ok"), "result": data.get("result", {})}, indent=2)) print("token: <redacted>") PY
bot token.
init.json, .agent.json,or the logs after refresh/relaunch.
/refresh, /doctor, and asimple chat prompt.
/start to the bot and confirm theagent responds.
.secrets/telegram.json, .env, screenshots, terminal captures,issue comments, or summaries containing a real bot token.
allowed_users; an omitted allowlist makes the bot reachable by anyonewho discovers it.
@BotFather immediately if it appears in logs, githistory, chat, crash reports, or process arguments.
<redacted-telegram-bot-token> in examples. Do not userealistic token-shaped dummy strings.
0600 and directories mode 0700 on sharedmachines.
Other measured skills in the registry, with their headline benchmark lift.