# Using the CLI [<span class="proPill"></span>](/curio/docs/#curio-professional) [<span class="sustainerPill"></span>](/curio/docs/#curio-sustainers)

This guide covers using the Curio CLI to inspect and modify Curio projects, organize content, import and export files, navigate the Curio app, and work with command output and errors.

## Two Ways to Work

By default, `curio` talks to the running Curio app. This mode supports everything, including selection-based queries, Organizer creation, property changes, import/export, and navigation by UUID.

```text
curio query "#active" --fields id,title
```

You can also point the CLI at a project file directly using `--project`:

```text
curio query --project ~/Documents/MyProject.curio "#active" --fields id,title
```

This direct mode is read-only, so it is ideal for scripts and automations that need to inspect a project without launching Curio first.

When several projects are open, add `--project-id <projectUUID>` to target one of them without bringing its window forward:

```text
curio query --project-id 12345678-1234-1234-1234-123456789ABC "#active" --fields id,title
```

Get the UUID from `curio status`. Live project targeting is supported by `query`, `get`, `set`, `tags`, `create`, `move`, `batch`, `export`, `import`, `navigate`, `save`, `sections`, and `assets`. It establishes the project context once for the request; only `navigate` activates the targeted project. Do not combine `--project-id` with the offline `--project <path>` option.

!!! important "Important"
    `--project` mode is read-only. You can use it with `query`, `get`, `sections`, `assets`, and `tags`, but not `project`, `set`, `create`, `export`, `import`, `navigate`, `project-center`, or `status`.

!!! note "Note"
    Direct project access requires a recent activation token. If you see a `NotActivated` error, simply launch the Curio app once and try again.

## Finding Help

The CLI is self-documenting. These commands are the quickest way to inspect the capabilities of the version you have installed:

```text
curio help
curio --help
curio docs
curio fields
curio fields due_date
curio tags
curio settings list
curio skills list
curio claude doctor
curio mcp-server --log ~/Library/Logs/curio-mcp.log
curio status
curio --version
```

Use `--verbose` with most commands if you want diagnostic details about transport selection, repository discovery, or activation handling.

## Quick Start

Check whether Curio is running:

```text
curio status
```

Query the running app using Curio’s full query language:

```text
curio query "#active due soon sort:priority" --fields id,title,priority,due_date
```

Read the currently selected figure:

```text
curio get --selected figure --fields plain_text,priority
```

Read a specific figure:

```text
curio get --id 12345678-1234-1234-1234-123456789ABC --fields raw_text,due_date,tags,asset_file,sync_file
```

Get the selected idea space:

```text
curio get --selected ideaspace --fields title,note,tags
```

List all available tags in the current project:

```text
curio tags
curio tags --project ~/Documents/MyProject.curio
```

Inspect Project Center categories and project paths:

```text
curio project-center categories
curio project-center projects --category "Active"
```

Create and open a completely empty project:

```text
curio project create ~/Documents/NewProject
```

Inspect the project’s section hierarchy:

```text
curio sections --project ~/Documents/MyProject.curio
```

List assets under a section or Organizer parent:

```text
curio assets --project ~/Documents/MyProject.curio --section 12345678-1234-1234-1234-123456789ABC --recursive --organizer_order
curio assets --project ~/Documents/MyProject.curio --parent 87654321-4321-4321-4321-CBA987654321 --recursive --organizer_order
```

Create Organizer items in the running app:

```text
curio create section --title "Research"
curio create folder --section 12345678-1234-1234-1234-123456789ABC --title "Exhibits"
curio create ideaspace --section 12345678-1234-1234-1234-123456789ABC --title "001 - Complaint"
curio create ideaspace --selected ideaspace --position next_sibling --title "002 - Exhibit A"
```

Move a figure on the canvas or move an idea space in the Organizer:

```text
curio move --selected figure --position 100,100
curio move --id 12345678-1234-1234-1234-123456789ABC --relative-to 87654321-4321-4321-4321-CBA987654321 --position next_sibling
curio move --id 12345678-1234-1234-1234-123456789ABC --parent 87654321-4321-4321-4321-CBA987654321 --position first_child
```

Update the selected figure:

```text
curio set --selected figure --priority 4 --due-date 2026-04-01
curio set --selected figure --url "apple.com"
curio set --selected figure --field priority=3 --values '{"note":"Reviewed"}'
curio set --selected figure --progress 60 --checkmark-visible true
curio set --selected figure --field size.width=360
curio set --selected figure --field fill_color=#ffcc66 --field pen_color=#112233
```

Send text through standard input:

```text
echo "hello from Terminal" | curio set --selected figure --raw-text --stdin
```

Set an asset figure caption:

```text
curio set --selected figure --caption "Sprint planning whiteboard"
```

Update the selected idea space:

```text
curio set --selected ideaspace --title "Roadmap" --tags "GTD/Active,Planning"
curio set --selected ideaspace --field dimension.width=1600 --field dimension.height=1000 --field fill_color=#f0f7ff
```

Export the currently selected figures as PDF:

```text
curio export --selected figures --format pdf --filename ~/Desktop/Selection
```

Import a file into the current idea space:

```text
curio import ~/Photos/diagram.png
```

Import content as a native figure:

```text
curio import ~/outline.md --as list
curio import ~/outline.md --as list --ignore-inline-tags --ignore-inline-resources
```

Spread a multi-page PDF into one idea space per page:

```text
curio import ~/Documents/affidavit.pdf --as spread-pdf --section <sectionUUID> --title-template "{page:03}"
curio import ~/Documents/affidavit.pdf --as spread-pdf --section <sectionUUID> --layout layout.json
```

## Command Summary

| Command | Purpose |
| --- | --- |
| `curio query ...` | Query figures using Curio’s [query language](/curio/docs/search/) |
| `curio get ...` | Read fields from one figure or one Organizer item |
| `curio sections ...` | Return the Organizer section hierarchy |
| `curio assets ...` | List project assets or assets within a section |
| `curio project create ...` | Create and open an empty project with a Default Section |
| `curio project-center ...` | Inspect Project Center categories and project paths |
| `curio create ...` | Create Organizer sections, folders, and idea spaces |
| `curio move ...` | Move a figure on the canvas or move an Organizer item |
| `curio batch ...` | Run a command batch file with pseudo-variable references |
| `curio set ...` | Update writable fields on one figure or one Organizer item |
| `curio export ...` | Export a figure, selected figures, or selected idea spaces |
| `curio import ...` | Import a file/content or Spread PDF pages |
| `curio navigate ...` | Open a deep link or project, or navigate to an item by UUID or identifier |
| `curio save ...` | Synchronously save the current project or an open project UUID |
| `curio status` | Report whether Curio is running, open projects, and sandbox info |
| `curio skills` | Manage AI platform skill installations |
| `curio claude` | Install, package, or diagnose the Claude Desktop connector |
| `curio mcp-server` | Run the stdio MCP server for host-launched AI integrations |
| `curio fields` | List all available fields |
| `curio fields <name>` | Show details for one field |
| `curio tags` | List available tags, or create project/global tags and tag sets |
| `curio update` | Check for the latest CLI release |
| `curio docs` | Open the detailed CLI documentation in the default browser |
| `curio --help` | Show built-in command help |
| `curio --version` | Show the installed CLI version |

## Status

The `status` command reports whether Curio is running, which projects are open, and whether the app is sandboxed:

```text
curio status
```

When Curio is running as the Mac App Store edition, the response includes `app_sandboxed: true` and an `accessible_folders` array listing the folders the sandboxed app can read from:

