How the CLI-Anything Skill Generator Manages Software with Complex State or Session Management
The CLI-Anything framework separates skill generation from session management, using static analysis to extract CLI metadata while delegating complex state persistence to atomic JSON session files with file-locking mechanisms.
The HKUDS/CLI-Anything repository enables developers to wrap complex software—such as QGIS or audio editors—in AI-agent-compatible interfaces. Its skill generator creates structured documentation by parsing source code without needing to understand internal application state, while dedicated session management modules handle runtime persistence for projects with sophisticated undo/redo stacks and multi-file timelines.
Architectural Separation of Concerns
CLI-Anything implements two independent systems that communicate only through the command interface:
- Skill Generator (
cli-anything-plugin/skill_generator.py) – A static analysis tool that producesSKILL.mddocumentation describing the public command surface. - Session Management (
QGIS/agent-harness/cli_anything/qgis/core/session.py) – A runtime module that persists user project paths, command history, and state to disk using atomic writes.
This separation allows the skill generator to remain agnostic to internal state complexity while still documenting every command—including those that manipulate that state.
Skill Generation Without Runtime State Awareness
The skill generator never inspects running application memory or session files. Instead, it extracts metadata from static sources to build the SKILL.md files that AI agents consume.
Metadata Extraction Pipeline
The generator uses a series of extraction functions to build a complete command vocabulary:
extract_cli_metadata(lines 69-122) – Coordinates the extraction process by readingREADME.md,setup.py, and CLI source files.extract_intro_from_readme– Pulls software descriptions from documentation.extract_version_from_setup– Captures version strings for compatibility tracking.extract_commands_from_cli(lines 191-260) – Parses the actual command definitions using regex patterns.
This pipeline captures the software name, description, system-package hints, and command groups without executing any application code.
Parsing Complex Command Hierarchies
For software with nested command groups (e.g., gis project export or audio track filter), the generator uses robust regular expressions that handle Click decorators spread across multiple lines:
# Regex patterns defined in skill_generator.py (lines 212-250)
group_pattern = r'@(\w+)\.group\(.*?\)'
command_pattern = r'@(\w+)\.command\(.*?\)'
These patterns identify group_pattern for command categories and command_pattern for individual operations, capturing optional docstrings and additional decorators regardless of formatting complexity.
Template Rendering with Fallbacks
The generate_skill_md function (lines 321-367) prefers Jinja2 rendering using templates/SKILL.md.template, but implements generate_skill_md_simple (lines 371-447) as a pure-Python fallback. This ensures the generator never fails due to missing optional dependencies, even when documenting software with the most intricate state management requirements.
Session Management for Complex Application State
While the generator handles documentation, the session module manages the runtime reality of complex software that maintains projects, layers, and undo histories.
Atomic Persistence with File Locking
The _locked_save_json method (lines 12-40 in session.py) prevents corruption when multiple REPL processes write simultaneously:
def _locked_save_json(self, data: dict, path: Path):
with open(path, 'w') as f:
# Exclusive lock prevents race conditions
fcntl.flock(f, fcntl.LOCK_EX)
json.dump(data, f)
fcntl.flock(f, fcntl.LOCK_UN)
This implementation uses flock for Unix-like systems with a graceful fallback to simple writes when locking is unavailable, ensuring session integrity during long-running exports or renders.
History Tracking and Project Isolation
The HistoryEntry dataclass (lines 42-71) records command names, arguments, UTC timestamps, and result payloads. The Session object (lines 73-143) maintains:
current_project_path– Isolates state per project, preventing cross-contamination when switching between files.- Command history – An append-only list supporting
undoandredooperations. - Auto-save triggers – The
_auto_savemethod (lines 98-107) persists changes whenever the project path updates or new commands are recorded.
Robust Recovery Mechanisms
The _load method (lines 133-143) tolerates missing or malformed JSON files by defaulting to a fresh empty session. This resilience ensures that corrupted session files never prevent application startup.
Bridging Static Documentation and Dynamic State
When a harness implements session-aware commands—such as project open, undo, or redo—the skill generator captures these automatically through static analysis. Because these commands are defined as standard Click decorators in *_cli.py files, they appear in the generated SKILL.md:
### Session
Commands for session operations.
| Command | Description |
|---------|-------------|
| `project-open` | Open a GIS project file. |
| `undo` | Revert the last command. |
| `redo` | Re‑apply a reverted command. |
AI agents receive the complete command vocabulary needed to manipulate complex state, while the underlying session logic remains encapsulated in session.py.
Practical Implementation Examples
Generating Skills for Complex Software
Generate documentation for QGIS with its sophisticated project state:
python -m cli_anything_plugin.skill_generator \
/path/to/CLI-Anything/main/QGIS/agent-harness \
-o /tmp/QGIS_SKILL.md
The resulting file contains the full command list including project management operations, regardless of QGIS's internal layer management complexity.
Managing Sessions in REPL Skins
from cli_anything.qgis.core.session import Session
from pathlib import Path
# Initialize with atomic persistence
sess = Session(session_file=str(Path.home() / ".qgis_cli_session.json"))
# Track project state
sess.set_project_path("/home/user/maps/city.qgz")
print(sess.status())
# {'current_project_path': '/home/user/maps/city.qgz', 'history_count': 0}
# Record operations for undo support
sess.record("add-layers", {"count": 3}, {"layers_added": 3})
print(sess.history(limit=1))
# [HistoryEntry(command='add-layers', args={'count': 3}, ...)]
Adding Session Commands to the CLI
import click
@click.command()
def undo():
"""Undo the last operation."""
# Implementation accesses session history stack
# After adding, rerun the skill generator
# The new SKILL.md automatically includes the undo command
Summary
- Static Analysis: The skill generator extracts command metadata from
README.md,setup.py, and*_cli.pywithout executing application code or understanding internal state. - Session Isolation: The
Sessionclass insession.pyprovides atomic JSON persistence, file locking, and project-specific state isolation. - Automatic Documentation: Session-aware commands defined in Click decorators automatically appear in generated
SKILL.mdfiles. - Thread Safety:
_locked_save_jsonusesflockto prevent corruption during concurrent REPL access. - Resilient Design: Both modules operate independently, allowing complex software like QGIS to maintain sophisticated undo/redo stacks while presenting a clean command interface to AI agents.
Frequently Asked Questions
Does the skill generator need to understand the software's internal state?
No. The skill generator only parses static source files—specifically Click decorators in *_cli.py and documentation in README.md. It extracts the public command surface using regex patterns (lines 212-250 in skill_generator.py) without inspecting runtime memory, session files, or application state. This design allows it to document software with arbitrarily complex internal state machines while remaining completely agnostic to their implementation.
How does CLI-Anything prevent session file corruption?
The session module implements atomic writes through _locked_save_json (lines 12-40 in session.py), which uses Unix file locking (flock) to obtain exclusive access before writing JSON data. If the locking mechanism is unavailable, it gracefully falls back to standard writes while maintaining backward compatibility. This prevents race conditions when multiple REPL processes attempt to update the session simultaneously during long-running operations.
Can the skill generator handle nested command groups and complex CLI hierarchies?
Yes. The generator uses the group_pattern and command_pattern regular expressions to identify Click decorators even when spread across multiple lines with complex argument signatures. It captures nested hierarchies (e.g., gis project export format) by parsing the decorator chain in the CLI source code, then renders these as organized sections in the Jinja2 template (templates/SKILL.md.template).
How are session-related commands documented for AI agents?
When developers implement session commands like project open or undo as standard Click functions in their harness CLI, the skill generator's extract_commands_from_cli function (lines 191-260) captures these during static analysis. The generated SKILL.md includes a "Session" command group table describing these operations, allowing AI agents to understand how to manipulate project state and history without accessing the underlying Session class implementation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →