Writer CLI Spec
Summary
Add a standalone-first, markdown-only writer CLI focused on spec authoring and validation.
The CLI should be useful even when the desktop app is not running. It should help users create structured spec documents, inspect markdown workspaces, edit frontmatter safely, and validate spec quality from the terminal and CI.
This feature is intentionally scoped so a single agent can implement it end to end in one pass. That means the initial version must stay narrow, avoid platform-specific installer work, and avoid larger programmatic surfaces such as HTTP APIs, MCP servers, TUIs, or JSON output.
Background
Writer is currently a Tauri desktop application with a React frontend and a Rust backend. The Rust side already has useful primitives for:
- reading and writing files
- indexing markdown workspaces
- fuzzy searching markdown paths
- tracking workspace state
Those primitives currently sit behind Tauri commands, but they are close to being reusable from a CLI.
Because the underlying source of truth is plain markdown, a CLI does not add much value if it only mirrors generic shell operations. The CLI becomes worth building when it adds Writer-specific behavior:
- opinionated spec templates
- frontmatter-aware edits
- validation of required sections and metadata
- consistent markdown scaffolding
- workspace inspection tailored to markdown specs
Product Thesis
The CLI should be a fast, markdown-native companion to the desktop app, not a replacement for every shell tool and not an app-wide remote control surface.
The first version should optimize for three jobs:
- Create a new spec quickly and consistently.
- Validate a spec or spec workspace in CI and pre-commit workflows.
- Inspect markdown content and frontmatter without opening the GUI.
Goals
- Build a headless CLI that works without a running GUI.
- Keep all inputs and outputs markdown, YAML, or plain text.
- Add Writer-specific value through templates and validation.
- Reuse as much existing Rust logic as possible.
- Keep the implementation small enough for one agent to complete end to end.
- Keep the CLI script-friendly through stable stdout, stderr, and exit codes.
Non-Goals
- No JSON output.
- No local HTTP API.
- No MCP server.
- No interactive TUI.
- No plugin, theme, sync, or developer-tool command surface.
- No bundling the CLI into the desktop installer in v1.
- No requirement to control or focus a running Writer instance in v1.
- No full parity with the desktop command palette.
- No non-markdown export formats such as PDF in v1.
Single-Agent Scope Boundary
This spec must remain implementable by one agent in a single end-to-end pass. To keep the scope realistic, v1 must follow these rules:
- The CLI lives inside the existing Rust package at
apps/desktop/src-tauri/. - The implementation may add shared Rust modules, but it should not begin with a full multi-crate refactor.
- The CLI should operate directly on the filesystem and markdown files.
- GUI integration is explicitly deferred unless it falls out naturally from existing code with almost no extra complexity.
- Output should stay plain text only.
- Validation should be deterministic and strict enough for CI.
- The command surface should stay under ten top-level commands in v1.
If a design choice increases scope materially, prefer the smaller alternative.
Product Decisions
Markdown-only output
The CLI will not support JSON. Output must be designed for humans first, while still being grep-friendly and stable enough for shell scripts.
Standalone-first
Every v1 command should work without the desktop app running. The CLI should read and write markdown files directly.
Spec-aware, not shell-clone
The CLI should avoid duplicating broad filesystem tooling unless the behavior is meaningfully Writer-specific.
Narrow v1 surface
The first release should focus on:
- markdown workspace inspection
- frontmatter edits
- spec creation
- spec validation
Primary User Workflows
Create a new spec
Examples:
writer spec create "Login flow" --template feature
writer spec create "Quarterly planning" --template prd --dir specs/product
writer spec create "Use local caching for search index" --template decision
Expected result:
- a slugged
.mdfile is created - frontmatter is prefilled
- required sections are scaffolded
- the file is immediately ready to edit in any editor
Validate a single spec
Examples:
writer spec validate specs/login-flow.md
writer spec validate docs/decision-cache-index.md
Expected result:
- validation diagnostics print to stderr or stdout in stable text format
- exit code is
0for valid input,1for validation failures
Validate a workspace in CI
Examples:
writer spec validate specs
writer spec validate .
Expected result:
- all markdown specs under the target path are checked
- each issue is reported on its own line
- CI can fail the build on invalid specs
Inspect and edit frontmatter
Examples:
writer frontmatter show specs/login-flow.md
writer frontmatter get specs/login-flow.md status
writer frontmatter set specs/login-flow.md status review
Expected result:
- frontmatter can be printed, queried, and updated without opening the GUI
- updates preserve markdown body content
- rewriting the YAML block into a canonical format is acceptable in v1
Search a spec workspace
Examples:
writer list specs
writer search login
writer read specs/login-flow.md
Expected result:
- users can inspect a spec workspace from the shell quickly
- filename search is fast and limited to markdown files
V1 Command Surface
Top-level commands
| Command | Purpose | Notes |
|---|---|---|
writer list [path] |
List markdown files under a directory | recursive |
writer read <file> |
Print file contents to stdout | raw markdown |
writer search <query> |
Fuzzy-search markdown filenames | workspace-aware |
writer frontmatter show <file> |
Print full frontmatter block | YAML only |
writer frontmatter get <file> <key> |
Print one frontmatter value | scalar or YAML fragment |
writer frontmatter set <file> <key> <value> |
Update one frontmatter field | rewrites YAML canonically |
writer spec create <title> |
Create a new spec from a template | Writer-specific |
writer spec validate [path] |
Validate one file or a whole directory | CI-friendly |
writer completion <shell> |
Generate shell completions | bash, zsh, fish |
Deferred commands
The following should not be part of the first implementation, but may be revisited later:
writer openwriter grepwriter outlinewriter spec formatwriter spec bundlewriter spec links
These are useful, but not necessary to make the first release valuable.
CLI UX Rules
Global behavior
- Use standard subcommand and flag syntax via
clap. - Print human-readable errors to stderr.
- Print command results to stdout.
- Avoid decorative formatting that makes shell usage awkward.
- Keep one result per line where possible.
- Paths should print relative to the resolved workspace root when practical, otherwise relative to the invocation directory.
Supported global flags
These flags are recommended for v1:
--workspace <path>: resolve relative operations against a specific workspace root--quiet: suppress non-essential success messages--no-color: disable colored diagnostics
V1 does not need --json, --stdin, or interactive prompts.
Exit codes
0: success1: validation failed2: invalid CLI usage3: runtime or IO failure
Command Details
writer list [path]
Purpose:
- list markdown files recursively beneath the target directory
Behavior:
- default path is current directory or
--workspaceif provided - include
.mdand.markdown - ignore hidden directories and files via the same traversal rules as workspace indexing
- return one relative path per line
Examples:
writer list
writer list specs
writer --workspace ~/work/project list docs
writer read <file>
Purpose:
- print raw file contents to stdout exactly as stored on disk
Behavior:
- preserve frontmatter and body
- fail if the path does not exist or is not a markdown file
writer search <query>
Purpose:
- fuzzy-search markdown files by relative path
Behavior:
- search within the resolved workspace root or current directory
- reuse the existing nucleo-based filename search where possible
- print one relative path per line in descending score order
- default result limit should be 20
- support
--limit <n>
writer frontmatter show <file>
Purpose:
- print the file's frontmatter block without the surrounding markdown body
Behavior:
- if no frontmatter exists, print nothing and exit
0 - print YAML content without surrounding
---delimiters to keep piping simple
writer frontmatter get <file> <key>
Purpose:
- print one frontmatter field value
Behavior:
- if the key is missing, exit
3with a clear error - scalar values print on one line
- array or map values print as YAML fragments
writer frontmatter set <file> <key> <value>
Purpose:
- set or create a frontmatter field
Behavior:
- if the file has no frontmatter, create one
- rewrite the YAML block into a canonical format
- preserve the markdown body unchanged
- support repeated
--tag <value>style handling later, but v1 only needs single key/value updates
Example:
writer frontmatter set specs/login-flow.md status review
writer spec create <title>
Purpose:
- create a new markdown spec using a built-in template
Required flags:
--template <feature|prd|decision>
Optional flags:
--dir <path>: destination directory, defaultspecs--status <value>: defaultdraft--owner <value>: may be repeated later, but v1 only requires single owner support--tag <value>: optional tag, may be repeated later if easy--force: overwrite if the destination file already exists
Behavior:
- generate a slug from the title
- write
<slug>.mdinto the destination directory - write frontmatter and template sections
- fail if the file exists unless
--forceis provided - print the created path on success
writer spec validate [path]
Purpose:
- validate a single file or every markdown file under a directory
Behavior:
- default path is current directory or
--workspaceif provided - if the target is a file, validate only that file
- if the target is a directory, validate all markdown files recursively beneath it
- print one diagnostic per line
- return
1when any validation issue is found - return
0when all checked files are valid
Example diagnostic format:
specs/login-flow.md: missing required section "Acceptance Criteria"
specs/login-flow.md: invalid status "inprogress" (expected draft|review|approved|done)
specs/login-flow.md: broken link ./auth-overview.md
writer completion <shell>
Purpose:
- generate shell completions using
clap_complete
Supported shells:
bashzshfish
Spec Template Model
The CLI only becomes meaningfully different from generic shell tooling if Writer defines a concrete spec shape. V1 should ship with three templates.
Common frontmatter
Required fields:
title: non-empty stringtype: one offeature,prd,decisionstatus: one ofdraft,review,approved,done
Optional fields:
owners: YAML list of stringstags: YAML list of strings
Canonical field order when creating or rewriting frontmatter:
titletypestatusownerstags
Common section rules
All spec templates should use H2 sections for major headings.
V1 validation should require exact H2 names for required sections. That constraint is intentionally strict to keep the first implementation deterministic.
Common required sections:
SummaryGoalsNon-Goals
Feature template
Required sections:
SummaryGoalsNon-GoalsRequirementsAcceptance Criteria
Template body:
---
title: Login flow
type: feature
status: draft
owners:
- product
tags:
- auth
---
## Summary
Describe the feature in 2-4 sentences.
## Goals
- Goal 1
- Goal 2
## Non-Goals
- Non-goal 1
## Requirements
- Requirement 1
- Requirement 2
## Acceptance Criteria
- [ ] Criterion 1
- [ ] Criterion 2
PRD template
Required sections:
SummaryGoalsNon-GoalsUser ProblemsRequirementsAcceptance Criteria
Template body:
---
title: Quarterly planning workspace
type: prd
status: draft
owners:
- product
tags:
- planning
---
## Summary
Describe the product decision or initiative.
## Goals
- Goal 1
Lines 1–500 of 804. Load more
# Writer CLI Spec ## Summary Add a standalone-first, markdown-only `writer` CLI focused on spec authoring and validation. The CLI should be useful even when the desktop app is not running. It should help users create structured spec documents, inspect markdown workspaces, edit frontmatter safely, and validate spec quality from the terminal and CI. This feature is intentionally scoped so a single agent can implement it end to end in one pass. That means the initial version must stay narrow, avoid platform-specific installer work, and avoid larger programmatic surfaces such as HTTP APIs, MCP servers, TUIs, or JSON output. ## Background Writer is currently a Tauri desktop application with a React frontend and a Rust backend. The Rust side already has useful primitives for: - reading and writing files - indexing markdown workspaces - fuzzy searching markdown paths - tracking workspace state Those primitives currently sit behind Tauri commands, but they are close to being reusable from a CLI. Because the underlying source of truth is plain markdown, a CLI does not add much value if it only mirrors generic shell operations. The CLI becomes worth building when it adds Writer-specific behavior: - opinionated spec templates - frontmatter-aware edits - validation of required sections and metadata - consistent markdown scaffolding - workspace inspection tailored to markdown specs ## Product Thesis The CLI should be a fast, markdown-native companion to the desktop app, not a replacement for every shell tool and not an app-wide remote control surface. The first version should optimize for three jobs: 1. Create a new spec quickly and consistently. 2. Validate a spec or spec workspace in CI and pre-commit workflows. 3. Inspect markdown content and frontmatter without opening the GUI. ## Goals - Build a headless CLI that works without a running GUI. - Keep all inputs and outputs markdown, YAML, or plain text. - Add Writer-specific value through templates and validation. - Reuse as much existing Rust logic as possible. - Keep the implementation small enough for one agent to complete end to end. - Keep the CLI script-friendly through stable stdout, stderr, and exit codes. ## Non-Goals - No JSON output. - No local HTTP API. - No MCP server. - No interactive TUI. - No plugin, theme, sync, or developer-tool command surface. - No bundling the CLI into the desktop installer in v1. - No requirement to control or focus a running Writer instance in v1. - No full parity with the desktop command palette. - No non-markdown export formats such as PDF in v1. ## Single-Agent Scope Boundary This spec must remain implementable by one agent in a single end-to-end pass. To keep the scope realistic, v1 must follow these rules: - The CLI lives inside the existing Rust package at `apps/desktop/src-tauri/`. - The implementation may add shared Rust modules, but it should not begin with a full multi-crate refactor. - The CLI should operate directly on the filesystem and markdown files. - GUI integration is explicitly deferred unless it falls out naturally from existing code with almost no extra complexity. - Output should stay plain text only. - Validation should be deterministic and strict enough for CI. - The command surface should stay under ten top-level commands in v1. If a design choice increases scope materially, prefer the smaller alternative. ## Product Decisions ### Markdown-only output The CLI will not support JSON. Output must be designed for humans first, while still being grep-friendly and stable enough for shell scripts. ### Standalone-first Every v1 command should work without the desktop app running. The CLI should read and write markdown files directly. ### Spec-aware, not shell-clone The CLI should avoid duplicating broad filesystem tooling unless the behavior is meaningfully Writer-specific. ### Narrow v1 surface The first release should focus on: - markdown workspace inspection - frontmatter edits - spec creation - spec validation ## Primary User Workflows ### Create a new spec Examples: ```bash writer spec create "Login flow" --template feature writer spec create "Quarterly planning" --template prd --dir specs/product writer spec create "Use local caching for search index" --template decision ``` Expected result: - a slugged `.md` file is created - frontmatter is prefilled - required sections are scaffolded - the file is immediately ready to edit in any editor ### Validate a single spec Examples: ```bash writer spec validate specs/login-flow.md writer spec validate docs/decision-cache-index.md ``` Expected result: - validation diagnostics print to stderr or stdout in stable text format - exit code is `0` for valid input, `1` for validation failures ### Validate a workspace in CI Examples: ```bash writer spec validate specs writer spec validate . ``` Expected result: - all markdown specs under the target path are checked - each issue is reported on its own line - CI can fail the build on invalid specs ### Inspect and edit frontmatter Examples: ```bash writer frontmatter show specs/login-flow.md writer frontmatter get specs/login-flow.md status writer frontmatter set specs/login-flow.md status review ``` Expected result: - frontmatter can be printed, queried, and updated without opening the GUI - updates preserve markdown body content - rewriting the YAML block into a canonical format is acceptable in v1 ### Search a spec workspace Examples: ```bash writer list specs writer search login writer read specs/login-flow.md ``` Expected result: - users can inspect a spec workspace from the shell quickly - filename search is fast and limited to markdown files ## V1 Command Surface ### Top-level commands | Command | Purpose | Notes | | --------------------------------------------- | -------------------------------------- | ------------------------- | | `writer list [path]` | List markdown files under a directory | recursive | | `writer read <file>` | Print file contents to stdout | raw markdown | | `writer search <query>` | Fuzzy-search markdown filenames | workspace-aware | | `writer frontmatter show <file>` | Print full frontmatter block | YAML only | | `writer frontmatter get <file> <key>` | Print one frontmatter value | scalar or YAML fragment | | `writer frontmatter set <file> <key> <value>` | Update one frontmatter field | rewrites YAML canonically | | `writer spec create <title>` | Create a new spec from a template | Writer-specific | | `writer spec validate [path]` | Validate one file or a whole directory | CI-friendly | | `writer completion <shell>` | Generate shell completions | bash, zsh, fish | ### Deferred commands The following should not be part of the first implementation, but may be revisited later: - `writer open` - `writer grep` - `writer outline` - `writer spec format` - `writer spec bundle` - `writer spec links` These are useful, but not necessary to make the first release valuable. ## CLI UX Rules ### Global behavior - Use standard subcommand and flag syntax via `clap`. - Print human-readable errors to stderr. - Print command results to stdout. - Avoid decorative formatting that makes shell usage awkward. - Keep one result per line where possible. - Paths should print relative to the resolved workspace root when practical, otherwise relative to the invocation directory. ### Supported global flags These flags are recommended for v1: - `--workspace <path>`: resolve relative operations against a specific workspace root - `--quiet`: suppress non-essential success messages - `--no-color`: disable colored diagnostics V1 does not need `--json`, `--stdin`, or interactive prompts. ### Exit codes - `0`: success - `1`: validation failed - `2`: invalid CLI usage - `3`: runtime or IO failure ## Command Details ### `writer list [path]` Purpose: - list markdown files recursively beneath the target directory Behavior: - default path is current directory or `--workspace` if provided - include `.md` and `.markdown` - ignore hidden directories and files via the same traversal rules as workspace indexing - return one relative path per line Examples: ```bash writer list writer list specs writer --workspace ~/work/project list docs ``` ### `writer read <file>` Purpose: - print raw file contents to stdout exactly as stored on disk Behavior: - preserve frontmatter and body - fail if the path does not exist or is not a markdown file ### `writer search <query>` Purpose: - fuzzy-search markdown files by relative path Behavior: - search within the resolved workspace root or current directory - reuse the existing nucleo-based filename search where possible - print one relative path per line in descending score order - default result limit should be 20 - support `--limit <n>` ### `writer frontmatter show <file>` Purpose: - print the file's frontmatter block without the surrounding markdown body Behavior: - if no frontmatter exists, print nothing and exit `0` - print YAML content without surrounding `---` delimiters to keep piping simple ### `writer frontmatter get <file> <key>` Purpose: - print one frontmatter field value Behavior: - if the key is missing, exit `3` with a clear error - scalar values print on one line - array or map values print as YAML fragments ### `writer frontmatter set <file> <key> <value>` Purpose: - set or create a frontmatter field Behavior: - if the file has no frontmatter, create one - rewrite the YAML block into a canonical format - preserve the markdown body unchanged - support repeated `--tag <value>` style handling later, but v1 only needs single key/value updates Example: ```bash writer frontmatter set specs/login-flow.md status review ``` ### `writer spec create <title>` Purpose: - create a new markdown spec using a built-in template Required flags: - `--template <feature|prd|decision>` Optional flags: - `--dir <path>`: destination directory, default `specs` - `--status <value>`: default `draft` - `--owner <value>`: may be repeated later, but v1 only requires single owner support - `--tag <value>`: optional tag, may be repeated later if easy - `--force`: overwrite if the destination file already exists Behavior: - generate a slug from the title - write `<slug>.md` into the destination directory - write frontmatter and template sections - fail if the file exists unless `--force` is provided - print the created path on success ### `writer spec validate [path]` Purpose: - validate a single file or every markdown file under a directory Behavior: - default path is current directory or `--workspace` if provided - if the target is a file, validate only that file - if the target is a directory, validate all markdown files recursively beneath it - print one diagnostic per line - return `1` when any validation issue is found - return `0` when all checked files are valid Example diagnostic format: ```text specs/login-flow.md: missing required section "Acceptance Criteria" specs/login-flow.md: invalid status "inprogress" (expected draft|review|approved|done) specs/login-flow.md: broken link ./auth-overview.md ``` ### `writer completion <shell>` Purpose: - generate shell completions using `clap_complete ` Supported shells: - `bash` - `zsh` - `fish` ## Spec Template Model The CLI only becomes meaningfully different from generic shell tooling if Writer defines a concrete spec shape. V1 should ship with three templates. ### Common frontmatter Required fields: - `title`: non-empty string - `type`: one of `feature`, `prd`, `decision` - `status`: one of `draft`, `review`, `approved`, `done` Optional fields: - `owners`: YAML list of strings - `tags`: YAML list of strings Canonical field order when creating or rewriting frontmatter: 1. `title` 2. `type` 3. `status` 4. `owners` 5. `tags` ### Common section rules All spec templates should use H2 sections for major headings. V1 validation should require exact H2 names for required sections. That constraint is intentionally strict to keep the first implementation deterministic. Common required sections: - `Summary` - `Goals` - `Non-Goals` ### Feature template Required sections: - `Summary` - `Goals` - `Non-Goals` - `Requirements` - `Acceptance Criteria` Template body: ```md --- title: Login flow type: feature status: draft owners: - product tags: - auth --- ## Summary Describe the feature in 2-4 sentences. ## Goals - Goal 1 - Goal 2 ## Non-Goals - Non-goal 1 ## Requirements - Requirement 1 - Requirement 2 ## Acceptance Criteria - [ ] Criterion 1 - [ ] Criterion 2 ``` ### PRD template Required sections: - `Summary` - `Goals` - `Non-Goals` - `User Problems` - `Requirements` - `Acceptance Criteria` Template body: ```md --- title: Quarterly planning workspace type: prd status: draft owners: - product tags: - planning --- ## Summary Describe the product decision or initiative. ## Goals - Goal 1