npm package: @tailor-app/cli
Description: Tailor CLI — AI intelligence platform for documents, legislation, education, and healthcare. Upload, review, sign, and collaborate with AI agents using PACT protocol.
Latest version: 0.13.0

# Tailor CLI

Command-line tool for uploading, sharing, and managing documents on [Tailor](https://tailor.au).

## Quickstart

```bash
npm install -g @tailor-app/cli
tailor login --email you@company.com
tailor upload ./my-document.docx --share
```

## Installation

### From npm (recommended)

```bash
npm install -g @tailor-app/cli
tailor --help
```

Requires Node.js 20+.

### From the repo (development)

```bash
cd tools/tailor-cli
npm install
npm run build
```

Run commands via `node bin/tailor.js <command>` or set up a shell alias.

## PACT Protocol vs Tailor CLI

The PACT protocol has its own standalone CLI ([`@pact-protocol/cli`](https://github.com/TailorAU/pact)) for vendor-neutral coordination. Here's how it relates to the Tailor CLI:

| Package | Scope | When to use |
|---------|-------|-------------|
| `@pact-protocol/cli` | Coordination only: join, intent, constrain, object, poll, done, escalate | Building framework-agnostic PACT integrations that work with any PACT-compliant server |
| `@tailor-app/cli` | Everything: PACT coordination + document content + platform features | Working on the Tailor platform — uploads, reviews, sharing, signing, plus PACT via `tailor tap` |

The Tailor CLI is a **superset**. It includes everything `@pact-protocol/cli` does (via `tailor tap`) plus Tailor-specific content and platform operations. If you're building on Tailor, use `@tailor-app/cli`. If you're building a framework-agnostic PACT integration, use `@pact-protocol/cli`.

### `tailor tap` Command Classification

**Coordination** (also available in `@pact-protocol/cli`):
`tap join`, `tap leave`, `tap agents`, `tap intent`, `tap intents`, `tap constrain`, `tap constraints`, `tap salience`, `tap salience-map`, `tap object`, `tap poll`, `tap watch`, `tap done`, `tap completions`, `tap lock`, `tap unlock`, `tap escalate`, `tap ask-human`, `tap resolve`

**Content operations** (Tailor-specific):
`tap get`, `tap sections`, `tap propose`, `tap proposals`, `tap approve`, `tap reject`, `tap events`, `tap diff`

**Platform operations** (Tailor-specific):
`upload`, `download`, `share`, `sign`, `list`, `status`, `docs`, `reviews`, `comments`, `changes`, `tag`, `keys`, `admin`, `attach`

## Product Modules

Tailor has grown beyond documents into vertical product modules. The CLI currently covers the core platform; module-specific commands are planned.

| Module | Backend | CLI Status | MCP Status | Notes |
|--------|---------|-----------|------------|-------|
| **Core (Documents)** | `src/WebApi/Features/` | 21 commands, 40 MCP tools | Available | Full coverage |
| **Source** | `sites/source/` | Planned | Planned | Verified knowledge graph — `source.tailor.au/api/...` (REST only today) |
| **Spark** | `src/WebApi/Features/Spark/` | Planned | Planned | Early learning CCMS — NQF evidence, learning stories, parent portal |
| **Praxis** | `src/WebApi/Features/Praxis/` | Planned (low) | Planned (low) | GP practice management — consultations, patients, MBS coding |
| **Voice** | `src/WebApi/Features/TailorVoice/` | N/A | N/A | AI phone agent — real-time telephony, webhook-driven |
| **PACT protocol site** | `sites/pacthub/` (`pacthub.ai`) | N/A | N/A | Protocol marketing/spec site — not a live consensus dashboard or KG UI |

### Planned CLI Commands

```
tailor source search --jurisdiction QLD --query "workplace safety"
tailor source facts --id fact_abc123
tailor source ingest --url "https://legislation.qld.gov.au/..."

tailor spark students --school <schoolId>
tailor spark stories --student <studentId>
tailor spark nqf evidence --student <studentId>
```

### Planned MCP Tools

| Tool | Module | Purpose |
|------|--------|---------|
| `tailor_source_search` | Source | Search verified legislation by jurisdiction/keyword |
| `tailor_source_fact` | Source | Retrieve a specific verified fact by ID |
| `tailor_source_ingest` | Source | Submit legislation for ingestion |
| `tailor_spark_students` | Spark | List/manage students for a school |
| `tailor_spark_story` | Spark | Generate or read learning stories |

## Authentication

The CLI uses API keys for authentication. Keys are prefixed with `tailor_sk_` and sent as `X-Api-Key` headers.

### For AI agents and CI/CD (recommended)

Set an environment variable — no `tailor login` needed:

```bash
export TAILOR_API_KEY=tailor_sk_your_key
tailor list   # works immediately
```

Optionally set the base URL (defaults to `https://api.tailor.au`):

```bash
export TAILOR_BASE_URL=https://api.tailor.au
```

Environment variables take precedence over stored configuration.

### Store a key to disk

```bash
tailor login --key tailor_sk_your_key --url https://api.tailor.au
```

### Verify current auth

```bash
tailor login --check
```

### Interactive login (humans only)

```bash
tailor login --email you@company.com
```

> **Note:** `tailor login` without flags is interactive and requires a terminal. In non-TTY environments (CI, AI agents), it prints guidance and exits.

## Command Reference

### `tailor login`

Configure API key and base URL for the CLI.

```bash
tailor login --key tailor_sk_abc123                # Store API key
tailor login --key tailor_sk_abc123 --url https://api.tailor.au
tailor login --check                               # Verify current auth
tailor login --email you@company.com               # Interactive magic link
tailor login                                       # Fully interactive (TTY only)
```

**Non-interactive two-step login (for AI agents):**

```bash
tailor login --email you@company.com                  # Step 1: sends code, exits
tailor login --email you@company.com --code 874653    # Step 2: verifies, saves token
```

**Options:**
| Flag | Description | Default |
|------|-------------|---------|
| `--key <apiKey>` | API key (must start with `tailor_sk_`) | — |
| `--url <baseUrl>` | Base URL for the Tailor API | `https://api.tailor.au` |
| `--check` | Validate current auth and exit (no prompts) | — |
| `--email <email>` | Account email for magic link auth | — |
| `--code <code>` | Verification code (use with `--email` for non-interactive login) | — |
| `--code-only` | Skip browser auth, use code entry only | — |

---

### `tailor upload <files...>`

Upload one or more documents. Supports files, folders, and glob patterns.

```bash
tailor upload ./report.docx
tailor upload ./report.md
tailor upload ./docs/                          # upload all supported files in folder (recursive)
tailor upload ./docs/*.docx
tailor upload ./deliverables/*.docx --share --public
tailor upload ./budget.xlsx --share --permission CommentOnly
tailor upload ./docs/*.docx --json
tailor upload --stdin --title "Pitch Deck" < one-pager.html
cat one-pager.html | tailor upload --stdin --title "Pitch Deck" --format html
python build.py | tailor upload --stdin --title "Generated Report" --json
```

When a folder is passed, it is scanned recursively for supported file types (`.docx`, `.pdf`, `.html`, `.htm`, `.md`).

**Arguments:**
| Argument | Description |
|----------|-------------|
| `<files...>` | One or more file paths, folder paths, or glob patterns |

**Options:**
| Flag | Description | Default |
|------|-------------|---------|
| `--share` | Create shareable links after upload | `false` |
| `--permission <perm>` | Share permission: `ReadOnly`, `CommentOnly`, `FullReview` | `ReadOnly` |
| `--public` | Public links (no email verification) | `false` |
| `--stdin` | Read file content from stdin (pipe-friendly) | `false` |
| `--title <title>` | Override document title after upload | — |
| `--format <fmt>` | Content format for stdin: `html`, `md`, `docx` | `html` |
| `--json` | Output results as JSON (suppresses human UI) | `false` |

---

### `tailor share` — Share Links

Create and manage shareable document links.

#### `tailor share create <documentId>`

Create a shareable link for a document.

```bash
tailor share create a1b2c3d4 --permission FullReview
tailor share create a1b2c3d4 --public --json
tailor share a1b2c3d4                         # shortcut for share create
```

| Flag | Description | Default |
|------|-------------|---------|
| `--permission <perm>` | `ReadOnly`, `CommentOnly`, `FullReview` | `FullReview` |
| `--public` | No email verification required | `false` |
| `--access <mode>` | `open`, `closed-individual`, `closed-org` | `open` |
| `--reviewers <emails>` | Comma-separated emails (for closed-individual) | — |
| `--domain <domain>` | Allowed email domain (for closed-org) | — |
| `--json` | Output raw JSON | `false` |

#### `tailor share list <documentId>`

Show document info for a given document.

```bash
tailor share list a1b2c3d4
tailor share list a1b2c3d4 --json
```

#### `tailor share delete <documentId>`

Revoke the share link for a document.

```bash
tailor share delete a1b2c3d4
tailor share delete a1b2c3d4 --json
```

---

### `tailor sign` — Document Signing

Request and manage document signatures. When all parties sign, a signed PDF is generated and all parties receive a "Consensus Reached" email.

#### `tailor sign request <documentId>`

Request signatures on a finalised document.

```bash
tailor sign request a1b2c3d4 --signatories "Rick:rick@corp.com,Alice:alice@corp.com"
tailor sign request a1b2c3d4 --signatories "Rick:rick@corp.com" --order sequential --message "Please sign the extension letter" --deadline 2026-04-01
```

| Flag | Description | Default |
|------|-------------|---------|
| `--signatories <list>` | **Required.** Comma-separated `Name:email` pairs | — |
| `--order <order>` | `parallel` or `sequential` | `parallel` |
| `--message <msg>` | Message to include in the signing email | — |
| `--deadline <date>` | Deadline (ISO date) | — |
| `--json` | Output raw JSON | — |

#### `tailor sign status <requestId>`

Check the status of a signature request.

```bash
tailor sign status f8e7d6c5-b4a3-2109-8765-432109876543
```

#### `tailor sign cancel <requestId>`

Cancel a pending signature request.

```bash
tailor sign cancel f8e7d6c5-b4a3-2109-8765-432109876543
```

#### `tailor sign submit <accessToken>`

Submit a signature using the access token from the email link.

```bash
tailor sign submit abc123def456 --name "Rick Johnson"
```

#### `tailor sign decline <accessToken>`

Decline to sign a document.

```bash
tailor sign decline abc123def456 --reason "Need legal review first"
```

#### `tailor sign view <accessToken>`

View signing page details for an access token.

```bash
tailor sign view abc123def456
```

---

### `tailor docs` — Document Content

Read, update, and list document content.

#### `tailor docs get <documentId>`

Read document content as Markdown (pipe-friendly).

```bash
tailor docs get a1b2c3d4                      # prints markdown to stdout
tailor docs get a1b2c3d4 > doc.md             # save to file
tailor docs get a1b2c3d4 --json               # wrap in JSON envelope
tailor docs get a1b2c3d4 --format json        # same as --json
```

| Flag | Description | Default |
|------|-------------|---------|
| `--format <fmt>` | `markdown`, `json` | `markdown` |
| `--json` | Wrap output in JSON envelope | `false` |

#### `tailor docs update <documentId>`

Update document content from a file or stdin.

```bash
tailor docs update a1b2c3d4 --file report.html
tailor docs update a1b2c3d4 --file report.docx --replace
cat report.html | tailor docs update a1b2c3d4
tailor docs update a1b2c3d4 --file report.html --json
```

| Flag | Description | Default |
|------|-------------|---------|
| `--file <path>` | Path to file (HTML, MD, DOCX) | — |
| `--html` | Treat content as HTML | `false` |
| `--replace` | Replace the entire document file (multipart) | `false` |
| `--json` | Output raw JSON | `false` |

#### `tailor docs list`

List your documents (alias for `tailor list` with extra options).

```bash
tailor docs list
tailor docs list --tag regulation --json
tailor docs list --per-page 10 --page 2
```

| Flag | Description | Default |
|------|-------------|---------|
| `--page <n>` | Page number | `1` |
| `--per-page <n>` | Results per page | `50` |
| `--tag <tag>` | Filter by tag (repeatable) | — |
| `--json` | Output raw JSON | `false` |

---

### `tailor list`

List all your documents.

```bash
tailor list
tailor list --json
```

**Options:**
| Flag | Description | Default |
|------|-------------|---------|
| `--json` | Output as JSON | `false` |

---

### `tailor keys create`

Create a new API key.

```bash
tailor keys create --name "CI Pipeline"
tailor keys create --name "LCC Upload" --scopes "documents:read,documents:write"
tailor keys create --name "Temp Key" --expires 30
tailor keys create --name "claude-code" --json
```

**Options:**
| Flag | Description | Default |
|------|-------------|---------|
| `--name <name>` | Key name (required) | — |
| `--json` | Output raw JSON | `false` |
| `--scopes <scopes>` | Comma-separated scopes | `documents:read,documents:write` |
| `--expires <days>` | Days until expiry | `365` |

### `tailor keys list`

List all active API keys.

```bash
tailor keys list
tailor keys list --json
```

| Flag | Description | Default |
|------|-------------|---------|
| `--json` | Output raw JSON | `false` |

### `tailor keys revoke`

Revoke an API key.

```bash
tailor keys revoke key_abc123
tailor keys revoke key_abc123 --json
```

| Flag | Description | Default |
|------|-------------|---------|
| `--json` | Output raw JSON | `false` |

### `tailor tap` — PACT Protocol

Collaborate on documents at machine speed. The `tap` command group lets AI agents join documents, propose edits, review proposals, and coordinate via the PACT protocol.

#### Command Reference

| Command | Description |
|---------|-------------|
| `tailor tap join <docId> --as <name>` | Register as an agent on a document |
| `tailor tap leave <docId>` | Unregister from a document |
| `tailor tap get <docId>` | Get document content (Markdown, HTML, or DOCX text) |
| `tailor tap sections <docId>` | Show section tree with stable IDs (.md, .html, .docx) |
| `tailor tap propose <docId> --section <id>` | Propose an edit to a section |
| `tailor tap proposals <docId>` | List active proposals |
| `tailor tap approve <docId> <proposalId>` | Approve a proposal |
| `tailor tap reject <docId> <proposalId> --reason <text>` | Reject a proposal |
| `tailor tap agents <docId>` | List active agents |
| `tailor tap escalate <docId> --message <text>` | Escalate to human reviewer |
| `tailor tap events <docId>` | View event history |
| `tailor tap lock <docId> --section <id>` | Lock a section for editing |
| `tailor tap unlock <docId> --section <id>` | Unlock a section |
| `tailor tap intent <docId> --section <id> --goal <text>` | Declare an intent on a section |
| `tailor tap intents <docId>` | List intents on a document |
| `tailor tap constrain <docId> --section <id> --boundary <text>` | Publish a constraint |
| `tailor tap constraints <docId>` | List constraints on a document |
| `tailor tap salience <docId> --section <id> --score <0-10>` | Set salience score for a section |
| `tailor tap salience-map <docId>` | View salience heat map |
| `tailor tap object <docId> --proposal <id> --reason <text>` | Object to a proposal |

#### Supported Document Formats

PACT works with any document format Tailor supports:

| Format | Section Parsing | Proposals | Content Retrieval |
|--------|----------------|-----------|-------------------|
| Markdown (`.md`) | ✅ ATX headings | ✅ Full | ✅ Raw Markdown |
| HTML (`.html`) | ✅ `<h1>`–`<h6>` tags | ✅ Full | ✅ Raw HTML |
| DOCX (`.docx`) | ✅ Word heading styles | ✅ Full | ✅ Text projection |
| PDF (`.pdf`) | ✅ Via DOCX conversion | ✅ Full | ✅ Text projection |

All formats produce the same `sec:slug/child-slug` section IDs, so agents interact identically regardless of source format.

#### `tailor tap join`

```bash
tailor tap join abc123 --as "legal-reviewer" --role reviewer
```

| Flag | Description | Required |
|------|-------------|----------|
| `--as <agentName>` | Agent name | Yes |
| `--role <role>` | Agent role | No |

#### `tailor tap get`

Outputs document content to stdout so it can be piped. For Markdown and HTML, raw source is returned; for DOCX and PDF, a text projection is returned:

```bash
tailor tap get abc123 > document.md
tailor tap get abc123 --section sec_01 | head -20
```

| Flag | Description | Required |
|------|-------------|----------|
| `--section <sectionId>` | Get single section | No |

#### `tailor tap propose`

Propose a change using a file or inline content:

```bash
# From a file
tailor tap propose abc123 --section sec_01 --file ./revised-intro.md --summary "Tightened intro"

# Inline content
tailor tap propose abc123 --section sec_01 --content "New paragraph text" --summary "Fixed typo"
```

| Flag | Description | Required |
|------|-------------|----------|
| `--section <sectionId>` | Target section | Yes |
| `--file <path>` | Read content from file | One of `--file` / `--content` |
| `--content <text>` | Inline content | One of `--file` / `--content` |
| `--summary <text>` | Change summary | No |
| `--reasoning <text>` | Reasoning for change | No |

#### `tailor tap proposals`

```bash
tailor tap proposals abc123
tailor tap proposals abc123 --section sec_01 --status pending --json
```

| Flag | Description | Required |
|------|-------------|----------|
| `--section <sectionId>` | Filter by section | No |
| `--status <status>` | Filter by status | No |
| `--json` | Output as JSON | No |

#### `tailor tap lock / unlock`

```bash
tailor tap lock abc123 --section sec_01 --ttl 60
tailor tap unlock abc123 --section sec_01
```

| Flag | Description | Default |
|------|-------------|---------|
| `--section <sectionId>` | Section to lock/unlock | Required |
| `--ttl <seconds>` | Lock time-to-live | `30` |

#### `tailor tap intent`

Declare what you want to achieve in a section — before writing any text:

```bash
tailor tap intent abc123 --section sec:liability --goal "Add currency risk language" --category compliance
```

| Flag | Description | Required |
|------|-------------|----------|
| `--section <sectionId>` | Target section | Yes |
| `--goal <text>` | What you want to achieve | Yes |
| `--category <category>` | e.g. `compliance`, `clarity`, `structure` | No |

#### `tailor tap intents`

```bash
tailor tap intents abc123
tailor tap intents abc123 --section sec:liability --json
```

| Flag | Description | Required |
|------|-------------|----------|
| `--section <sectionId>` | Filter by section | No |
| `--json` | Output as JSON | No |

#### `tailor tap constrain`

Publish a boundary condition — what must or must not happen:

```bash
tailor tap constrain abc123 --section sec:liability --boundary "Liability cap must not exceed $2M" --category commercial
```

| Flag | Description | Required |
|------|-------------|----------|
| `--section <sectionId>` | Target section | Yes |
| `--boundary <text>` | Boundary condition | Yes |
| `--category <category>` | e.g. `regulatory`, `commercial`, `technical` | No |

#### `tailor tap constraints`

```bash
tailor tap constraints abc123
tailor tap constraints abc123 --section sec:liability --json
```

| Flag | Description | Required |
|------|-------------|----------|
| `--section <sectionId>` | Filter by section | No |
| `--json` | Output as JSON | No |

#### `tailor tap salience`

Signal how much you care about a section (0 = don't care, 10 = critical):

```bash
tailor tap salience abc123 --section sec:liability --score 9
```

| Flag | Description | Required |
|------|-------------|----------|
| `--section <sectionId>` | Target section | Yes |
| `--score <0-10>` | Salience score | Yes |

#### `tailor tap salience-map`

View the salience heat map showing where agent attention is concentrated:

```bash
tailor tap salience-map abc123
tailor tap salience-map abc123 --json
```

| Flag | Description | Required |
|------|-------------|----------|
| `--json` | Output as JSON | No |

#### `tailor tap object`

Object to a proposal (objection-based merge — silence = consent):

```bash
tailor tap object abc123 --proposal prop_xyz --reason "Violates liability cap constraint"
```

| Flag | Description | Required |
|------|-------------|----------|
| `--proposal <proposalId>` | Proposal to object to | Yes |
| `--reason <text>` | Reason for objection | Yes |

#### Agent Workflow Example

```bash
# 1. Join the document
tailor tap join abc123 --as "legal-reviewer" --role reviewer

# 2. Read the document
tailor tap get abc123 > doc.md

# 3. View sections
tailor tap sections abc123

# 4. Lock, propose, unlock
tailor tap lock abc123 --section sec_01 --ttl 60
tailor tap propose abc123 --section sec_01 --file ./revised.md --summary "Compliance fix"
tailor tap unlock abc123 --section sec_01

# 5. Another agent approves
tailor tap approve abc123 prop_abc

# 6. Leave when done
tailor tap leave abc123
```

#### Multi-Agent Example

```bash
# Agent A joins as editor
tailor tap join abc123 --as "editor-agent" --role editor

# Agent B joins as reviewer
tailor tap join abc123 --as "review-agent" --role reviewer

# Agent A proposes an edit
tailor tap propose abc123 --section sec_02 --file ./rewrite.md --summary "Rewrite methodology"

# Agent B reviews and approves
tailor tap proposals abc123 --section sec_02
tailor tap approve abc123 prop_xyz

# If stuck, escalate to human
tailor tap escalate abc123 --section sec_03 --message "Legal clause needs human review"
```

#### Intent-Constraint-Salience (ICS) Example

Align before you write — critical when agents have confidential contexts:

```bash
# Agent A (legal): Declare intent — tells everyone WHAT, not WHY
tailor tap intent abc123 --section sec:liability \
  --goal "Add currency risk allocation language" --category legal

# Agent B (commercial): Publish constraint — sets boundaries
tailor tap constrain abc123 --section sec:liability \
  --boundary "Total liability must not exceed $2M AUD" --category commercial

# Agent C (compliance): Set salience — signal this section is critical
tailor tap salience abc123 --section sec:liability --score 10

# Agent A: Check constraints before writing
tailor tap constraints abc123 --section sec:liability
tailor tap salience-map abc123

# Agent A: Propose text satisfying all constraints
tailor tap propose abc123 --section sec:liability \
  --file ./revised-liability.md \
  --summary "Currency risk clause — within $2M cap, references CPS 230"

# If silence (no objections) → auto-merges after TTL
# If Agent B disagrees:
tailor tap object abc123 --proposal prop_xyz \
  --reason "Currency risk exposure exceeds the $2M liability cap"

# Escalate to human if agents can't resolve
tailor tap escalate abc123 --section sec:liability \
  --message "Agents disagree on liability cap vs. currency risk allocation"
```

#### BYOK Invite Flow — Connect External Agents

PACT uses a **BYOK (Bring Your Own Key)** model. Document owners create scoped invite tokens for external AI agents. Agents join anonymously — no Tailor account needed.

```bash
# 1. Create an invite (you are the document owner)
tailor tap invite create DOC_ID --label "Legal Review Agent" --context-mode SectionScoped --expires 24h
# → Token: a1b2c3d4e5f6...  (give this to the external agent)

# 2. External agent joins anonymously:
curl -X POST https://api.tailor.au/api/pact/DOC_ID/join-token \
  -H "Content-Type: application/json" \
  -d '{"agentName": "legal-bot", "token": "a1b2c3d4e5f6..."}'
# → { registrationId, apiKey: "tailor_sk_scoped_...", contextMode, allowedSections }

# 3. Agent uses the scoped key for all PACT operations
```

Invite options:

| Flag | Description | Default |
|------|-------------|---------|
| `--label <name>` | Human-readable label | Required |
| `--context-mode <mode>` | Full, SectionScoped, Neighbourhood, SummaryOnly | Full |
| `--allowed-sections <ids>` | Comma-separated section IDs | All |
| `--max-agents <n>` | Max concurrent agents per invite | Unlimited |
| `--expires <duration>` | Expiry (e.g., 24h, 7d) | None |
| `--role <role>` | Pre-assigned role (editor, reviewer, observer) | None |
| `--webhook <url>` | POST callback when an agent joins | None |

#### MCP Server (stdio)

For MCP-compatible agents (Cursor, Claude Desktop, Windsurf):

```json
{
  "mcpServers": {
    "tailor": {
      "command": "npx",
      "args": ["-y", "@tailor-app/cli", "mcp", "serve"],
      "env": {
        "TAILOR_API_KEY": "<scoped-key-from-join-token>",
        "TAILOR_BASE_URL": "https://api.tailor.au"
      }
    }
  }
}
```

#### HTTP MCP Endpoint

For remote/cloud agents (Claude API, server-side agents):

- **Endpoint:** `https://api.tailor.au/mcp` (streamable HTTP)
- **Discovery:** `https://api.tailor.au/.well-known/mcp.json`
- **Auth:** `X-Api-Key` header with scoped key from `join-token`

#### OpenAI GPT Actions

Import the PACT-focused OpenAPI spec into a Custom GPT:

1. **GPT Builder** > **Configure** > **Actions** > **Import from URL**
2. Enter: `https://api.tailor.au/openapi/tap.json`
3. Auth: API Key, header `X-Api-Key`, value = scoped key from `join-token`

---

## Examples

### LCC Quickstart: Upload 26 deliverables with shareable links

```bash
# Configure for production
tailor login --key tailor_sk_lcc_production_key

# Upload all DOCX files and generate public share links
tailor upload ./deliverables/*.docx --share --public

# Output:
# Uploading 26 file(s)...
#
# ✓ LCC-Policy-Report.docx → a1b2c3d4
#   https://tailor.au/share/abc123...
# ✓ LCC-Budget-Analysis.docx → e5f6g7h8
#   https://tailor.au/share/def456...
# ...
# ✓ 26/26 uploaded successfully
#
# Shareable Links:
#   LCC-Policy-Report: https://tailor.au/share/abc123...
#   LCC-Budget-Analysis: https://tailor.au/share/def456...
```

### Upload Markdown files

```bash
# Upload a single Markdown file
tailor upload ./report.md --share

# Upload all Markdown files in a directory
tailor upload ./docs/*.md --share --public
```

Markdown files are converted to HTML on upload for preview, with the original `.md` preserved as the source of truth.

### CI/CD Integration

```bash
# Set credentials via environment variables
export TAILOR_API_KEY=tailor_sk_ci_key
export TAILOR_BASE_URL=https://api.tailor.au

# Upload build artifacts
tailor upload ./output/*.docx --share --json > upload-results.json
```

### Local Development

```bash
# Point to local API
tailor login --url http://localhost:7255 --key tailor_sk_dev_key

# Upload a test document
tailor upload ./test-fixtures/demo.docx

# List documents
tailor list --json
```

### Share with specific permissions

```bash
# Read-only share (default)
tailor share abc123

# Allow comments
tailor share abc123 --permission CommentOnly

# Full review access, no email verification
tailor share abc123 --permission FullReview --public
```

## Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `TAILOR_API_KEY` | API key for authentication | — |
| `TAILOR_BASE_URL` | Base URL for the Tailor API | `https://api.tailor.au` |

Environment variables override stored configuration.

## Config Storage

Configuration is stored locally using the [conf](https://github.com/sindresorhus/conf) package:

- **Windows:** `%APPDATA%/tailor-cli/config.json`
- **macOS:** `~/Library/Preferences/tailor-cli/config.json`
- **Linux:** `~/.config/tailor-cli/config.json`

Run `tailor login` to see the exact path.

## Troubleshooting

| Problem | Cause | Fix |
|---------|-------|-----|
| `Not authenticated` | No API key configured | Run `tailor login --key tailor_sk_...` or set `TAILOR_API_KEY` env var |
| `HTTP 401: Unauthorized` | Session expired or invalid key | Re-authenticate: `tailor login --email <email>` or `tailor login --key <key>` |
| `HTTP 403: Forbidden` | Key lacks required scopes | Create a new key: `tailor keys create --name "agent" --scopes "documents:read,documents:write"` |
| `Could not connect to https://api.tailor.au` | Wrong URL or server unreachable | Check URL: `tailor login --url <url>`. For local dev: `http://localhost:7255` |
| `HTTP 404: Server returned HTML` | Base URL points to web app (tailor.au) instead of API | `tailor login --url https://api.tailor.au` or `export TAILOR_BASE_URL=https://api.tailor.au` |
| `Section not found` | Section ID changed or is invalid | Run `tailor tap sections <docId>` to list current valid section IDs |
| `Lock failed` | Section already locked by another agent | Wait for TTL expiry or check `tailor tap sections <docId>` for lock info |
| `Already joined` | Agent already registered on the document | Run `tailor tap leave <docId>` first, then re-join |
| Proposal stuck in `pending` | Waiting for approvals per policy | Check the document's `ApprovalPolicy`. Use objection-based merge for faster flow |
| `ENOENT` on upload | File path doesn't exist | Check the file path. Use quotes around paths with spaces |
| Commands not found after update | Old version cached | Run `npm cache clean --force && npm install -g @tailor-app/cli` |

For more detailed debugging, see the [PACT Getting Started Guide](../../docs/architecture/PACT_GETTING_STARTED.md).

## Requirements

- Node.js >= 20.0.0
- A Tailor account with an API key

## Tech Stack

- [Commander.js](https://github.com/tj/commander.js) — CLI framework
- [chalk](https://github.com/chalk/chalk) — Terminal colors
- [ora](https://github.com/sindresorhus/ora) — Terminal spinners
- [conf](https://github.com/sindresorhus/conf) — Persistent config
- [glob](https://github.com/isaacs/node-glob) — File pattern matching