thinkwell

Write TypeScript code using the Thinkwell framework. Covers the @JSONSchema syntax, Plan fluent API, agent lifecycle, tools, skills, thought streams, and the thinkwell CLI.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "thinkwell" with this command: npx skills add dherman/thinkwell/dherman-thinkwell-thinkwell

Thinkwell

Thinkwell is a TypeScript framework for blending deterministic code with LLM-powered reasoning. It provides a fluent Plan API for composing prompts, attaching tools, and getting structured JSON responses.

For detailed API signatures, see references/api-reference.md. For @JSONSchema details, see references/schema-guide.md. For complete working examples, see references/examples.md.

Core Concepts

1. The @JSONSchema Pattern

Annotate an interface with @JSONSchema in a JSDoc comment. Thinkwell auto-generates a TypeName.Schema namespace that provides the JSON schema at runtime:

/**
 * A summary of content.
 * @JSONSchema
 */
export interface Summary {
  /** A brief title */
  title: string;
  /** Key points from the content */
  points: string[];
  /**
   * Word count of the original
   * @minimum 0
   */
  wordCount: number;
}

// Summary.Schema is auto-generated — use it with agent.think()

Works with interfaces, type aliases, enums, and classes. JSDoc comments on properties become descriptions in the generated schema. Annotations like @minimum, @maximum, @minLength, @maxLength, @pattern, and @format map to JSON Schema validation keywords.

2. Agent Lifecycle

import { open } from "thinkwell";

const agent = await open('claude');  // Or: 'codex', 'gemini', 'kiro', 'opencode', 'auggie'
try {
  const result = await agent
    .think(Summary.Schema)           // Start builder with output schema
    .text("Summarize this:")         // Add prompt text
    .quote(content)                  // Add quoted content
    .run();                          // Execute → returns typed Summary
  console.log(result.title);
} finally {
  agent.close();                     // Always close when done
}

The pattern is always: open → think → plan → run → close.

3. Plan Fluent API

agent.think(schema) returns a Plan. Chain methods to compose the prompt, then call .run() or .stream():

Content methods:

  • .text(content) — Add literal text
  • .textln(content) — Add text with trailing newline
  • .quote(content, label?) — Add content in XML-style tags (e.g., <feedback>...</feedback>)
  • .code(content, language?) — Add content as a fenced code block

Tool methods:

  • .tool(name, description, handler) — Register a tool (no input schema)
  • .tool(name, description, inputSchema, handler) — Register a tool with typed input
  • .tool(name, description, inputSchema, outputSchema, handler) — Full form with both schemas
  • .defineTool(...) — Same overloads, but hidden from the prompt text

Skill methods:

  • .skill(path) — Attach a stored skill from a SKILL.md directory
  • .skill({ name, description, body, tools? }) — Attach a virtual skill

Configuration:

  • .cwd(path) — Set the working directory for the session

Execution:

  • .run() — Execute and return the typed result
  • .stream() — Execute and return a ThoughtStream for streaming events + result

4. Tools

Tools let the agent call back into your code. Three overloads:

Simple tool (no input schema):

.tool(
  "current_time",
  "Returns the current date and time.",
  async () => ({
    time: new Date().toLocaleTimeString(),
    date: new Date().toLocaleDateString(),
    timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
  })
)

Tool with typed input:

/** @JSONSchema */
interface SearchInput {
  /** Glob pattern to match files */
  pattern: string;
  /** Maximum results to return */
  limit?: number;
}

.tool(
  "search_files",
  "Search for files matching a glob pattern.",
  SearchInput.Schema,
  async (input) => {
    // input is typed as SearchInput
    const files = await glob(input.pattern);
    return { files: files.slice(0, input.limit ?? 10) };
  }
)

5. Thought Streaming

Use .stream() instead of .run() to get real-time events:

const stream = agent
  .think(Schema)
  .text("Analyze this codebase")
  .stream();

for await (const event of stream) {
  switch (event.type) {
    case "thought":    // Agent's internal reasoning
      process.stderr.write(event.text);
      break;
    case "message":    // Agent's visible response
      process.stdout.write(event.text);
      break;
    case "tool_start": // Tool invocation started
      console.log(`Using tool: ${event.title}`);
      break;
    case "tool_done":  // Tool completed
      break;
    case "plan":       // Agent's execution plan
      for (const entry of event.entries) {
        console.log(`[${entry.status}] ${entry.content}`);
      }
      break;
  }
}

