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.
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 helpCommands are organized into story-based groups in the help output:
| Group | Commands |
|---|---|
Discover |
|
Find |
|
Read |
|
Validate |
|
Edit |
|
If you mistype a command, dacli suggests the correct command:
$ dacli serch "API"
Error: No such command 'serch'.
Did you mean: searchFor faster typing, all commands have short aliases:
| Alias | Command | Description |
|---|---|---|
|
|
Show document structure |
|
|
Show project/section metadata |
|
|
Search documentation |
|
|
Sections at level N |
|
|
Read section content |
|
|
Get code/tables/images |
|
|
Validate structure |
Example:
# These are equivalent:
dacli --format json search "API"
dacli --format json s "API"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
}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"]
}
}
}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).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
}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-contentShows 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
}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\nto 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)."
}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\nto 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..."| Code | Meaning |
|---|---|
0 |
Success |
1 |
General error |
2 |
Invalid arguments |
3 |
Path not found |
4 |
Validation error |
5 |
Write error |
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..."