> For the complete documentation index, see [llms.txt](https://ascribed-dev.com/docs/llms.txt). This documentation describes Ascribe 0.4.4. Anything marked in main, not yet released isn't in that release yet.

# Editor workspaces, settings, tasks, and commands

> Reference · How the VS Code extension handles a workspace with several projects, Project Settings, its tasks, and its settings and commands.

How the [VS Code extension](https://ascribed-dev.com/docs/editor.md) works in a workspace that holds more than one Ascribe project, the project's own settings, the tasks it runs, and every setting and command it adds.

## Workspaces with several projects

Every `ascribe.toml` in the workspace is a project, with its own language server. A repository that keeps its code and its documentation together can hold several, such as `site/ascribe.toml` and `handbook/ascribe.toml`, and you can work on all of them in one window.

Some folders hold an `ascribe.toml` that isn't a project to work on, and the editor leaves them out on its own:

- **What git ignores.** In a workspace folder that's a git repository, an `ascribe.toml` git ignores isn't a project, such as one a test run writes into a build folder. One git tracks is, whatever the ignore rules say. Without git, or outside a repository, the folders below are still left out.
- **Output, dependencies, and caches,** in every folder with one of these names, whether or not git ignores it: `.ascribe`, each project's outputs and caches; `node_modules`, `bower_components`, `vendor`, `.venv`, `venv`, and `__pypackages__`; and `target`, `dist`, `.output`, `.next`, `.nuxt`, `.svelte-kit`, `.astro`, `.turbo`, `.vercel`, `.netlify`, `.cache`, `coverage`, `.gradle`, `.vscode-test`, `.tox`, and `.nox`. Not `build`, `out`, or `bin`, which are also names of folders people write in: where they're output, git ignores them.
- **A source's copies.** The copies of a [source in another repository](https://ascribed-dev.com/docs/content-model/sources.md#20-sourcesname), `sources/<name>/` in a project's folder, can hold that repository's own `ascribe.toml`: it's code the project shows, not a project.
- **Folders that `files.exclude` hides.**

Anything else whose `ascribe.toml` isn't a project, such as test fixtures, is left out with `ascribe.projects.exclude`: a list of glob patterns, from each workspace folder, in your workspace's settings, such as `"ascribe.projects.exclude": ["tests/**"]`.

A project left out isn't listed in **Switch Project** and gets no language server, so its pages get no diagnostics and no preview. To bring one back, open its folder as a workspace folder of its own (**File > Add Folder to Workspace**): a workspace folder's own `ascribe.toml` is always a project, unless `files.exclude` or `ascribe.projects.exclude` names it. One that `ascribe.projects.exclude` leaves out comes back when you take its pattern out, and one git ignores comes back when git tracks its `ascribe.toml` (`git add -f`).

A file belongs to the nearest `ascribe.toml` above it, and only that project's server reports on it. Projects can be nested: a file in `handbook/internal/` belongs to `handbook/internal/ascribe.toml`, not to `handbook/ascribe.toml`. A project nested in another's content root isn't part of it: the outer project's server, `ascribe check`, and `ascribe build` skip the nested project's folder, so a link or an include from the outer project to one of its pages is reported as a missing file. A file in a project's folder but outside its content root isn't one of the project's sources, and gets no diagnostics.

In the Problems panel, the source of each problem names the project whose server found it, such as `ascribe · handbook`, when the workspace has more than one project.

Each project runs its own `ascribe`, found as [Which `ascribe` it runs](https://ascribed-dev.com/docs/editor.md#which-ascribe-it-runs) describes.

### When servers start

A project's server starts the first time you open one of its Markdown files or its `ascribe.toml`, or preview one of its pages. It also starts when something asks for the project: choosing it in **Switch Project** or the Project view, a command run on it, or **Check Every Build**.

A change on disk starts nothing: a branch switch, a pull, a test run that rewrites files, or an agent that writes files without opening them leaves a project that hasn't started as it was, with no problems in the Problems panel and "not checked yet" in **Switch Project**. Opening the window starts nothing either, and neither does adding an `ascribe.toml`.

Once a server is running, it follows changes on disk to its project's files. A server runs until you close the window or delete its `ascribe.toml` or its folder, and its problems leave the Problems panel with it. A server that doesn't stop when asked is killed, with any process it started, and none outlives the window. A project nothing opens costs nothing.

To start every project's server when the workspace opens, set `ascribe.startServers` to `"all"`.

An `ascribe.toml` you add or delete is picked up as you work. If the editor misses a new one, opening or switching to one of the new project's files picks it up. The extension serves at most 50 projects in a workspace: the first 50 by path. When there are more, the first project's output says so, and that **Switch Project** lists only those 50; `ascribe.projects.exclude` leaves out the folders that aren't projects, beside [those left out on their own](https://ascribed-dev.com/docs/editor/settings.md#workspaces-with-several-projects).

Each server uses about 6 MB of memory, plus about 70 KB per page of its project.

### Commands and output

Commands act on the project that owns the active file: the preview, its **Build** picker, **Switch Build**, review, and **Show Server Output**. The [status bar](https://ascribed-dev.com/docs/editor/sidebar.md#the-status-bar) names that project. The sidebar shows it too, and [Switch Project](https://ascribed-dev.com/docs/editor/sidebar.md#switch-project) lists them all and shows another in the sidebar until a file of another project is active. **Restart Language Server** restarts every project's server that has started. [Commands](https://ascribed-dev.com/docs/editor/settings.md#commands) has the details.

Each project's server logs to its own output channel, `Ascribe (<project>)`. The project's name is its folder relative to the workspace folder, such as `Ascribe (handbook/internal)`, or the workspace folder's name for a project at its root. In a workspace with one project, the channel is just `Ascribe`.

A channel's name is set when the channel is first needed, usually when its project's server first starts. So if a workspace with one project gains a second, the first project's channel keeps the name `Ascribe`, beside `Ascribe (<other project>)`, until you reload the window.

A server's first line names its version and the `ascribe.toml` it uses, as [the language server](https://ascribed-dev.com/docs/editor/language-server.md#the-project-a-server-serves) says.

## Project Settings

**Ascribe: Open Project Settings** opens a tab of the project's own settings: those in its `ascribe.toml`, which everyone working on the project shares, and which the file holds for them all to read, review, and commit. Your editor's settings, the `ascribe.*` ones below, are VS Code's, and the tab links to them.

The tab opens from the Command Palette, from the gear in the [Project view](https://ascribed-dev.com/docs/editor/sidebar.md#the-project-view)'s head, and from the gear in `ascribe.toml`'s title bar. The button is there only on the `ascribe.toml` of a project the extension found: not on one that `ascribe.projects.exclude` leaves out, such as a test fixture's (see [Workspaces with several projects](https://ascribed-dev.com/docs/editor/settings.md#workspaces-with-several-projects)). Each project has one tab, titled with the project's name: opening it again shows the tab already open. Closing and reopening `ascribe.toml` changes nothing, since the tab reads the file from the disk while it isn't open. When `ascribe.toml` is deleted or renamed, the project is gone, so the tab says so, and closes when you click it.

### Finding a setting

The search at the top looks in every setting's name, its key, and its whole description, so a word from any sentence of what a setting does finds it, and so does a check's name or what it reports. Each word you type must be there, in any case. **Set in ascribe.toml** keeps to what the file sets. On the left, the sections list the tables, the parts of the project, and the checks; click one to go to it. A section with nothing that matches is left out of the list. The tab keeps the search, the filter, and where you were while you look at another tab.

### What the tab shows

Each setting shows its name, its key (`[consumer] trailing-slash`), what it does, its default, and where the file sets it, as a link to the line (`ascribe.toml:12`), or that the file leaves it out. A bar beside a setting marks one the file sets, as VS Code's Settings editor marks what's changed. The tab shows every setting that's a switch, a choice, a number, or a short text, under a heading for its table (`[project]`, `[consumer]`, `[editor]`, and the rest), in the order of the [content model reference](https://ascribed-dev.com/docs/content-model.md). A table or a list is the file's to edit, and a part of the project with a name of its own, such as a content type or a build, is a card that opens the file at it.

### Changing a value

The tab changes a value the file sets: choose another, or type one and press Enter. The change is made in `ascribe.toml` as an edit to that one value, unsaved, so the file's comments, blank lines, and order stay as they were, and **Undo** in the file puts it back. Typing in `ascribe.toml` changes the tab too: it always shows the file as the editor has it, unsaved text included, and never an older version of it. A value the [content model](https://ascribed-dev.com/docs/glossary.md#content-model) doesn't take is still set, and the language server's problem shows under the setting, in the words of the Problems panel.

A setting the file leaves out is shown with its default, and changing it adds it to its table, after the table's last setting, or adds the table at the end of the file when it has none. **Reset to default**, beside a setting the file sets, takes its line out of the file, and the comment directly above it with it: a comment that touches the line is about that setting. A comment with a blank line before the setting stays, since it may be about the lines around it too. Both are unsaved edits that **Undo** puts back.

### Content types, builds, and the rest

A content type, a [dimension](https://ascribed-dev.com/docs/glossary.md#dimension), a release target, a build, and a source are tables of their own, which the file holds as text for a reviewer to read. Each is a card under its section, **Content types**, **Dimensions**, **Releases**, **Builds**, or **Sources**, with its name, its table (`[types.guide]`), the keys the file sets in it, and its line. **Edit in ascribe.toml** opens the file at its table. `[frontmatter]`, the fields every content type shares, is a card of its own, first under **Content types**, with the fields it declares. Phrases, features, and glossary terms are added, renamed, and found in the [Content model view](https://ascribed-dev.com/docs/editor/sidebar.md#content-model), which **Open Content model** shows.

### Checks

Under **Checks**, the tab lists every check whose level `[checks]` can set ([Setting a check's level](https://ascribed-dev.com/docs/checks/diagnostics.md#setting-a-checks-level)), the ones the file names first, then the rest in the diagnostics reference's order. Each shows what it reports, its level, and its own level, which it has when the file sets none. `page-size`, `image-large`, `memo-long`, and `summary-long` show their `limit` too, with its default. A level and a limit are set and reset as any setting is, in the form the file writes the check: `page-size = "warning"` in `[checks]`, an inline table, or a `[checks.page-size]` table of its own.

## Tasks

The extension adds a task type, `ascribe`, for the jobs that write files: **Build** runs `ascribe build`, **Sync Agent Files** runs `ascribe agents sync`, and **Observe Examples** runs [`ascribe observe`](https://ascribed-dev.com/docs/examples/tested.md#2-run-the-test-and-commit-what-it-records). Each runs in the terminal, with its output in view as it would be in CI, and with no problem matcher: the Problems panel stays the editor's and [Check Every Build](https://ascribed-dev.com/docs/editor/problems.md#the-problems-panel)'s. The [Project view](https://ascribed-dev.com/docs/editor/sidebar.md#the-project-view)'s Actions run them, and so does **Tasks: Run Task**, where each project has its own, named after the project when the workspace has more than one.

Each runs the `ascribe` binary the project's language server runs: the `ascribe.path` setting, the project's own `node_modules/.bin/ascribe`, or the one bundled with the extension. Build and Sync Agent Files run in the project's folder, with `--config` naming its `ascribe.toml` when the project isn't the workspace folder. None is offered in Restricted Mode, where the extension runs nothing until you trust the folder.

**Observe Examples** runs in the folder of a [source with tested examples](https://ascribed-dev.com/docs/examples/tested.md), one task for each, named after the source when the project has more than one; `ascribe observe` runs only the `ascribe.examples.toml` in the folder it's run in, and takes no `--config`. Which sources have tested examples is what `ascribe model --section sources` says: those with an `ascribe.examples.toml`, and not a [source in another repository](https://ascribed-dev.com/docs/examples/docs-repository.md), whose config is never run. The config names commands to run, so before running them, `ascribe observe` lists them in the task's terminal and asks **Run them? \[y/N\]**. Type `y` to run them; anything else runs nothing. The Project view's **Observe Examples** asks which source first when there's more than one.

To bind one to a key, or run it from your own task, name it in `.vscode/tasks.json`:

```json
{
  "version": "2.0.0",
  "tasks": [
    { "type": "ascribe", "task": "build", "project": "docs", "label": "Build the docs" }
  ]
}
```

| Property | What it says |
| --- | --- |
| `task` | `build`, `syncAgentFiles`, or `observeExamples` |
| `project` | The project's folder, relative to the workspace folder. Leave it out for the project whose `ascribe.toml` is in the workspace folder. |
| `source` | For `observeExamples`: the source to observe, by its name in `ascribe.toml`. Leave it out when the project has one source with tested examples. |

## Settings

| Setting | Default | What it does |
| --- | --- | --- |
| `ascribe.path` | empty | The `ascribe` binary to run, for every project. When empty, each project's own `node_modules/.bin/ascribe`, then the binary included in the extension. Changing it restarts the servers that have started. |
| `ascribe.startServers` | `onDemand` | When to start the language server of each `ascribe.toml` project in the workspace. A project whose server hasn't started shows no problems in the Problems panel. A running server reports pages for the editor's build only (`[editor] build`); `ascribe check` checks every build. `onDemand`: Start a project's language server the first time one of its files is opened, or something asks for the project, such as Switch Project or Check Every Build. A change on disk starts nothing. `all`: Start every project's language server when the workspace opens. |
| `ascribe.projects.exclude` | empty | Glob patterns, from each workspace folder, for folders whose `ascribe.toml` isn't a project, such as test fixtures: `tests/**`. A project there gets no language server, and isn't listed. Left out too, without a pattern: an `ascribe.toml` git ignores, in a workspace folder that's a git repository; a source's copies, `sources/<name>/` in a project's folder; folders `files.exclude` hides; and every folder named `.ascribe`, `node_modules`, `bower_components`, `vendor`, `.venv`, `venv`, `__pypackages__`, `target`, `dist`, `.output`, `.next`, `.nuxt`, `.svelte-kit`, `.astro`, `.turbo`, `.vercel`, `.netlify`, `.cache`, `coverage`, `.gradle`, `.vscode-test`, `.tox`, or `.nox`. None of these can be brought back with a setting: to work on a project in one, open its folder as a workspace folder of its own. |
| `ascribe.formatOnSave` | `false` | Format Ascribe constructs into canonical form when saving. |
| `ascribe.agents.promptTarget` | `clipboard` | Where **Prompt agent** puts the prompt it builds for your agent. Every choice fills the prompt in and none sends it: you read it and send it. When the choice isn't available, the prompt is copied instead. `clipboard`: Copy the prompt, to paste into any agent. `chat`: Open VS Code's chat with the prompt in its input. `claude-code`: Open a Claude Code tab with the prompt in its input, through the Claude Code extension. `cursor`: Open Cursor's chat with the prompt in its input, through its `cursor://` link. Only in Cursor. |
| `ascribe.preview.scrollPreviewWithEditor` | `true` | Scroll the preview to the block at the top of the editor, and to the block the cursor moves to when it's out of view. |
| `ascribe.preview.scrollEditorWithPreview` | `true` | Scroll the editor to the line of the block at the top of the preview when the preview is scrolled. |
| `ascribe.review.sourceComments` | `auto` | Whether the source editor shows the pull request's review threads on the lines they're on, while review is on. `auto`: Show review threads on source lines unless the GitHub Pull Requests extension is active, which shows them already. `on`: Always show review threads on source lines. `off`: Show review threads in the [page preview](https://ascribed-dev.com/docs/glossary.md#page-preview) only. |
| `ascribe.maxCrashes` | `5` | How many crashes of a project's language server, since it was last restarted by hand, make the extension stop restarting it and explain why. |
| `ascribe.trace.server` | `off` | Log the conversation with the language server to its output channel, for reporting a problem. `off`: Log nothing. `messages`: Log each message's name and timing. `verbose`: Log each message with its content. |

## Commands

| Command | What it does |
| --- | --- |
| **Ascribe: Open Page Preview to the Side** | Opens the page preview of the active page beside the editor, starting its project's server if it hasn't started. |
| **Ascribe: Open Page Preview** | Opens the page preview of the active page in place of the editor, starting its project's server if it hasn't started. |
| **Ascribe: Open [Site Preview](https://ascribed-dev.com/docs/glossary.md#site-preview)** | Opens the active page on its project's dev server, in the browser. See [Site preview](https://ascribed-dev.com/docs/editor/preview.md#site-preview). |
| **Ascribe: Select Preview Build** | Picks the build the preview shows, for the previewed page's project, as the preview's **Build** picker does. It's the same choice as **Switch Build**. |
| **Ascribe: Prompt Agent to Fix This File** | Builds a prompt for your agent about the active file's problems, and puts it where `ascribe.agents.promptTarget` says, without sending it. Shown when the file has problems. See [Prompt your agent](https://ascribed-dev.com/docs/agents/prompts.md). |
| **Ascribe: Prompt Agent to Fix This Project** | Builds a prompt for your agent about the problems of the active file's project, or, from the Project view, the view's, as Prompt Agent to Fix This File does. Shown when the project has problems. |
| **Ascribe: New Page** | Starts a page from its type's template: asks for the type, each with what it's for, then the title, makes the page in the content root, and opens it with the first thing to fill in selected; the page names its type in its frontmatter. Run on a folder in the Explorer, it makes the page in that folder. It's in the Pages view's title bar too. See [Start a page](https://ascribed-dev.com/docs/editor/actions.md#start-a-page). |
| **Ascribe: Actions for the Cursor** | Opens the [actions bar](https://ascribed-dev.com/docs/editor/actions.md#the-actions-bar): the fixes for problems at the cursor, then the actions that apply to the cursor or selection. |
| **Ascribe: Go to Next Placeholder** | Selects the next thing the page's template left to fill in, after the cursor, so typing replaces it; after the last, it goes back to the first. |
| **Ascribe:** *an action* | Runs the action, such as **Ascribe: Wrap in a note**, in a Markdown file of a project. Each of the [actions](https://ascribed-dev.com/docs/editor/actions.md) is a command. |
| **Ascribe: Switch Build** | Picks the build you're looking at in the active file's project: the one the preview renders and the status bar names. Choosing the editor build follows `[editor] build`. See [The build you're looking at](https://ascribed-dev.com/docs/editor/preview.md#the-build-youre-looking-at). |
| **Ascribe: Check Every Build** | Runs `ascribe check` over every build of the active file's project, and adds to the Problems panel the problems of builds other than the editor's, which the language server doesn't check. When no file of a project is active and the workspace has several projects, it asks which. See [The Problems panel](https://ascribed-dev.com/docs/editor/problems.md#the-problems-panel). |
| **Ascribe: Search Pages as Readers See Them** | Finds words as readers read the active file's project's pages, as you type, unsaved text included, and opens the line that wrote the place you choose. It searches the build the status bar names; its **Search Every Build** button searches every build. See [Search](https://ascribed-dev.com/docs/editor/writing.md#search). |
| **Ascribe: Apply Every Safe Fix** | Applies, as one edit, every safe fix of the problems the editor shows in the active file's project, the editor build's, after saying how many fixes in how many files. The files are left unsaved, for you to read before you save them, and Undo takes the edit back. When no file of a project is active and the workspace has several projects, it asks which. See [Edits to the whole project](https://ascribed-dev.com/docs/editor/sidebar.md#edits-to-the-whole-project). |
| **Ascribe: Format Every Page** | Formats every page and [fragment](https://ascribed-dev.com/docs/glossary.md#fragment) of the active file's project as `ascribe fmt` does, as one edit, after saying how many files it changes. The files are left unsaved, for you to read before you save them, and Undo takes the edit back. When no file of a project is active and the workspace has several projects, it asks which. See [Edits to the whole project](https://ascribed-dev.com/docs/editor/sidebar.md#edits-to-the-whole-project). |
| **Ascribe: Project Menu** | Shows the menu of the active file's project, as clicking its status bar item does: switch the build, check every build, show the server's output, switch project, restart its server, dim what the build leaves out, or open the preview. |
| **Ascribe: Dim What the Build Leaves Out** | Turns the build lens on or off for the active file's project: the editor dims what the build you're looking at leaves out of each page, with a hover that says why. See [The build lens](https://ascribed-dev.com/docs/editor/preview.md#the-build-lens). |
| **Ascribe: Start Review** | Marks what changed in the preview, against a base it asks for, for the active page's project, or, from the Project view, the view's. Its server must be running: open one of its pages first. See [Review in the preview](https://ascribed-dev.com/docs/editor/review.md). |
| **Ascribe: Stop Review** | Turns review off for the active page's project, and frees its base. |
| **Ascribe: Changed Pages** | Lists the pages the change touches in the preview's build; choosing one opens it and its preview. |
| **Ascribe: Refresh Comments** | Reads the pull request's review threads from GitHub again, for the active page's project. See [Comments in the preview](https://ascribed-dev.com/docs/editor/review.md#comments-in-the-preview). |
| **Ascribe: Submit Review…** | Opens the preview's dialog for submitting your review's comments, on the page it shows or the first changed page. Shown while you have comments to submit. It's in the Review view's title bar too. |
| **Ascribe: Open Source Changes** | Opens one diff of the changed pages' source files, at the base against the working tree: each page's own file, and the fragments and pages it changed through. It's in the Review view's title bar too. See [The Review view](https://ascribed-dev.com/docs/editor/review.md#the-review-view). |
| **Ascribe: Open Project Settings** | Opens Project Settings for the active file's project: its `ascribe.toml` settings, each with its value, its default, its description, and the line that sets it, in a tab beside the file. A value the file sets can be changed there, and the change is made in `ascribe.toml`, unsaved. The gear in the Project view's head opens it for the view's project, and the button in `ascribe.toml`'s title bar for that file's. See [Project Settings](https://ascribed-dev.com/docs/editor/settings.md#project-settings). |
| **Ascribe: Show Server Output** | Opens the log of the active file's project. When no file of a project is active and the workspace has several projects, it asks which, showing whether each one's server is running. |
| **Ascribe: Restart Language Server** | Stops and starts every project's server that has started, including one that stopped after crashing, and forgets earlier crashes. A server that hasn't started stays off until it's needed; when none has, the command says so. It also picks up `ascribe.toml` files added or deleted. |
| **Ascribe: Switch Project** | Lists the workspace's projects, the sidebar's first, each with its folder and its server's state, and shows the one you choose in the sidebar, starting its server, until a file of another project is active. Shown while the workspace has several projects, and in the Project view's title bar then too. See [Switch Project](https://ascribed-dev.com/docs/editor/sidebar.md#switch-project). |