```json
{
  "api_version": 1,
  "app_version": "26.1",
  "response_ok": true,
  "result": {
    "accessible_folders": [
      "/Users/you/Library/Containers/com.zengobi.curio/Data/tmp/",
      "/Users/you/Library/Containers/com.zengobi.curio/Data/Downloads",
      "/Users/you/Documents/Curio",
      "/Users/you/Desktop"
    ],
    "app_open_projects": [
      { "active": true, "has_unsaved_changes": false, "path": "/Users/you/Documents/Curio/MyProject.curio", "title": "MyProject" }
    ],
    "app_running": true,
    "app_sandboxed": true
  }
}
```

The accessible folders typically include:

- The app’s **temporary directory** (inside the sandbox container, not system `/tmp` )
- The app’s **Downloads directory** (also inside the container)
- Your **Curio projects folder** (set in Preferences)
- Any **authorized folders** you have previously granted Curio access to

When the website (direct download) edition is running, `app_sandboxed` is `false` and `accessible_folders` is not included — the app can access any file path.

Each entry in `app_open_projects` includes `active` and `has_unsaved_changes` booleans. Exactly one open project is active, so scripts can determine which project receives live commands that depend on Curio's current UI state. `has_unsaved_changes` reports whether the project has edits that have not reached disk. Status settles pending model propagation before reporting this value. Use `curio get --selected ideaspace --fields id,title,section_path` to inspect the active project's current idea space.

When Curio is not running, `status` returns a local response with `app_running: false`.

## Creating a Project

Use `project create` to create and open a completely empty Curio project. The project contains its normal initial **Default Section**, but no folders, idea spaces, or figures:

```text
curio project create ~/Documents/Test
```

The command requires the running Curio app. The destination must be an absolute path; paths beginning with `~/` are expanded, and a missing `.curio` extension is added automatically. An explicitly different extension is rejected. The parent folder must already exist, and Curio refuses to overwrite an existing file, project, or symbolic link.

A successful result includes `project_uuid`, `path`, `title`, `default_section_id`, and `default_section_title`. The new project is open and active when the command returns, so the returned section UUID can be passed directly to existing Organizer creation commands:

```text
curio project create ~/Documents/Test
curio create folder --section <default_section_id> --title "Research"
curio create ideaspace --section <default_section_id> --title "Notes"
curio save --project-id <project_uuid>
```

## Project Center

The `project-center` command reads Curio’s Project Center categories and tracked-project paths. When Curio is running, it uses the freshest live Project Center model. Otherwise, it reads the last persisted Project Center snapshot and the separate Recently Opened bookmark list without launching Curio or modifying either file.

Every successful result includes `data_source`, which is `live` or `persisted`. A persisted response reflects the most recently saved Project Center state; changes that have not yet been written by the running app require the live path.

List the visible categories:

```text
curio project-center categories
```

Category results include:

- `id`: stable category UUID
- `title`: displayed category title
- `kind`: `all`, `recent`, or `category`
- `project_count`: number of projects currently in the category
- `packaged`: whether Curio supplied the category
- `sort_by_title`: whether the category’s projects are title-sorted

The result includes the special **All Categorized Projects** and **Recently Opened** categories in both live and persisted modes. Use their returned IDs rather than localized titles when saving a dashboard selection.

List projects in a category by UUID or case-insensitive title:

```text
curio project-center projects --category <category-uuid>
curio project-center projects --category "Active"
```

Omit `--category` to list All Categorized Projects:

```text
curio project-center projects
```

Project results include `title`, `path`, `valid`, `status_ignored`, and category memberships. Entries without either a resolved path or a last-known path are omitted. A persisted project may also include `path_is_last_known` when its bookmark could not be resolved. Use the path with offline read commands to inspect the project without opening it:

```text
curio query --project "/Users/you/Documents/Business.curio" "#meta/unchecked sort:due" --fields id,title,due_date,priority,tags
```

## Navigating

Use `navigate` to jump to a Curio location:

```text
curio navigate "curio://project/MyProject.curio?figure=abc-123"
curio navigate ~/Documents/MyProject.curio
curio navigate --id 12345678-1234-1234-1234-123456789ABC
curio navigate --project-id ABCDEFAB-1234-5678-90AB-ABCDEFABCDEF
curio navigate --project-id ABCDEFAB-1234-5678-90AB-ABCDEFABCDEF --id 12345678-1234-1234-1234-123456789ABC
```

The positional item form remains supported for compatibility. A bare UUID or identifier is resolved in the current project (or the project selected by `--project-id`) in this order: figure UUID, Organizer item UUID, figure identifier, then idea space drawing identifier. It is never interpreted as a project UUID. Use `--project-id` to identify an open project explicitly and `--id` to make an item target explicit.

If Curio is not already running, a `curio://` URL or `.curio` project path launches it. The CLI then waits for Curio's automation service and confirms the navigation rather than reporting success merely because LaunchServices accepted the URL. Item UUIDs, identifiers, and project UUIDs require the app to be running because they resolve against open projects.

Project targets must use an absolute path; `~/...` paths are expanded. Bare filenames and relative paths such as `./MyProject.curio` are rejected because multiple projects may share the same filename. Navigating to a saved project path opens it if necessary and makes an already-open project current. If Curio is hidden, it is unhidden and the destination is reasserted after the window transition. Save an untitled project first so it has a stable path that scripts can navigate to.

On success, `result` confirms the destination with `target_kind`, `project_uuid`, `path`, and `title`. It also includes `section_id`, `organizer_item_id`, or `figure_id` when those targets resolve. Therefore `response_ok: true` means Curio completed and verified the requested navigation. A missing project, unresolved item, or unconfirmed activation returns `response_ok: false`.

## Available Fields

Field names are always written in `snake_case` and are passed as a comma-separated list to `--fields`.

When `--fields` is omitted, `query` and `get` return only the compact default fields `id`, `kind`, and `title`. This default is the same whether the command uses the running app or direct `--project` mode. Scripts should name every field they depend on explicitly; use `--fields all` to return every field that applies to each result item's type.

For example:

```text
curio query "#active" --fields id,title,priority,due_date
```

The exact field list for your installed version is available via `curio fields`.
For convenience, the full reference is split below into:

