skills/ai-skel-ts-usage/SKILL.md
Scaffold a complete AI agent application skeleton with LLM integration, tool calling, observability, cost tracking, session management, and content fetching using @korchasa/ai-skel-ts. Use when the user asks to create an AI agent, add LLM integration, scaffold an AI application, or mentions "ai-skel", "agent skeleton", or "AI scaffold". Works with any programming language.
npx skillsauth add korchasa/ai-skel-ts ai-skel-ts-usageInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Generates a production-grade AI agent skeleton directly in the target project. The scaffold follows battle-tested patterns from the ai-skel-ts reference implementation.
The skeleton consists of 10 modules organized into 4 layers:
+---------------------------------------------------+
| Agent Layer |
| Agent (stateful conversation + tool dispatch) |
+----------------+----------------+-----------------+
| LLM Layer | Tools Layer | Session Layer |
| LLM Request | MCP Client | History Compact|
| (retry, | (protocol | (trim or |
| validate, | bridge, | summarize, |
| observe) | namespace) | tool-pair |
| | | consistency) |
+----------------+----------------+-----------------+
| Observability Layer |
| Logger | Cost Tracker | Run Context | Debug I/O |
+---------------------------------------------------+
| Content Layer |
| Local Fetcher | Brave Search | Jina Scraper |
+---------------------------------------------------+
When generating the scaffold, follow these steps:
ai package)openai, anthropic, google-generativeai, or litellmgithub.com/sashabaranov/go-openai or provider SDKsasync-openai or provider SDKsGenerate modules in this order (each depends only on previously generated ones):
For each module, generate at minimum:
Create a main entry point (mod.ts / main.py / main.go / etc.) that re-exports public API.
Each module below includes: purpose, key interfaces, algorithm, and critical implementation details. For full reference implementations in TypeScript, see the reference files.
Purpose: Structured logging with levels, context, and timestamps.
Interface:
(context: string, logLevel: "debug"|"info"|"warn"|"error")debug(msg, meta?), info(msg, meta?), warn(msg, meta?), error(msg, error?)createFromLevelString(context, levelString) — validates level, warns and falls back to "debug"Algorithm: Compare numeric level values (debug=0, info=1, warn=2, error=3). Log only if message level >= configured level.
Key detail: Error objects need special serialization (JSON.stringify(Error) returns "{}"). Extract name, message, stack explicitly.
Reference: reference-observability.md
Purpose: Accumulate LLM token usage and costs across requests.
Interface:
addCost(cost: number) — adds USD cost, increments request counteraddTokens(inputTokens, outputTokens) — accumulates token countsgetReport() → {totalCost, totalInputTokens, totalOutputTokens, totalTokens, requestCount}getFormattedReport() → human-readable stringreset() — zeroes all countersPattern: Singleton (for global tracking) OR instance (for per-run tracking). Provide both options.
Reference: reference-observability.md
Purpose: Immutable execution context with unique run ID and debug artifact management.
Interface:
RunContext { runId, debugDir, logger, startTime, saveDebugFile() }createRunContext(logger, debugDir, runId?) — auto-generates reverse-sortable ID if not providedgetSubDebugDir(ctx, stageDir) — returns subdirectory path for a stagesafeSanitize(obj) — recursively handles Error, Buffer, circular refs for serializationCritical algorithm — Reverse-sortable Run ID:
maxMs = Date.UTC(9999, 11, 31, 23, 59, 59, 999)
reversedMs = maxMs - Date.now()
runId = toISO(reversedMs) + microSequence
This makes newest runs sort first in file listings.
Reference: reference-core.md
Purpose: Provider-agnostic LLM interface with retry, validation, self-correction, and observability.
This is the most complex module. Read reference-core.md carefully.
Interface:
ModelURI — parses protocol://provider/model?params (e.g., chat://openai/gpt-4o?timeout=60000)createLlmRequester(modelUri, logger, costTracker, ctx) → requester function(messages, identifier, schema?, tools?, maxSteps?, stageName, settings?) → GenerateResultGenerateResult { result, text, toolCalls, toolResults, newMessages, steps, estimatedCost, inputTokens, outputTokens, validationError?, rawResponse? }Core algorithm (retry loop with self-correction):
parse URI → create provider instance → return requester function
requester(messages, schema, tools, ...):
write initial YAML log file
for attempt = 1..MAX_RETRIES(3):
set timeout via AbortController
call LLM SDK (generateText / chat completion)
on success:
track cost/tokens
aggregate newMessages from steps
update YAML log
return result
on validation error (schema mismatch):
append assistant message (raw response)
append user message (error description) <- SELF-CORRECTION
update YAML log
retry
on fatal API error (401/403/400):
return error immediately (no retry)
on transient error:
exponential backoff: delay = 1000ms * 2^(attempt-1) + jitter(20%)
retry
Key details:
{PROVIDER}_API_KEY)Reference: reference-core.md
Purpose: Manage conversation context window by trimming or summarizing history.
Interface:
HistoryCompactor { compact(messages) → messages, estimateSymbols(message) → number }SimpleHistoryCompactor(maxSymbols) — trims oldest messagesSummarizingHistoryCompactor(maxSymbols, summaryTokenThreshold, summaryGenerator) — LLM-poweredAlgorithm (SimpleHistoryCompactor):
trimBySymbolLimit:
iterate messages from newest to oldest
accumulate symbol weight (JSON.stringify length)
stop when budget exceeded
ensureToolConsistency:
collect all tool-call IDs and tool-result IDs
remove messages with orphaned tool-calls (no matching result)
remove messages with orphaned tool-results (no matching call)
Algorithm (SummarizingHistoryCompactor):
estimate token count (symbols / 4)
if under threshold → delegate to SimpleHistoryCompactor
if over threshold:
split: messages[0..splitPoint] → summarize, messages[splitPoint..] → keep
splitPoint = index before last assistant message
call SummaryGenerator.generateSummary(toSummarize) → summary message
ensure proper message alternation (add dummy user message if needed)
apply tool consistency check
on error → fallback to SimpleHistoryCompactor
Reference: reference-session.md
Purpose: Bridge between Model Context Protocol servers and the LLM tool interface.
Interface:
McpClientWrapper(config, logger, name) — config is either {type:"stdio", command, args} or {type:"sse", url}connect() / disconnect() — lifecycle managementgetTools() → Record<string, Tool> — discovers and converts toolsKey details:
serverName__toolName) to prevent collisionsjsonSchema() helper if available)tools/call, extract text content from response partsReference: reference-session.md
Purpose: Stateful conversation runner with tool integration and history management.
Interface:
Agent(llm, mcpClients?, ctx, systemPrompt?, compactor?, tools?)init() — connects MCP clients, aggregates all toolsrun(input) → GenerateResult — full access to resultschat(input) → string — convenience wrappergetHistory() → messages arrayAlgorithm:
constructor:
store params
if systemPrompt: push system message to history
init:
for each mcpClient:
connect()
getTools() → merge into this.tools
run(input):
push user message to history
if compactor: compact history
call llm(messages, tools, maxSteps=10, ...)
if error: throw
append all newMessages to history
return result
chat(input):
return run(input).text
Reference: reference-core.md
Three content acquisition strategies. Add only what the project needs.
Local Fetcher: HTML → clean text + metadata via Readability algorithm Brave Search: REST API client with 429 retry and rate-limited batch search Jina Scraper: Dual-endpoint (search + reader) API client with rich options
Reference: reference-fetchers.md
When adapting patterns to a target language:
| Concept | TypeScript | Python | Go | Rust |
|---------|-----------|--------|-----|------|
| Interfaces | interface / type | Protocol / ABC | interface | trait |
| Generics | <T> | Generic[T] | [T any] | <T> |
| Async | async/await | async/await | goroutines/channels | async/await |
| Error handling | try/catch + types | try/except + types | error return | Result<T, E> |
| Dependency injection | constructor params | constructor / __init__ | struct fields | struct fields |
| Singleton | static instance | module-level / __new__ | sync.Once | once_cell::Lazy |
| Schema validation | Zod | Pydantic | struct tags + validator | serde + validator |
| URI parsing | URL class | urllib.parse | net/url | url crate |
Adapt to the project's conventions. Suggested structure:
{src_dir}/
+-- agent/
| +-- agent.{ext}
| +-- agent_test.{ext}
+-- llm/
| +-- requester.{ext}
| +-- model_uri.{ext}
| +-- requester_test.{ext}
+-- run_context/
| +-- context.{ext}
| +-- context_test.{ext}
+-- cost_tracker/
| +-- tracker.{ext}
| +-- tracker_test.{ext}
+-- logger/
| +-- logger.{ext}
| +-- logger_test.{ext}
+-- session/
| +-- compactor.{ext}
| +-- summary_generator.{ext}
| +-- compactor_test.{ext}
+-- mcp/ # optional
| +-- client.{ext}
+-- fetchers/ # optional
| +-- local/
| +-- brave/
| +-- jina/
+-- mod.{ext} # public API entry point
delay = baseDelay * 2^(attempt-1) + jitterprotocol://provider/model?paramsAfter generating the scaffold, verify:
testing
Create structured specification for large features using phased decomposition. Produces documents/spec-{name}.md with dependency-ordered phases, atomic tasks, explicit boundaries, and per-phase status tracking.
documentation
Guidelines for writing comprehensive Product Requirements Documents (PRD)
development
How to write in informational style
development
How to write tasks using GODS framework