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

# Using Linkly AI CLI

> Learn how to use Linkly AI CLI for greater flexibility and a more technical experience.

## Introduction to Linkly AI CLI

Linkly AI CLI is a command-line tool that connects to Linkly AI Desktop's MCP service, allowing you to search, browse, and read local documents from the terminal. It also serves as a bridge between AI Agents (such as Claude Desktop, Cursor) and Linkly AI.

<CardGroup cols={2}>
  <Card title="Terminal Search" icon="terminal" iconType="duotone">
    Search your documents directly from the command line — ideal for developers and power users
  </Card>

  <Card title="MCP Bridge" icon="plug" iconType="duotone">
    Run in stdio MCP mode, enabling Claude Desktop, Cursor, and other AI tools to call Linkly AI
  </Card>
</CardGroup>

## Installation

<Tabs>
  <Tab title="macOS / Linux" icon="apple">
    Run in your terminal:

    ```bash theme={null}
    curl -sSL https://updater.linkly.ai/cli/install.sh | sh
    ```

    Or install via Homebrew:

    ```bash theme={null}
    brew tap LinklyAI/tap
    brew install linkly
    ```
  </Tab>

  <Tab title="Windows" icon="windows">
    Run in PowerShell:

    ```powershell theme={null}
    irm https://updater.linkly.ai/cli/install.ps1 | iex
    ```
  </Tab>

  <Tab title="Cargo" icon="box">
    Install from [crates.io](https://crates.io/crates/linkly-ai-cli) (requires Rust toolchain):

    ```bash theme={null}
    cargo install linkly-ai-cli
    ```
  </Tab>
</Tabs>

After installation, verify the installation:

```bash theme={null}
linkly --version
```

<Note>By default, the CLI discovers and connects to the local Linkly AI Desktop app via `~/.linkly/port`. You can also connect to a remote device via LAN or the cloud tunnel — see [Connection Modes](#connection-modes) below.</Note>

## Usage

The CLI follows a **search → grep or outline → read** progressive workflow: first search to find target documents, then use grep to find patterns or view the outline to understand the structure, and finally read the specific content. When the user describes a container ("in my Notion notes", "in my Dropbox papers folder") whose actual path is unknown, call `find-paths` before `search` to discover the path.

<Note>Every successful command output ends with a `[meta] now=2026-05-08T...Z` UTC timestamp line (or a top-level `_meta.now` field in JSON mode). This is metadata Desktop provides to AI assistants for computing relative dates like "last month" — human users can ignore it; for scripting, you may want to filter out the last line before further parsing.</Note>

### Check Connection Status

```bash theme={null}
linkly status
```

Returns the running status, version number, number of indexed documents, and indexing status of Linkly AI Desktop.

### Search Documents

```bash theme={null}
linkly search "keywords or phrases"
```

Searches your local documents and returns the most relevant results, including title, path, relevance score, and content snippet.

**Common parameters:**

```bash theme={null}
# Limit the number of results (default 20, max 50)
linkly search "API design" --limit 5

# Filter by document type
linkly search "meeting minutes" --type pdf,docx

# Output in JSON format (suitable for scripting)
linkly search "budget report" --json

# Limit by time window (Q3 2024 quarterly reports)
linkly search "quarterly report" --modified-after 2024-07-01 --modified-before 2024-09-30

# Sort by time ("the latest", "the earliest" — when there's no fixed window)
linkly search "weekly retro" --time-sort newest --limit 5

# Search audio and video by what was said in them
linkly search "we decided to postpone" --type audio,video

# Search your notes instead of your files
linkly search "onboarding checklist" --scope notes
```

<Tip>`--modified-after` / `--modified-before` accept ISO 8601 UTC: a bare date `2024-01-01` (treated as `00:00:00Z`) or a full RFC 3339 timestamp `2024-01-01T00:00:00Z`. `--time-sort` accepts `newest` / `oldest`; omit it to keep the default BM25 + vector relevance order.</Tip>

<Note>`--scope notes` restricts results to your notes and **ignores `--library` and `--path-glob`** — those filters are silently dropped rather than rejected.</Note>

### View Document Outline

```bash theme={null}
linkly outline <DOC_ID>
```

Retrieves the structured outline and metadata of a document. `DOC_ID` is obtained from search results. You can view multiple documents at once, or pipe the IDs in with `-`:

```bash theme={null}
linkly outline id1 id2 id3

# Outline everything a search found, without a shell loop
linkly search "architecture" --json | jq -r '.results[].doc_id' | linkly outline -
```

<Tip>The outline feature works best with Markdown, DOCX, DOC, PowerPoint (PPTX), XLSX, CSV, EPUB, and RTF documents, as their heading structures can be parsed. For plain text or PDFs without bookmarks, it's recommended to use the `read` command directly.</Tip>

### Search Patterns in Documents

```bash theme={null}
linkly grep <PATTERN> <DOC_ID>...
```

Search for regex pattern matches inside one or more documents. Use when you need to find specific text (terms, names, dates, identifiers, etc.):

```bash theme={null}
# Find a pattern in a document
linkly grep "useState" 456

# Case-insensitive with context lines
linkly grep "error|warning" 1044 -C 3 -i

# Count matches only
linkly grep "TODO" 591 --mode count

# Paginate matches (skip first 20, show next 20)
linkly grep "import" 302 --offset 20 --limit 20

# Grep across everything a search found
linkly search "deployment" --json | jq -r '.results[].doc_id' \
  | linkly grep "docker|kubernetes" -
```

### Read Document Content

```bash theme={null}
linkly read <DOC_ID>...
```

Reads the full content of one or more documents, outputting text with line numbers. For long documents, you can read in pages:

```bash theme={null}
# Start from line 50, read 100 lines
linkly read <DOC_ID> --offset 50 --limit 100

# Include the OCR text of images referenced in the page
linkly read <DOC_ID> --image-text full

# Read several documents at once, or pipe the IDs in
linkly read id1 id2 id3
linkly search "onboarding" --json | jq -r '.results[].doc_id' | linkly read -
```

<Tip>With `--json`, several documents print as **JSON Lines** — one object per line. A single ID still prints a single object, so existing scripts are unaffected.</Tip>

**Pagination strategy:** By default, 200 lines are read per request (max 500 lines). For long documents, adjust `--offset` to read progressively:

```bash theme={null}
linkly read <DOC_ID> --offset 1 --limit 200    # Lines 1-200
linkly read <DOC_ID> --offset 201 --limit 200  # Lines 201-400
linkly read <DOC_ID> --offset 401 --limit 200  # Lines 401-600
```

### Find Paths (find-paths)

```bash theme={null}
linkly find-paths --patterns <keyword,keyword,...>
```

Fuzzy-matches keywords against the **file path** field of indexed documents, aggregates matches at folder granularity, and returns the top folder candidates. It is positioned as a helper for `search`: when the user names a container ("in my Notion notes", "in my Dropbox papers folder") but you don't know its on-disk path, call `find-paths` first, then pass a distinctive segment of the returned path to `search` as `--path-glob`. When a folder name contains glob metacharacters (`* ? [`), use the returned `path_glob` field directly — it is already escaped to match that folder literally.

**Two-step workflow:**

```bash theme={null}
# Step 1: locate the real path
linkly find-paths --patterns Notion,notion --limit 5
# Suppose it returns .../Documents/Notion-Export-abc/workspace (1240 files)

# Step 2: search within that container
linkly search "shopping receipt" --path-glob "*Notion-Export*"
```

**Variant matching:** `--patterns` takes a comma-separated list of keywords, OR-matched against the path. **Pass several variants in one call** (translation pairs, casing, real app/SDK identifiers when known) to maximise first-pass recall:

```bash theme={null}
linkly find-paths --patterns Notion,notion,Notion-Export
linkly find-paths --patterns Slack,slack --library work-notes
```

<Tip>`find-paths` is a "find folders" tool, not a "find files" tool: only matches on **directory segments** count. If keywords match only the filename segment (an "orphan file"), they are silently dropped. If a query returns zero folders even though you expect matching files, fall back to calling `linkly search` directly without `--path-glob`.</Tip>

### Notes

Linkly AI keeps short Markdown notes in your library folder. They are ordinary local files — never uploaded — and they are indexed like any other document.

```bash theme={null}
# List them, newest first
linkly list --scope notes

# Filter by tag
linkly list --scope notes --tags project,urgent

# Write one
linkly note-save --mode create --content "Remember to renew the certificate" --tags ops

# Pipe a long body in instead of quoting it
some-command | linkly note-save --mode create --content -
```

Editing an existing note needs its `note_id` and current `version`, both of which `linkly list --scope notes` returns:

```bash theme={null}
linkly note-save --mode edit \
  --note-id <uuid> --base-version <version> \
  --tags ops --content "Updated text"
```

<Warning>Inline `#tags` in the note body are the note's tags — `--tags` only **adds** (remove a tag by deleting its `#tag` marker from the content). Desktops older than 0.11.0 instead require `--tags` on edit and treat it as the full replacement set. `--base-version` is a concurrency check: if the note changed since you read it, the command fails with `NOTE_VERSION_CONFLICT` instead of overwriting. Note content is a restricted Markdown subset (paragraphs, bold, strikethrough, lists); headings, code, links and tables are rejected.</Warning>

### Shell Completions

```bash theme={null}
linkly completions <SHELL>
```

Prints a completion script for `bash`, `zsh`, `fish`, `powershell` or `elvish`.

<Tabs>
  <Tab title="zsh">
    ```bash theme={null}
    mkdir -p ~/.zfunc
    linkly completions zsh > ~/.zfunc/_linkly
    # Add to ~/.zshrc if not already there:
    #   fpath=(~/.zfunc $fpath)
    #   autoload -Uz compinit && compinit
    ```
  </Tab>

  <Tab title="bash">
    ```bash theme={null}
    linkly completions bash > /usr/local/etc/bash_completion.d/linkly
    ```
  </Tab>

  <Tab title="fish">
    ```bash theme={null}
    linkly completions fish > ~/.config/fish/completions/linkly.fish
    ```
  </Tab>

  <Tab title="PowerShell">
    ```powershell theme={null}
    linkly completions powershell | Out-String | Invoke-Expression
    ```
  </Tab>
</Tabs>

Open a new shell afterwards. The script is static — it never contacts Linkly AI Desktop, so it works with the app closed and can't slow down your prompt.

### MCP Mode

```bash theme={null}
linkly mcp
```

Runs as a stdio MCP server, exposing Linkly AI's tools to MCP-compatible AI clients.

The upstream decides what the client can reach:

| Command                       | Reaches                                                          |
| ----------------------------- | ---------------------------------------------------------------- |
| `linkly mcp`                  | The local desktop app — your local content only                  |
| `linkly mcp --endpoint <url>` | A desktop app on the LAN — same content boundary                 |
| `linkly mcp --remote`         | The cloud gateway — local content **and** linked cloud libraries |

<Tip>`--remote` is the only bridge mode that can reach cloud libraries. It needs an API key saved first (see [Remote Mode](#remote-mode)).</Tip>

**Configure Claude Desktop and other local AI apps:**

Add the following to the configuration file of Claude Desktop or similar apps:

<Tabs>
  <Tab title="macOS / Linux">
    Edit `~/.config/Claude/claude_desktop_config.json`:

    ```json theme={null}
    {
      "mcpServers": {
        "linkly-ai": {
          "command": "linkly",
          "args": ["mcp"]
        }
      }
    }
    ```
  </Tab>

  <Tab title="Windows">
    Edit `%APPDATA%\Claude\claude_desktop_config.json`:

    ```json theme={null}
    {
      "mcpServers": {
        "linkly-ai": {
          "command": "linkly",
          "args": ["mcp"]
        }
      }
    }
    ```
  </Tab>
</Tabs>

**Configure Cursor:**

In Cursor, open Settings → MCP Servers → Add Server, and add:

* Name: `linkly-ai`
* Command: `linkly mcp`

### Update CLI

```bash theme={null}
linkly self-update
```

Automatically checks for and updates to the latest version. The CLI also checks for updates in the background on each launch and will prompt you to run this command if a new version is available.

## Connection Modes

The CLI supports three ways to connect to your Linkly AI knowledge base:

| Mode       | Flags                              | How it works                                                    |
| ---------- | ---------------------------------- | --------------------------------------------------------------- |
| **Local**  | *(default, no flags needed)*       | Auto-discovers the desktop app via `~/.linkly/port`             |
| **LAN**    | `--endpoint <url> --token <token>` | Direct connection to another device on the local network        |
| **Remote** | `--remote`                         | Connects via `https://mcp.linkly.ai` cloud tunnel using API Key |

### Local Mode (default)

No extra flags needed. The CLI reads `~/.linkly/port` to find the running desktop app:

```bash theme={null}
linkly search "machine learning"
linkly status
```

### LAN Mode

Connect to a Linkly AI instance running on another device in your local network. The token can be found in the desktop app under **Settings → MCP**:

```bash theme={null}
linkly search "report" --endpoint http://192.168.1.100:60606/mcp --token your_lan_token
linkly status --endpoint http://192.168.1.100:60606/mcp --token your_lan_token
```

### Remote Mode

Connect to your knowledge base from anywhere via the cloud tunnel. First, save your API key (from [linkly.ai/dashboard](https://linkly.ai/dashboard)):

```bash theme={null}
linkly auth set-key lkai_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

Then use `--remote` with any command:

```bash theme={null}
linkly search "machine learning" --remote
linkly status --remote
```

Check or clear the stored key at any time:

```bash theme={null}
linkly auth status   # which key is in use, whether it's valid, and your plan
linkly auth logout   # remove the stored credentials
```

<Warning>`--endpoint` and `--token` are required together for LAN access. They cannot be combined with `--remote`. For remote access, use `linkly auth set-key` to save your API key.</Warning>

## Parameter Reference

### Global Options

| Option             | Scope        | Description                                                                                    |
| ------------------ | ------------ | ---------------------------------------------------------------------------------------------- |
| `--endpoint <URL>` | LAN          | Connect to a specific MCP endpoint (e.g. `http://192.168.1.100:60606/mcp`), requires `--token` |
| `--token <token>`  | LAN          | Bearer token for LAN authentication (required with `--endpoint`, conflicts with `--remote`)    |
| `--remote`         | Remote       | Connect via cloud tunnel at `https://mcp.linkly.ai` (conflicts with `--endpoint`)              |
| `--json`           | All commands | Output in JSON format (suitable for scripting and automation)                                  |
| `--exit-code`      | All commands | Distinguish "found nothing" (exit 1) from "failed" (exit 2) — see [Exit Codes](#exit-codes)    |
| `--client <name>`  | All commands | Name the calling application, e.g. `--client claude-code`. Agents should always pass it        |
| `-V, --version`    | —            | Display CLI version number                                                                     |
| `-h, --help`       | —            | Display help information                                                                       |

<Tip>`--endpoint`, `--token` and `--remote` are available on the document commands (`search`, `grep`, `outline`, `read`, `list`, `note-save`, `find-paths`, `explore`, `list-libraries`) plus `status` and `doctor`. The `mcp` command accepts `--endpoint` or `--remote`, but not `--token`. `--json` and `--exit-code` are available everywhere.</Tip>

### Exit Codes

By default the CLI uses the conventional two values: **0** on success, **1** on failure. Note that "success" includes finding nothing — a search with no hits still exits 0.

Pass `--exit-code` to tell the two apart:

| Code | Meaning                                                    |
| ---- | ---------------------------------------------------------- |
| `0`  | Ran and produced at least one result                       |
| `1`  | Ran fine but matched nothing                               |
| `2`  | Failed (connection, authentication, bad arguments, errors) |

```bash theme={null}
# Only opens the report when the search actually found something
linkly search "quarterly report" --exit-code && open-report
```

<Note>The flag is opt-in because it changes what `1` means. Without it, `1` is "failed" — which is what existing scripts test for.</Note>

### search Parameters

| Parameter                 | Description                                                                                                                                                  | Default |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- |
| `<QUERY>`                 | Search keywords or phrase (required)                                                                                                                         | —       |
| `--limit <N>`             | Maximum number of results (1-50)                                                                                                                             | 20      |
| `--type <TYPES>`          | Filter by document type (comma-separated): `pdf`, `docx`, `doc`, `pptx`, `xlsx`, `csv`, `epub`, `rtf`, `md`, `txt`, `html`, `image`, `audio`, `video`        | All     |
| `--library <NAME>`        | Restrict search to a specific library                                                                                                                        | —       |
| `--path-glob <PATTERN>`   | Filter by file path; substring-matched (may appear anywhere, no leading/trailing `*` needed). When the actual path is unknown, run `linkly find-paths` first | —       |
| `--modified-after <ISO>`  | Inclusive lower bound on modification time. ISO 8601 UTC: bare date `2024-01-01` or full `2024-01-01T00:00:00Z`                                              | —       |
| `--modified-before <ISO>` | Inclusive upper bound on modification time. Same format as `--modified-after`                                                                                | —       |
| `--time-sort <MODE>`      | Time-based reordering: `newest` / `oldest`. Omit to keep relevance ordering                                                                                  | —       |
| `--scope <SCOPE>`         | `folder` searches all indexed content; `notes` restricts results to your notes and ignores `--library` / `--path-glob`                                       | folder  |
| `--tags <LIST>`           | Comma-separated note tags; returns only documents carrying **all** of them                                                                                   | —       |

### find-paths Parameters

| Parameter           | Description                                                                                                                                                                           | Default |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `--patterns <LIST>` | Comma-separated keyword list (required). Multiple keywords are OR-matched; pass several variants in one call (translation pairs, casing). Case-insensitive for ASCII; CJK is literal. | —       |
| `--library <NAME>`  | Restrict to a specific library                                                                                                                                                        | —       |
| `--limit <N>`       | Maximum number of candidate folders (1-50)                                                                                                                                            | 10      |

### outline Parameters

| Parameter | Description                                                                                   | Default |
| --------- | --------------------------------------------------------------------------------------------- | ------- |
| `<ID>...` | Document IDs (required, supports multiple). Pass `-` to read the IDs from stdin, one per line | —       |

### grep Parameters

| Parameter            | Description                                                                                             | Default |
| -------------------- | ------------------------------------------------------------------------------------------------------- | ------- |
| `<PATTERN>`          | Regular expression pattern (required)                                                                   | —       |
| `<DOC_ID>...`        | Document IDs to search (required, supports multiple). Pass `-` to read the IDs from stdin               | —       |
| `-C, --context`      | Lines of context before and after                                                                       | 3       |
| `-B, --before`       | Lines of context before each match                                                                      | —       |
| `-A, --after`        | Lines of context after each match                                                                       | —       |
| `-i`                 | Case-insensitive matching                                                                               | —       |
| `--mode`             | Output mode: `content` or `count`                                                                       | content |
| `--limit`            | Maximum matches (max 100)                                                                               | 20      |
| `--offset`           | Number of matches to skip                                                                               | 0       |
| `--fuzzy-whitespace` | Fuzzy whitespace matching: `true` to force on, `false` to force off, omit for auto (PDF on, others off) | auto    |

### read Parameters

| Parameter               | Description                                                                                                     | Default  |
| ----------------------- | --------------------------------------------------------------------------------------------------------------- | -------- |
| `<ID>...`               | Document IDs (required, supports multiple). Pass `-` to read the IDs from stdin                                 | —        |
| `--offset <N>`          | Starting line number (from 1)                                                                                   | 1        |
| `--limit <N>`           | Number of lines to read (max 500)                                                                               | 200      |
| `--image-text <DETAIL>` | Detail for images referenced in the page: `none` (mapping only), `abstract` (excerpt), `full` (inline OCR text) | abstract |

### list Parameters

| Parameter                    | Description                                                                                                           | Default   |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------- | --------- |
| `--scope <SCOPE>`            | Container to list: `folder` (indexed files under a directory), `library` (one library's files), or `notes` (required) | —         |
| `--library <LIB>`            | Which library, for `--scope library` — a name, `local://<id>`, or `cloud://<owner>/<slug>` (remote)                   | —         |
| `--path <DIR>`               | Directory to list — an absolute path locally, a relative prefix for cloud libraries                                   | —         |
| `--type <LIST>`              | Comma-separated document types (`folder` / `library` only), e.g. `pdf,docx`                                           | all       |
| `--modified-after <DATE>`    | Only items modified on or after this date (`folder` / `library` only). ISO 8601, e.g. `2026-01-01`                    | —         |
| `--modified-before <DATE>`   | Only items modified on or before this date. Same format                                                               | —         |
| `--tags <LIST>`              | Comma-separated tags (`notes` only); returns only items carrying **all** of them                                      | —         |
| `--limit <N>`                | Maximum items (max 200; capped at 50 while snippets are on)                                                           | 50        |
| `--offset <N>`               | Pagination offset                                                                                                     | 0         |
| `--sort <ORDER>`             | `recent` (newest first), `oldest`, or `name`                                                                          | recent    |
| `--snippet` / `--no-snippet` | Force per-item snippets on / off (mutually exclusive). Snippets default on for `notes`, off for `folder` / `library`  | per scope |

### note-save Parameters

| Parameter               | Description                                                                                                   | Default |
| ----------------------- | ------------------------------------------------------------------------------------------------------------- | ------- |
| `--mode <MODE>`         | `create` writes a new note; `edit` rewrites one (requires `--note-id` and `--base-version`)                   | —       |
| `--content <TEXT>`      | Note body (required). Pass `-` to read it from stdin                                                          | —       |
| `--note-id <UUID>`      | Which note to edit, from `linkly list --scope notes`                                                          | —       |
| `--base-version <HASH>` | The note's current version, from `linkly list --scope notes`. Rejects the write if the note changed meanwhile | —       |
| `--tags <LIST>`         | Comma-separated tags to **add** — remove a tag by deleting its `#tag` marker from the content                 | —       |
| `--app-name <NAME>`     | Display name of the application hosting the conversation, shown as the note's source badge. Max 64 characters | —       |

### completions Parameters

| Parameter | Description                                                | Default |
| --------- | ---------------------------------------------------------- | ------- |
| `<SHELL>` | `bash`, `zsh`, `fish`, `powershell` or `elvish` (required) | —       |