- [Figure Fields](#figure-fields)
- [Organizer Item Fields](#organizer-item-fields)

`curio fields` shows the combined catalog across both target types, and `curio fields <name>` shows the type and description for one field.

## Available Tags

Use `curio tags` to list all visible tags in the current project.

Examples:

```text
curio tags
curio tags --project ~/Documents/MyProject.curio
```

`curio tags` returns:

- `items` : array of tag name strings
- `count` : number of returned tags

Tag names are formatted exactly as Curio expects them in tag-oriented CLI operations:

- Named tag sets use `TagSet/Tag Name` , such as `GTD/Active` or `Context/Next Action` .
- Project-local keyword tags use just `Tag Name` .
- Global keyword tags use `/Tag Name` .

When you set `tags`, Curio matches tag names case-insensitively and ignores spaces and emoji, so inputs such as `vacation,gtd/onhold` still resolve to the corresponding visible tags.

### Creating Tags and Tag Sets

The running app can idempotently create project or global tags. With no scope option, creation targets the active project:

```text
# Project keyword tags in the active project
curio tags create --tags "homework, study guide, exam"

# Global keyword tags
curio tags create --global --tags "homework, study guide, exam"

# A project tag set and its tags
curio tags create --tagset "GTD" --tags "done, on hold, finished"

# A global tag set and its tags
curio tags create --global --tagset "GTD" --tags "done, on hold, finished"
```

Use `--one-choice` when creating or extending a named set that should allow only one associated tag at a time, such as a status set used to group a Kanban board:

```text
curio tags create --tagset "Status" --one-choice --tags "todo, active, on hold, done"
```

`--tagset` can be used without `--tags` to create an empty named set. If the set already exists, Curio extends it. If `--one-choice` is omitted, an existing set keeps its current one-choice setting.

Creation is idempotent: matching ignores case, spaces, and emoji, and existing tags or tag sets are reused rather than duplicated. If the selected project or global scope already contains multiple tag sets with the same normalized name, Curio rejects the request instead of choosing one arbitrarily. The response separates `created` and `existing` names and reports the resolved scope and tag-set properties:

```json
{
  "scope": "project",
  "created": ["Status/todo", "Status/active"],
  "existing": ["Status/done"],
  "tag_set": {
    "name": "Status",
    "created": true,
    "one_choice": true,
    "uuid": "..."
  }
}
```

As with the other live project-scoped commands, use `--project-id <projectUUID>` to target a particular open project without activating it. Project UUIDs are available from `curio status`. For tag creation, this option cannot be combined with `--global`.

`--project <path>` remains the offline, read-only listing mode and is intentionally not accepted by `tags create`.

## Querying

The `query` command uses the same query language as Curio’s Search shelf and Quick Find, so the same query expressions, scopes, grouping, and sorting rules apply.
See Curio’s [query language](/curio/docs/search/) documentation for a deeper discussion of query syntax.

When Curio is running, context scopes such as `scope:section` and `scope:ideaspace` use the current Organizer section or active idea space. Direct `--project` mode is offline and returns `InvalidRequest` for context-dependent scopes that require live UI state.

Unless the query text includes an explicit `group:` command, the CLI returns a flat `items` array and reports `groupBy` as `null`. If the query text includes `group:`, the response switches to a `groups` array with `label` and `items` for each group.

A query containing only shaping commands such as `sort:title limit:400` has no criterion and therefore matches nothing. Use `not kind=nothing` to match all searchable items before applying those commands:

```text
curio query "not kind=nothing sort:title limit:400" --fields id,title
```

Examples:

```text
curio query "ipad"
curio query "(ipad or iphone) @SteveJ #event #2010" --fields id,title
curio query "#active due soon sort:priority" --fields id,title,priority,due_date
curio query "#active group:priority" --fields id,title,priority
curio query --project ~/Documents/MyProject.curio "#active" --fields id,title
curio query --project ~/Documents/MyProject.curio "kind=ideaspace modified > -3m sort:-modified limit:40" --fields id,title,modified_date,preview_image_file,link
```

The final example is suitable for building a visual gallery of recently modified idea spaces. `kind=ideaspace` selects Organizer idea spaces, `modified > -3m` limits the results to the past three months, `sort:-modified` puts the newest first, and `limit:40` caps the gallery. Each result includes its stored preview path and a `curio://` link that opens Curio at that idea space.

You can optionally truncate long text fields in the response:

```text
curio query "#meeting" --fields id,title,plain_text --max-chars 500
```

Use `--fields all` with `query` to return every field that applies to each result item’s type.

### Reading Selected Targets

Use `get --selected <kind>` to read specific live UI targets without constructing a query.
These selected targets only work when Curio is running.

| `--selected` value | Returns |
| --- | --- |
| `figure` | The single selected figure |
| `figures` | All selected figures |
| `section` | The current section containing the active organizer item |
| `sections` | All selected sections |
| `organizer_item` / `ideaspace` | The active Organizer item or idea space |
| `organizer_items` / `ideaspaces` | The selected Organizer items or idea spaces |

Examples:

```text
curio get --selected figure --fields plain_text,priority
curio get --selected figures --fields id,title,priority
curio get --selected section --fields id,title,kind
curio get --selected sections --fields id,title,kind
curio get --selected ideaspace --fields id,title
curio get --selected ideaspaces --fields id,title,kind
```

Singular targets return one object directly in `result`. Plural targets return an `items` array and `count`.

!!! important "Important"
    Selected targets are based on Curio’s current UI state, so they are not available with `--project`.

## Figure Fields

These are the fields available when the target is a figure.
You can use them with `query`, `get`, and figure-targeted `set`.

Use `--id <uuid-or-identifier>` to target a specific figure. UUID lookup is tried first; identifiers must be unique or Curio returns `InvalidRequest`.
Use `--selected figure` to target the single selected figure.

`get` supports both the running app and `--project`.
Use `--fields all` with `get` to return the full field set for the selected target type.
`set` requires the running app.

Examples:

```text
curio get --selected figure --fields raw_text,due_date,tags
curio get --id 12345678-1234-1234-1234-123456789ABC --fields id,title,kind,link
curio get --project ~/Documents/MyProject.curio --id 12345678-1234-1234-1234-123456789ABC --fields title,plain_text
curio get --id 12345678-1234-1234-1234-123456789ABC --fields link,asset_id,asset_file_kind,display_page
curio set --selected figure --priority 4
curio set --selected figure --rating 5 --progress 75
curio set --selected figure --progress 60 --checkmark-visible true
curio set --selected figure --start-date 2026-03-20 --due-date 2026-03-27
curio set --selected figure --done-date 2026-03-27T14:30
curio set --selected figure --caption "Sprint planning whiteboard"
curio set --selected figure --url "x-devonthink-item://932493923"
curio set --selected figure --note "Reviewed with team"
curio set --selected figure --field priority=3 --field rating=4
curio set --selected figure --field size.width=360 --field origin.x=72 --field origin.y=96
curio set --selected figure --field text_font_size=18 --field text_color=#123456 --field text_bold=true
curio set --selected figure --field fill_color=#ffcc66 --field pen_color=#112233 --field pen_width=2
curio set --selected figure --field displaying_as=preview --field display_page=2
curio set --selected figure --field icon_size=64 --field caption_within_shape_border=false
curio set --selected figure --field freeform_sizing=false --field min_height=120
curio set --selected figure --values '{"bounds":{"x":72,"y":96,"width":360,"height":240}}'
curio set --selected figure --values '{"text_alignment":"center","text_vertical_alignment":"middle"}'
curio set --selected figure --values '{"fill_style":"solid","shape":"RoundedRect","opacity":0.75}'
curio set --selected figure --values '{"note":"Reviewed with team","priority":3}'
curio set --selected figure --values-file updates.json
cat notes.md | curio set --selected figure --raw-text --stdin
```

For common fields, use the named flags such as `--priority` or `--due-date`.
For script-generated updates, `--field key=value` handles quick scalar assignments, while `--values` and `--values-file` accept a JSON object of writable field values.
Set `size.width` to let Curio recompute a natural height for figures that support it, such as text figures, image previews, and collections.
Text styling fields use the `text_` prefix, including `text_font_size`, `text_color`, `text_bold`, and alignment fields.
Figure styling fields include `fill_color`, `pen_color`, `pen_width`, `opacity`, `corner_radius`, `fill_style`, `shape`, and `dashed_pattern`.
Asset presentation fields include `displaying_as`, `display_page`, caption placement, icon size, automatic sizing, freeform sizing, and minimum height.
Use `--url` to set an asset figure's associated URL. Values without a URI scheme default to `https://`, so `apple.com` becomes `https://apple.com`. Schemes such as `mailto:` and `x-devonthink-item:` are preserved.
All set forms merge into the same update request, so don’t provide the same field more than once in a single command.

Setting `raw_text` is treated as a committed text edit. Before the command returns, Curio writes file-backed text to its underlying asset file, exports to any writable sync file, and propagates the text to synced figure instances and special idea-space text figures.

| Field | Settable | Description |
| --- | --- | --- |
| `id` | no | Figure or Organizer item UUID (stable, used for addressing) |
| `identifier` | no | User-visible figure identifier set via Curio’s Info panel |
| `kind` | no | Figure or Organizer item kind used by query `kind` filters |
| `title` | no | Processed plain text title (writable when targeting an Organizer item) |
| `link` | no | `curio://` deep link URL that opens the project and navigates to the target item |
| `section_path` | no | Absolute section path beginning with `/` and ending with the current or containing section |
| `organizer_path` | no | Absolute Organizer path within the containing section, ending with the Organizer item or figure’s idea space |
| `parent_id` | no | Immediate Organizer parent UUID, or containing idea space UUID for a figure |
| `raw_text` | yes | Raw text content (may contain markdown, MathJax, Mermaid source) |
| `caption` | yes | Asset figure caption text (empty string clears it) |
| `displaying_as` | yes | Asset figure display mode: `icon`, `preview`, `text`, or `live_preview` when supported |
| `display_page` | yes | 1-based displayed page number for PDF asset figures |
| `caption_within_shape_border` | yes | Whether the asset figure caption is drawn within the shape border |
| `icon_size` | yes | Asset figure icon size in points, from `16` to `512`; sets square icon width and height |
| `auto_sizing` | yes | Whether the asset figure automatically sizes itself to its content |
| `freeform_sizing` | yes | Whether the asset figure uses freeform sizing instead of automatic sizing or minimum-height fitting |
| `min_height` | yes | Asset figure minimum height in points; `0` clears it |
| `plain_text` | no | Processed searchable text |
| `rendered_text` | no | Fully processed plain text |
| `rendered_markdown` | no | Figure content converted to Markdown |
| `url` | yes | Asset figure’s associated URL; values without a URI scheme default to `https://`, while custom schemes are preserved |
| `asset_file` | no | File path to the underlying asset file (PDF, image, doc, etc.) |
| `preview_image_file` | no | Absolute path to an existing stored JPEG or PDF preview; `null` when no preview file exists |
| `asset_id` | no | Underlying asset UUID for an asset-backed figure |
| `asset_file_kind` | no | Underlying asset display/file kind |
| `sync_file` | no | Sync file URL, if sync is configured |
| `sync_file_direction` | no | Sync direction: `bidirectional`, `export`, or `import` |
| `start_date` | yes | Start date (`YYYY-MM-DD` or `YYYY-MM-DDTHH:MM`) |
| `due_date` | yes | Due date (`YYYY-MM-DD` or `YYYY-MM-DDTHH:MM`) |
| `done_date` | yes | Done date (`YYYY-MM-DD` or `YYYY-MM-DDTHH:MM`) |
| `added_date` | no | Date the figure or Organizer item was added (`YYYY-MM-DD` or `YYYY-MM-DDTHH:MM`, read-only) |
| `modified_date` | no | Date the figure or Organizer item was last modified (`YYYY-MM-DD` or `YYYY-MM-DDTHH:MM`, read-only) |
| `tags` | yes | Array of tag names; matching ignores case, spaces, and emoji when setting, for example `vacation,gtd/onhold` |
| `resources` | no | Array of resource names |
| `references` | no | Outgoing references grouped by reference type name; each value is an array of target `curio://` links |
| `priority` | yes | Priority level (`0`=none, `1`=very low, `2`=low, `3`=medium, `4`=high, `5`=urgent) |
| `rating` | yes | Rating (`0-5`) |
| `progress` | yes | Percent complete (`0-100`) |
| `checkmark_visible` | yes | Whether the figure’s progress checkbox is visible |
| `text_font_family` | yes | Text font family name |
| `text_font_face` | yes | Text font face name within the selected family, such as `Regular`, `Bold`, or `Italic`; PostScript font names are also accepted |
| `text_font_size` | yes | Text font size in points |
| `text_color` | yes | Text foreground color as `#rgb`, `#rgba`, `#rrggbb`, or `#rrggbbaa` |
| `text_background_color` | yes | Text background color as `#rgb`, `#rgba`, `#rrggbb`, or `#rrggbbaa`; transparent clears the background |
| `text_bold` | yes | Whether text is bold |
| `text_italic` | yes | Whether text is italic |
| `text_underline` | yes | Whether text is underlined |
| `text_strikethrough` | yes | Whether text has strikethrough |
| `text_alignment` | yes | Text horizontal alignment: `left`, `center`, `right`, `justified`, or `natural` |
| `text_vertical_alignment` | yes | Text vertical alignment: `top`, `middle`, or `bottom` |
| `origin.x` | yes | Figure bounds origin x coordinate |
| `origin.y` | yes | Figure bounds origin y coordinate |
| `size.width` | yes | Figure width; recomputes natural height when supported |
| `size.height` | yes | Figure height |
| `bounds` | yes | Rectangle object with `x`, `y`, `width`, and `height` |
| `rotation` | yes | Figure rotation angle in degrees |
| `fill_color` | yes | Figure fill color as `#rgb`, `#rgba`, `#rrggbb`, or `#rrggbbaa`; setting a figure fill color enables solid fill unless `fill_style` is also set to `none` |
| `pen_color` | yes | Figure pen color as `#rgb`, `#rgba`, `#rrggbb`, or `#rrggbbaa`; setting a pen color ensures a visible pen width unless `pen_width` is also set to `0` |
| `pen_width` | yes | Figure pen width in points; `0` hides the stroke |
| `opacity` | yes | Figure opacity from `0.0` transparent through `1.0` opaque |
| `corner_radius` | yes | Figure corner radius in points, from `0` to `30`, when the figure or collection layout supports corner radius changes |
| `fill_style` | yes | Figure fill style: `none` or `solid` |
| `shape` | yes | Figure shape name, such as `Rectangle`, `RoundedRect`, `Capsule`, `Circle`, `Diamond`, or `Cloud` |
| `dashed_pattern` | yes | Figure dash pattern as `pixelsOn{,pixelsOff,pixelsOn,pixelsOff}{;phase}`; use `0` for a solid line |
| `note` | yes | Meta note text |

`checkmark_visible` and `progress` are independent. Showing or hiding the progress checkbox does not change the percentage, and setting a percentage does not make the checkbox visible. Set both fields when the figure should behave as a visible task.

For MCP, pass `checkmark_visible` as a boolean property to `curio_set`; it is also accepted inside the generic `values` object.

### Asset Presentation Fields

Asset figures support display, caption, and sizing controls through generic writable fields.
Curio rejects display modes that the target figure cannot support, such as `text` for a plain image asset or `live_preview` for a non-URL asset.
For PDFs, `display_page` is 1-based and out-of-range errors include the valid page range when Curio can read it.

`freeform_sizing` and `min_height` are mutually exclusive in Curio’s figure model: enabling freeform sizing clears `min_height` and disables `auto_sizing`; setting `min_height` above `0` turns freeform sizing off.

## Organizer Item Fields

These are the fields available when the target is an Organizer item or idea space.
You can use them with `query`, `get`, and `set`.

Use `--id <uuid-or-identifier>` to target a specific Organizer item or idea space. UUID lookup is tried first; identifiers must be unique or Curio returns `InvalidRequest`.
Use `--selected ideaspace` to target the active organizer item / idea space. `organizer_item` is still accepted as an alias.

`get` supports both the running app and `--project`.
Use `--fields all` with `get` to return the full field set for the selected target type.
`set` requires the running app.

Examples:

```text
curio get --id 87654321-4321-4321-4321-CBA987654321 --fields title,note,tags
curio get --selected ideaspace --fields title,note,tags
curio set --selected ideaspace --title "Deep Link"
curio set --selected ideaspace --tags "GTD/Active,Planning"
curio set --selected ideaspace --note "Reviewed with team"
curio set --selected ideaspace --field dimension.width=1600 --field dimension.height=1000
curio set --selected ideaspace --values '{"dimension":{"width":1600,"height":1000},"fill_color":"#f0f7ff"}'
```

Idea space and Organizer item IDs support direct `title`, `note`, and `tags` reads and writes.
Idea spaces also support `dimension.width`, `dimension.height`, and `fill_color`; these change the canvas size and background fill, not figure bounds or figure fill.

| Field | Settable | Description |
| --- | --- | --- |
| `id` | no | Idea space or Organizer item UUID (stable, used for addressing, identical to the underlying asset UUID) |
| `identifier` | no | User-visible idea space identifier set via Curio’s Info panel |
| `kind` | no | Figure or Organizer item kind used by query `kind` filters |
| `asset_file_kind` | no | Underlying asset display/file kind |
| `title` | yes | Processed plain text title (writable when targeting an Organizer item) |
| `link` | no | `curio://` deep link URL that opens the project and navigates to the target item |
| `preview_image_file` | no | Absolute path to an existing stored JPEG or PDF preview; `null` when no preview file exists |
| `section_path` | no | Absolute section path beginning with `/` and ending with the current or containing section |
| `organizer_path` | no | Absolute Organizer path within the containing section, ending with the Organizer item or figure’s idea space |
| `parent_id` | no | Immediate Organizer parent UUID, or containing idea space UUID for a figure |
| `dimension.width` | yes | Idea-space canvas width in points |
| `dimension.height` | yes | Idea-space canvas height in points |
| `fill_color` | yes | Idea-space background fill color as `#rgb`, `#rgba`, `#rrggbb`, or `#rrggbbaa` |
| `added_date` | no | Date the figure or Organizer item was added (`YYYY-MM-DD` or `YYYY-MM-DDTHH:MM`, read-only) |
| `modified_date` | no | Date the figure or Organizer item was last modified (`YYYY-MM-DD` or `YYYY-MM-DDTHH:MM`, read-only) |
| `tags` | yes | Array of tag names; matching ignores case, spaces, and emoji when setting, for example `vacation,gtd/onhold` |
| `references` | no | Outgoing references grouped by reference type name; each value is an array of target `curio://` links |
| `note` | yes | Meta note text |

`section_path` and `organizer_path` are separate absolute, title-based paths. Both begin with `/` and include their final component. For an Organizer item in a nested section, values might be `/Clients/Acme/Research` and `/Filings/Contracts/Executed`. A figure reports the same `section_path` as its containing idea space, while its `organizer_path` ends with that idea space. Single-section projects still report a value such as `/Default Section`.

Keep the two fields separate rather than concatenating them, because the boundary between the section hierarchy and Organizer hierarchy would be lost. Use UUIDs and `parent_id` when stable, unambiguous addressing is required.

## Creating Organizer Items

Use `create` to add sections, folders, and idea spaces through the running Curio app.
Creation is app-only so Curio’s Organizer model, validation, selection handling, and undo behavior stay in charge; it is not available with `--project`.

Examples:

```text
curio create section --title "Correspondence" --note "Reviewed" --tags "Discovery/Verified"
curio create section --parent 12345678-1234-1234-1234-123456789ABC --title "Authorities"
curio create folder --section 12345678-1234-1234-1234-123456789ABC --title "Discovery"
curio create ideaspace --section 12345678-1234-1234-1234-123456789ABC --title "001 - Notice of Motion"
curio create ideaspace --selected ideaspace --position next_sibling --title "002 - Exhibit A"
```

`create section` can target the Organizer root or a parent section.
`create folder` and `create ideaspace` can target a section or another Organizer item.
Every successful create response returns the new item’s stable `id`, `kind`, `title`, `parent_id`, resolved `index`, and any create-time `note` or `tags` so scripts can chain later operations.

## Moving Figures And Idea Spaces

Use `move` through the running Curio app to reposition a figure on the current idea-space canvas or to reparent/reorder an Organizer item such as an idea space.
`move` is app-only and is not available with `--project`.

Figure moves use an absolute idea-space coordinate:

```text
curio move --id 12345678-1234-1234-1234-123456789ABC --position 100,100
curio move --selected figure --position 240,160
```

Organizer moves use the same placement vocabulary as `create`:

```text
curio move --id 12345678-1234-1234-1234-123456789ABC --relative-to 87654321-4321-4321-4321-CBA987654321 --position previous_sibling
curio move --id 12345678-1234-1234-1234-123456789ABC --section 87654321-4321-4321-4321-CBA987654321 --position last_child
curio move --selected ideaspace --parent 87654321-4321-4321-4321-CBA987654321 --position first_child
```

Options:

- `--id <uuid>` targets a figure or Organizer item.
- `--selected figure|ideaspace|organizer_item` uses Curio’s current UI selection.
- `--position x,y` moves a figure to an absolute canvas coordinate.
- `--position first_child|last_child|first_sibling|previous_sibling|next_sibling|last_sibling` moves an Organizer item.
- `--parent <uuid>` , `--section <uuid>` , and `--relative-to <uuid>` choose the Organizer destination context.
- `--index <n>` provides an explicit child index for Organizer moves and cannot be combined with `--position` .

Successful move responses include `mode` (`figure` or `organizer`) plus the moved item’s `id`.
Figure responses include final geometry fields such as `origin.x`, `origin.y`, and `bounds`; Organizer responses include final `parent_id` and `index`.

### Command Batch Files

Use `curio batch` to run a UTF-8 text file containing one Curio command per line. Lines can omit the leading `curio` prefix, blank lines are ignored, and `#` starts a comment outside quotes.

```text
# filing.curio-batch
discovery = create section --title "Discovery"
emails = create ideaspace --title "Emails" --parent $discovery.id
import "~/Documents/emails.pdf" --as spread-pdf --parent $discovery.id --layout layout.json
```

```text
curio batch filing.curio-batch
```

A line can store its result with `name = command ...`. Later lines can refer to top-level result fields such as `$discovery.id`, `$emails.link`, or `$spread.asset_id`.

A reference is always substituted as a single argument, so values containing spaces need no quoting. A reference that resolves to a value beginning with `--` is rejected, because project text such as a figure title would otherwise be read as a command option.

Batch output is JSONL: each executed command writes one compact JSON object on its own line. There is no enclosing JSON document or top-level `response_ok` value for the complete batch. Each line object contains `line`, `command`, `ok`, `exit_code`, `ref` (`null` for unassigned lines), and either `result` or `error`:

```jsonl
{"command":"create","exit_code":0,"line":2,"ok":true,"ref":"discovery","result":{"id":"..."}}
{"command":"save","exit_code":0,"line":3,"ok":true,"ref":null,"result":{"saved":true}}
```

Scripts should read stdout one line at a time. Execution stops on the first failure unless you pass `--continue-on-error`, so the final emitted object may describe the failed line.

Batch files do not support child commands that read from stdin, such as `import --stdin` or `set --stdin`, because the batch file itself owns the input stream. Use file-backed imports or pass values directly in the batch line.

Placement options:

| Position | Meaning |
| --- | --- |
| `first_child` | Insert as the first child of the target parent. |
| `last_child` | Insert as the last child of the target parent. This is the default. |
| `first_sibling` | Insert at the beginning of the reference item’s sibling list. |
| `previous_sibling` | Insert immediately before the reference item. |
| `next_sibling` | Insert immediately after the reference item. |
| `last_sibling` | Insert at the end of the reference item’s sibling list. |

Target options:

- `--parent <uuid>` targets a parent section or Organizer item.
- `--section <uuid>` targets a section for folder or idea-space creation.
- `--relative-to <uuid>` targets an existing section or Organizer item for sibling placement.
- `--selected section|ideaspace|organizer_item` uses Curio’s current UI selection.
- `--index <n>` provides an explicit child index and cannot be combined with `--position` .

## Sections

Use `sections` to return the Organizer’s section hierarchy.
This command returns a recursive tree by default using `children`, so child sections are nested under their parent sections automatically.

Examples:

```text
curio sections
curio sections --project ~/Documents/MyProject.curio
```

Top-level special sections such as Archive, Trash, and Journal are included when present.

### Sections Result

`curio sections` returns JSON directly and does not use `--fields`:

- `items` : array of top-level section objects
- `count` : number of top-level section objects

Each section object includes the same general metadata style as other CLI output, and parent sections include a `children` array when they contain child sections.

## Assets

Use `assets` to list assets in the project or within a specific section.

Examples:

```text
curio assets
curio assets --project ~/Documents/MyProject.curio
curio assets --project ~/Documents/MyProject.curio --section 12345678-1234-1234-1234-123456789ABC
curio assets --project ~/Documents/MyProject.curio --parent 87654321-4321-4321-4321-CBA987654321
curio assets --project ~/Documents/MyProject.curio --section 12345678-1234-1234-1234-123456789ABC --recursive --organizer_order
curio assets --project ~/Documents/MyProject.curio --parent 87654321-4321-4321-4321-CBA987654321 --recursive --organizer_order
```

`assets --section <uuid>` restricts results to a section. `assets --parent <uuid>` restricts results to an Organizer parent such as a folder, idea space, or section. Both are non-recursive by default.
Add `--recursive` to include child Organizer items within that same section.

Sections are independent project divisions. Recursion follows folders and Organizer items but does not cross into child sections. To process a complete project, walk the hierarchy returned by `curio sections`, including nested `children`, and issue a separate `assets --section <uuid>` request for each section.

The returned asset list includes figure assets that are contained within idea spaces, like PDF images.
File-backed assets include `asset_file`, so a normal flat `curio assets` response can serve as a bulk file manifest without one `get` call per figure. Organizer-only listings produced by `--organizer_order` generally contain idea spaces and folders rather than their underlying figure assets, so use normal assets mode when collecting file paths.

Asset results also include `link` when a `curio://` destination is available. Idea spaces or asset-backed figures with an existing stored preview include `preview_image_file`. Stored previews may be JPEG or PDF. Reading them is non-mutating: the CLI does not generate or refresh missing previews.

### Assets Result

`curio assets` returns JSON directly and does not use `--fields`:

- `items` : array of asset objects
- `count` : number of returned assets
- `total_count` : in Organizer-order mode, number of top-level and nested Organizer items

In normal mode, `items` is a flat array.
Use `--section <uuid>` to start from a section or `--parent <uuid>` to start from any Organizer parent. With `--organizer_order`, `items` follows the Organizer order and organizer folders include nested `children`; `count` remains the size of that top-level `items` array and `total_count` includes the nested descendants. Combine `--recursive` with `--organizer_order` to include descendants in Organizer order within the requested section. Section recursion is explicit: walk `curio sections` and request each section UUID separately.
This Organizer-order mode only returns items that appear in the Organizer UI for that parent.

The hierarchy fields `parent_id`, `parent_title`, `depth`, `index`, and `container_kind` are included only when `--organizer_order` is specified.

Each asset object may include:

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | Asset UUID |
| `type` | string | Asset object type |
| `kind` | string | Stable Curio asset kind for scripting, such as `organizerfolder`, `organizersection`, or `image` |
| `asset_file_kind` | string | Human-readable asset display/file kind, if applicable |
| `title` | string | Asset title |
| `tags` | `[string]` | Asset tags |
| `note` | string | Asset note text, if present |
| `asset_file` | string | Underlying file path, if the asset has one |
| `preview_image_file` | string | Existing stored JPEG or PDF preview path, if the asset has one |
| `link` | string | `curio://` link for an Organizer item, if applicable |
| `section_path` | string | Absolute path ending with the current or containing section |
| `organizer_path` | string | Separate absolute Organizer path within the containing section, including the item itself when applicable |
| `parent_id` | string | UUID of the containing Organizer parent; only present with `--organizer_order` |
| `parent_title` | string | Title of the containing Organizer parent; only present with `--organizer_order` |
| `depth` | number | Hierarchy depth relative to the requested section or parent; only present with `--organizer_order` |
| `index` | number | Zero-based position within the containing Organizer parent; only present with `--organizer_order` |
| `container_kind` | string | Kind of Organizer parent containing the asset, such as `section`, `folder`, or `ideaspace`; only present with `--organizer_order` |
| `added_date` | string | Added date in `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM` format |
| `modified_date` | string | Modified date in `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM` format |

### Organizer contents

Technically Organizer items are assets so you can use `curio assets` to get your Organizer tree as well.

- Use `--organizer_order` to return only organizer-visible items in the exact Organizer UI order for that section or parent, with folder contents nested under `children` .
- `--organizer_order` requires `--section` or `--parent` and can be combined with `--recursive` .
- In Organizer-order mode, `count` is the number of top-level objects in `items`, while `total_count` includes all descendants nested under `children`.
- When `--organizer_order` is specified, results include `parent_id` , `parent_title` , `depth` , `index` , and `container_kind` so callers can reconstruct the Organizer tree. Flat asset listings do not include these hierarchy fields.
- This mode is Organizer-facing, so it does not include figure assets that are contained within idea spaces, like PDF images.

To flatten or count the complete Organizer tree with `jq`:

```text
curio assets --section <uuid> --recursive --organizer_order |
  jq '[.result.items[] | recurse(.children[]?)] | length'
```

Example Organizer tree:

```text
Case 25
├── Deep Link
└── Deep Folder
    └── Item in Folder
```

The UUID for each returned asset can be used as the idea space / Organizer item UUID in calls such as `set` and `get`.

Each asset includes both a machine-readable `kind` and a human-readable `asset_file_kind`:

- `kind` is the stable Curio asset kind for scripting and matches the query language’s `kind=...` filter, with values such as `organizerfolder` , `organizersection` , `organizerideaspace` , `organizerspecialsection` , or `image` .
- `asset_file_kind` is the asset’s display/file kind, such as `Organizer Folder` or `PDF document` .

This split lets scripts filter by Curio item type using `kind` while still inspecting file-specific details using `asset_file_kind` and `asset_file`.

### Asset files, previews, and sidecars

The `asset_file` path points to the file stored by or linked to the Curio asset. For an embedded asset, scripts may place related sidecar files in the asset's containing folder. Curio keeps the whole folder together through asset renames and Organizer reorganization. If the asset is deleted, Curio deletes its containing folder, including any sidecars.

The read-only `preview_image_file` field points to an existing stored preview for an idea space or asset-backed figure. An explicitly requested field is `null` when no preview file exists; bulk `assets` results omit the field in that case. Preview files may be JPEG or PDF. Reading the field does not generate or refresh a missing or stale preview.

For shared Spread PDF assets, replacing `asset_file` while Curio is closed preserves the existing figure UUIDs and `display_page` references. Use a replacement with the same page count. Curio does not key ordinary PDF assets by content hash; sync-file hashing applies only to `sync_file` workflows.

## Exporting

The `export` command exports content from the running app to a file or the clipboard.
You can export a single figure by UUID or identifier, all selected figures, or selected idea spaces in the Organizer.

Supported export formats are `png`, `jpg`, `pdf`, `text`, `markdown`, and `rtf`.

Examples:

```text
curio export --id 12345678-1234-1234-1234-123456789ABC --format png --filename ~/Desktop/Figure
curio export --id 87654321-4321-4321-4321-CBA987654321 --format pdf --filename ~/Desktop/IdeaSpace
curio export --selected figures --format png --filename ~/Desktop/Selection
curio export --selected figures --format pdf --clipboard
curio export --selected figures --format markdown --filename ~/Desktop/Notes --include-assets
curio export --selected ideaspaces --format pdf --filename ~/Desktop/IdeaSpaces
curio export --selected ideaspace --format pdf --clipboard
curio export --selected figures --format png --filename ~/Desktop/Selection --overwrite
```

If `--filename` points to an existing folder then Curio will automatically name the export using the selected content’s title.

Without `--overwrite` an existing destination is never replaced. Even with `--overwrite`, exports refuse to replace your home folder and its standard subfolders, hidden configuration items in your home folder such as `~/.zshrc` and `~/.ssh`, and the `LaunchAgents`, `LaunchDaemons`, and `StartupItems` folders.

If `--id` names a figure, Curio navigates to that figure and exports the resulting single selection. If `--id` names an idea space, Curio exports that idea space directly.

!!! note "Note"
    `--clipboard` is supported for figure exports, and for `--selected ideaspace` only when exactly one idea space is selected and the format is `png`, `jpg`, or `pdf`.
    For file exports, `--selected ideaspace` supports `png` and `jpg` only when exactly one idea space is selected.
    Multi-idea-space file exports are supported for `pdf`, `text`, `markdown`, and rich text.

!!! note "Note"
    Rich text exports are requested with `--format rtf`, but the actual output may be `.rtf` or `.rtfd` depending on whether attachments are present.
    Markdown exports with `--include-assets` use a sibling `Assets/` folder, and repeated exports merge into that folder instead of replacing it. Exported markdown asset references are written to match the resulting asset folder layout.

## Importing

The `import` command creates a new figure on the current idea space from a file or from standard input.
It can also spread a multi-page PDF into one idea space per page.
It requires the running Curio app and does not support `--project`.

!!! important "Important"
    The Mac App Store edition of Curio runs in a sandbox that limits which file paths it can access.
    File import (`--as file`), Spread PDF import (`--as spread-pdf`), and file sync (`--sync`) work only when the source file is in an **accessible folder** — the app’s temporary directory, Downloads, your Curio projects folder, or any folder you have previously authorized in Curio.
    Run `curio status` to see the list of accessible folders for your installation.
    Content import (`--as text`, `list`, `mindmap`, `stack`, `table`) without `--sync` works with both editions regardless of file location, because the CLI reads the file and sends the content directly.

!!! tip "Tip"
    If you are scripting file imports against the Mac App Store edition, use the accessible folders list from `curio status` to find a staging location.
    The app’s temporary directory is always available and can be written to by any process running as the same user.
    Note that the sandboxed temporary directory is inside the app container (e.g. `~/Library/Containers/com.zengobi.curio/Data/tmp/`), not the system `/tmp`.
    The website (direct download) edition is not sandboxed and can access any file path.

### Importing a file as an asset figure

By default, `import` copies the file into the project as an asset figure (image, PDF, document, etc.):

```text
curio import ~/Photos/diagram.png
curio import ~/Documents/report.pdf --transfer move
curio import /Volumes/Ext/video.mp4 --transfer alias
```

The `--transfer` flag controls how the file is handled:

| Value | Description |
| --- | --- |
| `copy` | Copy into the project (default) |
| `move` | Move into the project |
| `alias` | Reference the file externally without copying |

### Spreading a PDF across idea spaces

Use `--as spread-pdf` to import a multi-page PDF as one stored PDF asset and one or more idea spaces.
By default, Curio creates one idea space per requested page.
Each created idea space contains a distinct PDF figure instance, so each page has its own figure UUID and `curio://` link while all figures share the same `asset_id` and `asset_file`.

Curio text and Markdown export describes the canvas; it does not extract text from embedded asset files. Scripts that need a PDF text layer or OCR output should process the returned `asset_file` externally and use each figure's `display_page` to associate page text with its Curio figure and deep link.

!!! note "Preview regeneration when opening a project"
    - By default, Curio silently regenerates missing or stale idea space previews after a project opens. It generates one preview at a time and returns to the event loop between idea spaces, so you can continue navigating and editing without a progress window interrupting your work.
    - By default, if an individual preview takes one second or longer and more previews remain, Curio pauses quiet regeneration for the rest of that project session to avoid a series of stalls. The completed preview remains fresh; outstanding previews can be updated manually or picked up the next time the project opens. You can [fine-tune these thresholds and diagnostics](/curio/docs/?advanced-settings/#idea-space-previews).
    - Incoming CLI automation takes priority. Curio stops preview generation after the current idea space, lets the CLI request run, then schedules another quiet preview pass after automation becomes idle.
    - Closing the project or quitting Curio also stops the quiet pass after the current idea space. Any remaining stale previews are picked up the next time the project opens. Automatic PDF Mirror projects still regenerate required previews when publishing during close or quit.
    - To maximize CLI speed by disabling this background work, [click here](curio://defaults-write?Preview%20Update%20On%20Project%20Open=no) to set `Preview Update On Project Open` to `no`. You can also use **Curio > Change Advanced Setting**. Set it back to `yes` to restore the default behavior.

```text
curio import ~/Documents/affidavit.pdf --as spread-pdf --section <sectionUUID> --title-template "{page:03}"
curio import ~/Documents/affidavit.pdf --as spread-pdf --section <sectionUUID> --pages 1-10,15
curio import ~/Documents/affidavit.pdf --as spread-pdf --selected ideaspace --position next_sibling
curio import ~/Documents/affidavit.pdf --as spread-pdf --section <sectionUUID> --page-titles titles.json
curio import ~/Documents/affidavit.pdf --as spread-pdf --section <sectionUUID> --layout layout.json
curio import ~/Documents/affidavit.pdf --as spread-pdf --section <sectionUUID> --dry-run
```

Spread PDF placement uses the same target and position model as `curio create`:

| Option | Description |
| --- | --- |
| `--section <uuid>` | Create page idea spaces under a section |
| `--parent <uuid>` | Create page idea spaces under a section or Organizer item |
| `--selected ideaspace` | Use the selected or active Organizer item as the placement reference |
| `--selected organizer_item` | Alias for `--selected ideaspace` |
| `--relative-to <uuid>` | Use an Organizer item UUID as the placement reference |
| `--position <pos>` | `first_child`, `last_child`, `first_sibling`, `previous_sibling`, `next_sibling`, or `last_sibling` |
| `--index <n>` | Numeric child index; cannot be combined with `--position` |

By default, `--pages all` creates one idea space for every PDF page.
Use a comma-separated page/range list such as `1-10,15` to import a subset.
Duplicate pages are rejected.

Use `--title-template` for generated idea space titles.
Supported tokens are `{page}`, `{page:03}`, `{page_start}`, `{page_end}`, `{page_count}`, and `{filename}`.
Use `--page-titles` when titles need to come from a manifest:

```json
[
  { "page": 1, "title": "2026-05-01 - Letter from Smith" },
  { "page": 2, "title": "p2" }
]
```

Use `--layout` when each idea space should contain a grid of PDF pages instead of a single page.
The number of pages per idea space is `columns * rows`.
The optional `figure` frame defines the first page figure, and `x_gap` / `y_gap` determine the offsets for the remaining columns and rows:

```json
{
  "columns": 2,
  "rows": 2,
  "x_gap": 50,
  "y_gap": 50,
  "figure": { "x": 72, "y": 72, "width": 420, "height": 540 },
  "title_template": "Pages {page_start}-{page_end}"
}
```

A layout file can also include `pages`.
Explicit command-line options such as `--pages` and `--title-template` override values in the layout file.

`--transfer copy` and `--transfer move` are supported.
`--transfer alias` is rejected for Spread PDF import because the operation creates a stored project asset that is shared by the created page figures.

The response includes `asset_id`, `asset_file`, and an `items` array with every created `ideaspace_id`, title, page range, and page figure.
For layout imports, each item includes a `figures` array with each figure’s `figure_id`, `display_page`, frame, and link.

### Importing content as a native figure

Use `--as` to choose a specific import mode instead of the default asset-figure import:

```text
curio import ~/notes.md --as text
curio import ~/outline.md --as list
curio import ~/outline.md --as mindmap
curio import ~/outline.md --as stack
curio import ~/data.csv --as table
```

| `--as` value | Description |
| --- | --- |
| `file` | Store the file as an asset figure (default when `--as` is omitted) |
| `spread-pdf` | Store one PDF asset and create one idea space per requested page |
| `text` | Create a text figure from the file content |
| `list` | Parse hierarchical text into a list collection |
| `mindmap` | Parse into a mind map collection |
| `stack` | Parse into a stack collection |
| `table` | Parse tabular text (CSV/TSV/markdown table) into a table collection |

The content format is auto-detected from the file extension:

| Extension | Format |
| --- | --- |
| `.md`, `.markdown` | Markdown |
| `.taskpaper` | TaskPaper |
| `.opml` | OPML |
| `.csv` | CSV |
| `.tsv` | TSV |
| `.txt`, other | Plain text |

Use `--ignore-inline-tags` or `--ignore-inline-resources` with a collection import (`list`, `mindmap`, `stack`, or `table`) to preserve inline `#tag` and `@resource` tokens in item text instead of converting them into hidden metadata. An ignored inline token does not create or associate a new tag or resource. Function-style metadata such as `@tags(...)` and `@resources(...)` is still processed normally.

```text
curio import ~/outline.md --as list --ignore-inline-tags --ignore-inline-resources
```

### Reading content from standard input

Use `--stdin` to read content from standard input instead of a file.
This only works with content types (`--as text`, `list`, `mindmap`, `stack`, or `table`).

```text
echo "- Item 1\n- Item 2" | curio import --stdin --as list
pbpaste | curio import --stdin --as text
cat data.tsv | curio import --stdin --as table --content-format tsv
```

Since stdin has no file extension, the content format defaults to Markdown (or CSV when `--as table`).
Use `--content-format` to override this default:

```text
curio import --stdin --as list --content-format taskpaper
curio import --stdin --as table --content-format tsv
curio import --stdin --as mindmap --content-format opml
```

Supported `--content-format` values: `markdown`, `taskpaper`, `opml`, `csv`, `tsv`, `plain`.

### Setting up a sync file

Use `--sync` to create a content figure that stays synchronized with the source file.
This requires a file path (not `--stdin`) and a content type (`--as text`, `list`, `mindmap`, `stack`, or `table`).

```text
curio import ~/todo.md --as list --sync bidirectional
curio import ~/brainstorm.md --as mindmap --sync import
curio import ~/notes.md --as text --sync export
```

| `--sync` value | Description |
| --- | --- |
| `bidirectional` | Changes flow both ways between the figure and the file |
| `export` | Figure changes export to the file |
| `import` | File changes import into the figure |

For synced collection figures, Curio's Markdown exporter appends an HTML comment such as `<!--id:Ab3-->` to each item. Preserve those markers when an external generator rewrites the file: they allow Curio to retain each item's figure UUID. Without them, imported entries are treated as new figures and can lose figure-only styling, metadata, or links.

### Import response

The response includes the created figure’s UUID so you can immediately use `get` or `set` on it:

```json
{
  "api_version": 1,
  "app_version": "26.1",
  "response_ok": true,
  "result": {
    "figure_id": "12345678-1234-1234-1234-123456789ABC",
    "figure_kind": "list",
    "title": "todo"
  }
}
```

When `--sync` is used, the response also includes `sync_file` and `sync_direction`.

Ordinary file and content imports update the live project immediately, so the returned UUID can be used by `get`, `set`, or `query` without an intervening save. Before a subsequent read or save, Curio settles deferred figure synchronization, query-backed collection refreshes, cache invalidation, and document edited-state propagation. Imports do not force a disk save per item. Batch imports and related mutations, then call `curio save` once. Spread PDF imports are the exception: they settle and save once after the full spread is created.

## Saving

Use `save` as a synchronous persistence barrier after a sequence of live mutations:

```text
curio save
curio save --project-id 12345678-1234-1234-1234-123456789ABC
```

Without `--project-id`, Curio saves the current project. Use a `project_uuid` returned by `curio status` to save a different open project without bringing its window to the front. Before saving, Curio settles deferred model propagation; the command returns only after Curio has prepared every window controller and saved the project’s Organizer state and page drawings.

An untitled project must first be saved through Curio so it has a stable path. Read-only projects cannot be saved through the CLI.

`curio save` does not close the project. If a script closes Curio through AppleScript, use `saving yes`; a bare `close` does not provide the same explicit persistence barrier. AppleScript's `documents` collection also contains Curio's internal Stencils and IdeaSpaceTemplates documents, whereas `curio status` lists user projects, so do not assume those collections have matching names or members.

## JSON Output and Exit Codes

All commands print JSON responses.
On success the response contains `response_ok: true`; on failure it contains `response_ok: false` plus an `error` dictionary.

A typical success response looks like this:

```json
{
  "api_version": 1,
  "app_version": "25.1",
  "response_ok": true,
  "result": {
    "items": [
      {
        "id": "fig-abc",
        "title": "Write docs",
        "priority": 4
      }
    ]
  }
}
```

Exit codes are:

- `0` for success
- `1` for an API or runtime error
- `2` for invalid command line usage

## Common Errors

| Error | Meaning |
| --- | --- |
| `NoSelection` | Nothing is selected |
| `MultipleSelection` | More than one figure is selected when a single figure is required |
| `SelectionNotTextCapable` | The selected figure does not support text for the requested operation |
| `FigureNotFound` | The requested UUID or identifier could not be found |
| `ReadOnlyField` | You tried to write a read-only field |
| `ReadOnlyTransport` | You tried to write through a read-only transport |
| `NotActivated` | Curio needs to be launched to refresh CLI activation |
| `InvalidRequest` | The command or supplied values were not valid, or an identifier matched multiple targets |
| `Busy` | Another automation request is still finishing; the client retries briefly before returning this error |
| `InternalError` | An unexpected internal failure occurred |

!!! tip "Tip"
    If you are scripting against the CLI, check the `response_ok` field in the JSON response and the process exit code.

## Troubleshooting

If a command says it cannot connect, Curio is not running and the command requires the live app.
Launch Curio and try again.

If `--project` mode returns `NotActivated`, launch Curio once so it can refresh the CLI activation token.

If a selection-based command fails, confirm that the correct project window is frontmost and that the expected figures or Organizer items are selected.

## Settings

The CLI supports user settings in `~/.curio/settings.json`. Use `curio settings` to inspect and update the file instead of editing it by hand:

```text
curio settings list
curio settings get query.max_chars
curio settings set query.max_chars=4000
curio settings set output.pretty_json=no
curio settings remove query.max_chars
curio settings path
curio settings reveal
```

Settings are addressed by dotted keys and stored as nested JSON. Explicit command-line flags still win over settings. Boolean settings accept `true`/`false`, `yes`/`no`, `on`/`off`, and `1`/`0`. Use `curio settings reveal` to create the settings file if needed and reveal it in Finder.

| Setting | Type | Default | Description |
| --- | --- | --- | --- |
| `query.max_chars` | integer | unset | Default text truncation for `curio query`; explicit `--max-chars` wins. |
| `output.pretty_json` | boolean | `true` | Pretty-print normal JSON responses. Set to `no` for compact script output. |
| `output.color` | string | `auto` | Terminal styling: `auto`, `always`, or `never`. |
| `diagnostics.verbose` | boolean | `false` | Enable verbose diagnostics by default. |
| `batch.continue_on_error` | boolean | `false` | Continue batch execution after a failed line unless stopped by another fatal condition. |
| `mcp.log_path` | string | unset | Default log path for `curio mcp-server`; explicit `--log` wins. |
| `transport.send_timeout_seconds` | number | `10.0` | Timeout for sending requests to the running Curio app. |
| `transport.receive_timeout_seconds` | number | `10.0` | Timeout while waiting for the running Curio app to reply. |
| `curio.install_preference` | string | `auto` | Preferred Curio install for direct project reads when both editions are present: `auto`, `direct`, or `mas`. |
