> ## Documentation Index
> Fetch the complete documentation index at: https://thinkwell.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Plan

> The fluent API for building prompts.

The `Plan` class provides a fluent interface for constructing prompts. You obtain a `Plan` by calling `agent.think()` or `session.think()`, then chain methods to build up your prompt content before executing with `.run()`.

<Note>
  `Plan` was previously named `ThinkBuilder`. The old name is still available as a deprecated type alias.
</Note>

## Basic Usage

```typescript theme={null}
import { open } from "thinkwell";

/** @JSONSchema */
interface Summary {
  title: string;
  points: string[];
}

const agent = await open('claude');

const result = await agent
  .think(Summary.Schema)      // Start building with output schema
  .text("Summarize this:")    // Add prompt text
  .quote(documentContent)     // Add quoted content
  .run();                     // Execute and get typed result

agent.close();
```

## Content Methods

These methods add content to your prompt.

### `.text(content)`

Adds literal text to the prompt.

```typescript theme={null}
.text("Analyze the following code for potential bugs.")
```

### `.textln(content)`

Adds text with a trailing newline. Useful when building prompts incrementally.

```typescript theme={null}
.textln("First, identify the main function.")
.textln("Then, trace the data flow.")
```

### `.quote(content, label?)`

Adds content wrapped in XML-style tags. Use this for user input, documents, or any content that should be clearly delimited from instructions.

```typescript theme={null}
// Without label
.quote(userInput)

// With label for clarity
.quote(customerFeedback, "feedback")
.quote(companyPolicy, "policy document")
```

The label helps the AI understand what the quoted content represents.

### `.code(content, language?)`

Adds content as a fenced Markdown code block. Use this when including source code in your prompt.

```typescript theme={null}
// Without language hint
.code(sourceCode)

// With language for syntax context
.code(jsCode, "javascript")
.code(pythonCode, "python")
```

## Tool Methods

Tools allow the AI agent to call back into your code during prompt execution.

### `.tool(name, description, handler)` - Simple Form

Register a tool that takes no input parameters.

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

### `.tool(name, description, inputSchema, handler)` - With Input Schema

Register a tool with typed, validated input. Define the input shape using an interface with `@JSONSchema`:

```typescript theme={null}
/** @JSONSchema */
interface SearchInput {
  /** Glob pattern to match files */
  pattern: string;
  /** Maximum number of results */
  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) };
  }
)
```

The schema serves three purposes:

1. Tells the AI how to format tool calls
2. Validates incoming calls at runtime
3. Provides TypeScript typing for your handler

## Skill Methods

Skills let you give the agent reusable, self-contained capabilities following the [Agent Skills standard](https://agentskills.io/). Skills support progressive disclosure — only metadata is loaded initially, with full instructions loaded on demand when the agent activates a skill.

### `.skill(path)` - Stored Skill

Load a skill from a `SKILL.md` file on disk.

```typescript theme={null}
.skill("./skills/code-review")
```

The file is parsed at `run()` time. The skill directory must contain a `SKILL.md` with YAML frontmatter:

```yaml theme={null}
---
name: code-review
description: Reviews code for bugs, style issues, and best practices.
---

# Code Review

## Steps
1. Read the files to review
2. Identify bugs, style issues, and improvement opportunities
...
```

Reference files and assets in the skill directory can be accessed via the `read_skill_file` tool.

### `.skill(definition)` - Virtual Skill

Define a skill programmatically without filesystem artifacts.

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

## Steps
1. Analyze the function signature and behavior
2. Generate comprehensive test 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 };
    },
  }],
})
```

Virtual skills can include handler functions via the `tools` array. These are invoked through the `call_skill_tool` dispatcher — they don't appear as individual MCP tools, preserving progressive disclosure.

### Skill Name Rules

Skill names must follow the Agent Skills spec:

* 1-64 characters
* Lowercase alphanumeric plus hyphens
* No leading, trailing, or consecutive hyphens

### Multiple Skills

You can attach multiple skills to a single prompt:

```typescript theme={null}
const result = await agent
  .think(OutputSchema)
  .skill("./skills/code-review")
  .skill({
    name: "test-writer",
    description: "Generates unit tests.",
    body: "...",
  })
  .text("Review the auth module and write tests for any issues found")
  .run();
```

## Configuration Methods

### `.cwd(path)`

Sets the working directory for the session. This affects where the agent looks for files when using its built-in tools.

```typescript theme={null}
.cwd("/path/to/project")
```

## Execution

### `.run()`

Executes the prompt and returns the typed result. This is always the final method in the chain.

```typescript theme={null}
const result = await agent
  .think(OutputSchema)
  .text("Your prompt here")
  .run();
// result is typed according to OutputSchema
```

### `.stream()`

Executes the prompt and returns a `ThoughtStream` — a handle providing both the final typed result and an async iterable of intermediate progress events.

```typescript theme={null}
const stream = agent
  .think(OutputSchema)
  .text("Analyze this codebase")
  .stream();

// Iterate over events as they arrive
for await (const event of stream) {
  if (event.type === "thought") {
    process.stderr.write(event.text);
  }
}