const result = await stream.result;  // Final typed result

The stream and result promise are independent — you can iterate events, await the result, or both.

6. Sessions (Multi-Turn)

For conversations where the agent needs to remember context across calls:

const session = await agent.createSession({ cwd: "/my/project" });

const analysis = await session
  .think(AnalysisSchema)
  .text("Analyze this codebase")
  .run();

// Same session — agent remembers the analysis
const fixes = await session
  .think(FixesSchema)
  .text("Suggest fixes for the top issues")
  .run();

session.close();

Each agent.think() creates an ephemeral session. Use agent.createSession() when you need multi-turn context.

7. Skills

Attach reusable instruction packages to a prompt:

Stored skill (from filesystem):

.skill("./skills/code-review")

Virtual skill (programmatic):

.skill({
  name: "test-writer",
  description: "Generates unit tests for TypeScript functions.",
  body: `
# Test Writer

## Steps
1. Analyze function signatures
2. Generate test cases covering edge cases
3. Use the \`count-assertions\` tool to verify coverage

## Available Tools

### count-assertions
Count assertions in a test file.
Input: \`{ "path": "string" }\`
  `,
  tools: [{
    name: "count-assertions",
    description: "Count assertions in a test file",
    handler: async ({ path }) => {
      const content = await fs.readFile(path, "utf-8");
      const matches = content.match(/expect\(/g) || [];
      return { count: matches.length };
    },
  }],
})

CLI Usage

Running Scripts

# Run a script directly
thinkwell script.ts

# Or use a shebang
#!/usr/bin/env thinkwell

Scripts use standard npm imports (import { open } from "thinkwell").

IDE Support

Generate declaration files for TypeName.Schema autocomplete:

thinkwell types          # One-time generation
thinkwell types --watch  # Watch mode for development

Add *.thinkwell.d.ts to your tsconfig's include array and .gitignore.

Alternatively, install the Thinkwell VSCode extension for automatic IDE support without generating files.

Type Checking

thinkwell check          # Type-check the project (supports @JSONSchema)

Recommended Method Chain Order

const result = await agent
  .think(OutputSchema)               // 1. Schema (always first) → returns Plan
  .cwd("/my/project")                // 2. Configuration
  .skill("./skills/code-review")     // 3. Skills
  .text("Analyze this code:")        // 4. Prompt content
  .code(sourceCode, "typescript")
  .tool("helper", "...", handler)    // 5. Tools
  .run();                            // 6. Execute (always last)

Common Patterns

Prompt-Only (No Tools)

const summary = await agent
  .think(Summary.Schema)
  .text("Summarize the following content:")
  .quote(content)
  .run();

Tool + Streaming

const stream = agent
  .think(Greeting.Schema)
  .text("Create a time-appropriate greeting")
  .tool("current_time", "Get current time", async () => new Date())
  .stream();

for await (const event of stream) {
  if (event.type === 'message') process.stdout.write(event.text);
}
const greeting = await stream.result;

Custom Agent Command

const agent = await open({ cmd: 'my-custom-agent --acp' });

Environment Variable Override

# Override the agent for all scripts
THINKWELL_AGENT=gemini thinkwell script.ts

# Override with a custom command
THINKWELL_AGENT_CMD="my-agent --acp" thinkwell script.ts

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

openclaw-version-monitor

监控 OpenClaw GitHub 版本更新,获取最新版本发布说明,翻译成中文, 并推送到 Telegram 和 Feishu。用于:(1) 定时检查版本更新 (2) 推送版本更新通知 (3) 生成中文版发布说明

Archived SourceRecently Updated
Coding

ask-claude

Delegate a task to Claude Code CLI and immediately report the result back in chat. Supports persistent sessions with full context memory. Safe execution: no data exfiltration, no external calls, file operations confined to workspace. Use when the user asks to run Claude, delegate a coding task, continue a previous Claude session, or any task benefiting from Claude Code's tools (file editing, code analysis, bash, etc.).

Archived SourceRecently Updated
Coding

ai-dating

This skill enables dating and matchmaking workflows. Use it when a user asks to make friends, find a partner, run matchmaking, or provide dating preferences/profile updates. The skill should execute `dating-cli` commands to complete profile setup, task creation/update, match checking, contact reveal, and review.

Archived SourceRecently Updated