dacli - Docs-As-Code CLI Specification

Overview

dacli (Docs-As-Code CLI) enables the use of all documentation tools via the command line. This is especially useful for LLMs that don’t have MCP support but can use Bash/Shell tools.

Global Options

dacli [OPTIONS] <COMMAND> [ARGS]

Options:
  --docs-root PATH    Documentation root directory (default: $PROJECT_PATH or cwd)
  --format FORMAT     Output format: text (default), json, yaml
  --pretty            Formatted output for humans
  --verbose, -v       Show warning messages (default: only errors shown)
  --no-gitignore      Include files that would normally be excluded by .gitignore patterns
  --include-hidden    Include files in hidden directories (starting with '.')
  --version           Show version
  --help              Show help

Help System

Command Groups

Commands are organized into story-based groups in the help output:

Group Commands

Discover

structure, metadata - Understand your documentation

Find

search, sections-at-level - Locate specific content

Read

section, elements - Access content details

Validate

validate - Check documentation quality

Edit

update, insert - Modify content

Typo Correction

If you mistype a command, dacli suggests the correct command:

$ dacli serch "API"
Error: No such command 'serch'.

Did you mean: search

Usage Examples

The main help (dacli --help) and each command help show usage examples:

$ dacli --help
...
Examples:
  dacli --format json structure          # Get document structure as JSON
  dacli search "authentication"          # Find sections about authentication
  dacli section api.endpoints            # Read a specific section

Command Aliases

For faster typing, all commands have short aliases:

Alias Command Description

str

structure

Show document structure

meta

metadata

Show project/section metadata

s

search

Search documentation

lv

sections-at-level

Sections at level N

sec

section

Read section content

el

elements

Get code/tables/images

val

validate

Validate structure

Example:

# These are equivalent:
dacli --format json search "API"
dacli --format json s "API"

Navigation Commands

structure

Shows the hierarchical document structure.

dacli structure [--max-depth N]

Example:

$ dacli structure --max-depth 1
{
  "sections": [
    {"path": "introduction", "title": "Introduction", "level": 1},
    {"path": "architecture", "title": "Architecture", "level": 1}
  ],
  "total_sections": 15
}

section

Reads the content of a section.

dacli section <PATH>

Path Format:

Section paths use hybrid notation: - Colon (:) separates document from section: doc:section - Dot (.) separates nested sections: doc:section.subsection

Examples:

# Read a chapter (Level 1)
$ dacli section my-doc:introduction
{
  "path": "my-doc:introduction",
  "title": "Introduction",
  "content": "== Introduction\n\nThis section...",
  "format": "asciidoc"
}

# Read a nested section (Level 2)
$ dacli section my-doc:introduction.goals
{
  "path": "my-doc:introduction.goals",
  "title": "Goals",
  "content": "=== Goals\n\nThese are...",
  "format": "asciidoc"
}

# Error: Path not found with helpful hint (Issue #198)
$ dacli section my-doc:intro:goals
{
  "error": {
    "code": "PATH_NOT_FOUND",
    "message": "Section 'my-doc:intro:goals' not found",
    "details": {
      "requested_path": "my-doc:intro:goals",
      "corrected_path": "my-doc:intro.goals",
      "hint": "Use colon (:) only once to separate document from section. Use dots (.) for nested sections. Example: my-doc:intro.goals",
      "suggestions": ["my-doc:introduction", "my-doc:introduction.goals"]
    }
  }
}

sections-at-level

Shows all sections at a specific level.

dacli sections-at-level <LEVEL>

Arguments:

  • LEVEL: Non-negative integer (0, 1, 2, …​) representing the nesting level

    • Level 0: Document root

    • Level 1: Top-level chapters/sections

    • Level 2: Subsections

    • Level 3 and higher: Deeper nested sections

Examples:

# Get all top-level chapters (Level 1)
$ dacli sections-at-level 1

# Get all second-level sections (Level 2)
$ dacli sections-at-level 2

# Error: Negative levels are not valid (Issue #199)
$ dacli sections-at-level -1
Error: Invalid value for 'level': Level must be non-negative, got -1.
Document hierarchies start at level 0 (document root).

Search & Elements Commands

Searches the documentation.

dacli search <QUERY> [--scope PATH] [--max-results N] [--limit N]

Options:

  • --max-results N, --limit N: Maximum results to return (default: 20, aliases)

  • --scope PATH: Restrict search to path prefix. Warns if scope path doesn’t exist in the documentation structure.

Example:

$ dacli search "authentication" --limit 5
{
  "query": "authentication",
  "results": [
    {"path": "security.auth", "context": "...implements authentication...", "score": 0.95}
  ],
  "total_results": 1
}

elements

Lists elements (code blocks, tables, etc.).