// Get the final result
const result = await stream.result;
```

Execution begins eagerly when `stream()` is called — you don't need to iterate to start the operation.

## ThoughtStream

The `ThoughtStream<Output>` class provides access to streaming events during prompt execution.

### Properties

#### `.result`

A `Promise<Output>` that resolves with the final typed result.

```typescript theme={null}
const stream = agent.think(schema).text("...").stream();
const result = await stream.result;
```

### Async Iteration

`ThoughtStream` implements `AsyncIterable<ThoughtEvent>`, allowing you to iterate over events with `for await`:

```typescript theme={null}
for await (const event of stream) {
  switch (event.type) {
    case "thought":
      ui.updateThinking(event.text);
      break;
    case "tool_start":
      ui.showToolActivity(event.title, event.kind);
      break;
    case "tool_done":
      ui.clearToolActivity(event.id);
      break;
    case "plan":
      ui.renderPlan(event.entries);
      break;
  }
}
```

### Execution Semantics

The async iterator and result promise have independent lifecycles:

* **Iterate without awaiting `.result`** — the result resolves in the background
* **Await `.result` without iterating** — events buffer internally
* **Do both concurrently** — iterate and await simultaneously
* **Early termination** — break out of the loop and still await `.result`

```typescript theme={null}
const stream = agent.think(schema).text("...").stream();

for await (const event of stream) {
  if (event.type === "thought") {
    process.stderr.write(event.text);
  }
  if (someCondition) break; // Early exit is safe
}

// Result is still available after breaking
const result = await stream.result;
```

## ThoughtEvent Types

Events emitted during streaming are represented as a discriminated union:

### `thought`

Streaming internal reasoning / chain-of-thought from the agent.

```typescript theme={null}
{ type: "thought"; text: string }
```

### `message`

Streaming visible response text from the agent.

```typescript theme={null}
{ type: "message"; text: string }
```

### `tool_start`

Emitted when the agent starts using a tool.

```typescript theme={null}
{ type: "tool_start"; id: string; title: string; kind?: ToolKind }
```

The `kind` field indicates the category of tool: `"read"`, `"edit"`, `"delete"`, `"move"`, `"search"`, `"execute"`, `"think"`, `"fetch"`, `"switch_mode"`, or `"other"`.

### `tool_update`

Emitted during tool execution with progress or intermediate content.

```typescript theme={null}
{ type: "tool_update"; id: string; status: string; content?: ToolContent[] }
```

The `content` array may include:

* `{ type: "content"; content: ContentBlock }` — text, image, or resource link
* `{ type: "diff"; path: string; oldText: string; newText: string }` — file diff
* `{ type: "terminal"; terminalId: string }` — terminal output reference

### `tool_done`

Emitted when a tool completes.

```typescript theme={null}
{ type: "tool_done"; id: string; status: "completed" | "failed" }
```

### `plan`

Emitted when the agent shares its execution plan.

```typescript theme={null}
{ type: "plan"; entries: PlanEntry[] }
```

Each `PlanEntry` has:

* `content: string` — description of the plan step
* `status: "pending" | "in_progress" | "completed"`
* `priority: "high" | "medium" | "low"`

## Complete Example

Here's a complete example showing multiple Plan features:

```typescript theme={null}
import { open } from "thinkwell";
import * as fs from "fs/promises";

/**
 * Result of analyzing a codebase.
 * @JSONSchema
 */
interface CodeAnalysis {
  /** Main programming language detected */
  language: string;
  /** List of potential issues */
  issues: Array<{
    file: string;
    line: number;
    description: string;
    severity: "low" | "medium" | "high";
  }>;
  /** Summary of the codebase */
  summary: string;
}

/**
 * Input for reading a file.
 * @JSONSchema
 */
interface ReadFileInput {
  /** Path to the file to read */
  path: string;
}

async function analyzeProject(projectPath: string) {
  const agent = await open('claude');

  try {
    const analysis = await agent
      .think(CodeAnalysis.Schema)
      .cwd(projectPath)
      .text(`
        Analyze this codebase for potential issues.
        Use the read_file tool to examine source files.
        Focus on bugs, security issues, and code smells.
      `)
      .tool(
        "read_file",
        "Read the contents of a file in the project.",
        ReadFileInput.Schema,
        async (input) => {
          const content = await fs.readFile(input.path, "utf-8");
          return { content };
        }
      )
      .tool(
        "list_files",
        "List all files in the project directory.",
        async () => {
          const files = await fs.readdir(projectPath, { recursive: true });
          return { files };
        }
      )
      .run();

    return analysis;
  } finally {
    agent.close();
  }
}
```

## Method Chaining Order

While most methods can be called in any order, we recommend this sequence for readability:

1. `.think(schema)` - Always first (starts the builder)
2. `.cwd(path)` - Configuration
3. `.skill()` - Skill attachments
4. `.text()` / `.textln()` / `.quote()` / `.code()` - Main prompt instructions
5. `.tool()` - Tool definitions
6. `.run()` or `.stream()` - Always last (executes the prompt)

```typescript theme={null}
const result = await agent
  .think(OutputSchema)               // 1. Schema
  .cwd("/my/project")                // 2. Config
  .skill("./skills/code-review")     // 3. Skills
  .text("Analyze this code:")        // 4. Instructions
  .code(sourceCode, "typescript")
  .tool("helper", "...", handler)    // 5. Tools
  .run();                            // 6. Execute

// Or with streaming:
const stream = agent
  .think(OutputSchema)
  .skill("./skills/code-review")
  .text("Analyze this code:")
  .stream();                         // 6. Execute with streaming
```