dacli elements [SECTION_PATH] [--type TYPE] [--recursive] [--include-content] [--content-limit N]

Arguments:

  • SECTION_PATH: Optional section path to filter elements (positional argument)

Options:

  • --type TYPE: Element type filter - code, table, image, plantuml, list, admonition. Warns if type is invalid but still returns empty result.

  • --recursive: Include elements from child sections (default: exact match only)

  • --include-content: Include element content and attributes (Issue #159)

  • --content-limit N: Limit content to first N lines (requires --include-content)

Examples:

# Get code blocks without content (metadata only)
$ dacli elements --type code

# Get code blocks with full content
$ dacli elements --type code --include-content

# Get code blocks with first 5 lines only
$ dacli elements --type code --include-content --content-limit 5

# Get all elements in a section with content
$ dacli elements api --recursive --include-content

Meta-Information Commands

metadata

Shows metadata for project or section.

dacli metadata [PATH]

Project metadata (without PATH):

$ dacli metadata
{
  "total_files": 15,
  "total_sections": 87,
  "total_words": 12450,
  "formats": ["asciidoc", "markdown"]
}

Section metadata (with PATH):

$ dacli metadata architecture.decisions
{
  "path": "architecture.decisions",
  "title": "Architecture Decisions",
  "word_count": 2340,
  "subsection_count": 5
}

validate

Validates the documentation structure.

dacli validate

Example:

$ dacli validate
{
  "valid": true,
  "errors": [],
  "warnings": [
    {"type": "orphaned_file", "path": "unused.adoc"}
  ],
  "validation_time_ms": 45
}

Manipulation Commands

update

Updates the content of a section.

dacli update <PATH> --content "..." [--no-preserve-title] [--expected-hash HASH]

Content Input:

  • Direct string: --content "New content here"

  • Escape sequences: --content "Line 1\nLine 2" (converts \n to actual newlines)

  • From stdin: --content - (reads from stdin)

Options:

  • --no-preserve-title - Use title from provided content instead of preserving original title. When this flag is set, the content must include a section title (starting with = for AsciiDoc or # for Markdown), otherwise the update will fail. This allows changing the section title while maintaining document structure.

  • --expected-hash HASH - Hash for optimistic locking (see examples below)

Examples:

# Direct content with escape sequences
$ dacli update introduction --content "First paragraph.\n\nSecond paragraph."
{
  "success": true,
  "path": "introduction",
  "previous_hash": "a1b2c3d4",
  "new_hash": "e5f6g7h8"
}

# Read content from stdin (useful for multi-line content)
$ cat new_content.txt | dacli update introduction --content -

# Pipe from another command
$ echo "Updated via stdin" | dacli update introduction --content -

# Change section title (requires --no-preserve-title)
$ dacli update introduction --content "== New Title\n\nUpdated content." --no-preserve-title
{
  "success": true,
  "path": "introduction"
}

# Error: --no-preserve-title requires content with title
$ dacli update introduction --content "Content without title" --no-preserve-title
{
  "success": false,
  "error": "Content must include a section title when preserve_title is false. Expected content to start with '=' (AsciiDoc) or '#' (Markdown)."
}

insert

Inserts content relative to a section.

dacli insert <PATH> --position before|after|append --content "..."

Position Options:

  • before - Insert before the section heading. Adds blank line after content if next line is a heading.

  • after - Insert after the section end, including all child sections (before next sibling at same or higher level)

  • append - Insert at end of section’s own content (before any child sections)

Content Input:

  • Direct string: --content "New content"

  • Escape sequences: --content "## Section\n\nContent" (converts \n to newlines)

  • From stdin: --content - (reads from stdin)

Note
When inserting content that starts with a heading (# or =) after existing content, a blank line is automatically added for proper formatting.

Examples:

# Insert new section with escape sequences
$ dacli insert architecture --position after --content "== New Section\n\nContent..."
{
  "success": true,
  "inserted_at": {"file": "arc42.adoc", "line": 150}
}

# Insert from stdin (for complex multi-line content)
$ cat new_section.adoc | dacli insert architecture --position after --content -

# Append to end of parent section (after all subsections)
$ dacli insert components --position append --content "=== New Component\n\nDetails..."

Exit Codes

Code Meaning

0

Success

1

General error

2

Invalid arguments

3

Path not found

4

Validation error

5

Write error

LLM Integration Example

An LLM can use the CLI via Bash tools:

# Query structure
structure=$(dacli structure --max-depth 2 --docs-root /project/docs)

# Search for topic
results=$(dacli search "database" | jq '.results[].path')

# Read section
content=$(dacli section architecture.decisions)

# Update documentation
dacli update api.endpoints --content "Updated API documentation..."