llm-tool-cli 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- llm_tool_cli-0.2.0/.gitignore +19 -0
- llm_tool_cli-0.2.0/CHANGELOG.md +38 -0
- llm_tool_cli-0.2.0/LICENSE +28 -0
- llm_tool_cli-0.2.0/PKG-INFO +275 -0
- llm_tool_cli-0.2.0/README.md +251 -0
- llm_tool_cli-0.2.0/llm_tool_cli/__init__.py +0 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/__init__.py +1 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/application.py +31 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/commands/__init__.py +0 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/commands/initialization.py +36 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/commands/skills.py +25 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/commands/tests/__init__.py +0 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/commands/tests/test_initialization.py +130 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/commands/tests/test_skills.py +154 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/commands/tests/test_version.py +98 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/commands/version.py +17 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/context.py +39 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/entities.py +3 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/errors.py +15 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/handling.py +41 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/options.py +48 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/tests/__init__.py +0 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/tests/test_application.py +167 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/tests/test_context.py +179 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/tests/test_handling.py +230 -0
- llm_tool_cli-0.2.0/llm_tool_cli/cli/tests/test_options.py +125 -0
- llm_tool_cli-0.2.0/llm_tool_cli/config/__init__.py +33 -0
- llm_tool_cli-0.2.0/llm_tool_cli/config/errors.py +92 -0
- llm_tool_cli-0.2.0/llm_tool_cli/config/fields.py +39 -0
- llm_tool_cli-0.2.0/llm_tool_cli/config/files.py +175 -0
- llm_tool_cli-0.2.0/llm_tool_cli/config/project.py +87 -0
- llm_tool_cli-0.2.0/llm_tool_cli/config/runtime.py +14 -0
- llm_tool_cli-0.2.0/llm_tool_cli/config/tests/__init__.py +0 -0
- llm_tool_cli-0.2.0/llm_tool_cli/config/tests/fixtures.py +34 -0
- llm_tool_cli-0.2.0/llm_tool_cli/config/tests/test_fields.py +124 -0
- llm_tool_cli-0.2.0/llm_tool_cli/config/tests/test_files.py +826 -0
- llm_tool_cli-0.2.0/llm_tool_cli/config/tests/test_project.py +257 -0
- llm_tool_cli-0.2.0/llm_tool_cli/core/__init__.py +1 -0
- llm_tool_cli-0.2.0/llm_tool_cli/core/context.py +50 -0
- llm_tool_cli-0.2.0/llm_tool_cli/core/entities.py +35 -0
- llm_tool_cli-0.2.0/llm_tool_cli/core/errors.py +97 -0
- llm_tool_cli-0.2.0/llm_tool_cli/core/result.py +118 -0
- llm_tool_cli-0.2.0/llm_tool_cli/core/settings.py +37 -0
- llm_tool_cli-0.2.0/llm_tool_cli/core/tests/__init__.py +0 -0
- llm_tool_cli-0.2.0/llm_tool_cli/core/tests/fixtures.py +13 -0
- llm_tool_cli-0.2.0/llm_tool_cli/core/tests/test_context.py +152 -0
- llm_tool_cli-0.2.0/llm_tool_cli/core/tests/test_entities.py +65 -0
- llm_tool_cli-0.2.0/llm_tool_cli/core/tests/test_errors.py +211 -0
- llm_tool_cli-0.2.0/llm_tool_cli/core/tests/test_result.py +256 -0
- llm_tool_cli-0.2.0/llm_tool_cli/core/tests/test_settings.py +64 -0
- llm_tool_cli-0.2.0/llm_tool_cli/output/__init__.py +3 -0
- llm_tool_cli-0.2.0/llm_tool_cli/output/emitter.py +108 -0
- llm_tool_cli-0.2.0/llm_tool_cli/output/errors.py +21 -0
- llm_tool_cli-0.2.0/llm_tool_cli/output/journal.py +66 -0
- llm_tool_cli-0.2.0/llm_tool_cli/output/tests/__init__.py +0 -0
- llm_tool_cli-0.2.0/llm_tool_cli/output/tests/helpers.py +29 -0
- llm_tool_cli-0.2.0/llm_tool_cli/output/tests/test_emitter.py +406 -0
- llm_tool_cli-0.2.0/llm_tool_cli/output/tests/test_journal.py +141 -0
- llm_tool_cli-0.2.0/llm_tool_cli/paths/__init__.py +51 -0
- llm_tool_cli-0.2.0/llm_tool_cli/paths/enumeration.py +72 -0
- llm_tool_cli-0.2.0/llm_tool_cli/paths/errors.py +42 -0
- llm_tool_cli-0.2.0/llm_tool_cli/paths/filesystem.py +164 -0
- llm_tool_cli-0.2.0/llm_tool_cli/paths/globs.py +124 -0
- llm_tool_cli-0.2.0/llm_tool_cli/paths/normalization.py +58 -0
- llm_tool_cli-0.2.0/llm_tool_cli/paths/tests/__init__.py +0 -0
- llm_tool_cli-0.2.0/llm_tool_cli/paths/tests/test_enumeration.py +229 -0
- llm_tool_cli-0.2.0/llm_tool_cli/paths/tests/test_filesystem.py +778 -0
- llm_tool_cli-0.2.0/llm_tool_cli/paths/tests/test_globs.py +137 -0
- llm_tool_cli-0.2.0/llm_tool_cli/paths/tests/test_normalization.py +163 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/__init__.py +4 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/cell_shortcuts.py +39 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/entities.py +7 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/errors.py +21 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/journal.py +21 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/logic_cells/__init__.py +4 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/logic_cells/base.py +41 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/logic_cells/content.py +26 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/logic_cells/environment_error.py +36 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/logic_cells/journal.py +44 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/logic_cells/tests/__init__.py +0 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/logic_cells/tests/test_base.py +47 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/logic_cells/tests/test_content.py +59 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/logic_cells/tests/test_environment_error.py +130 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/logic_cells/tests/test_journal.py +47 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/logic_cells/tests/test_uniform.py +60 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/logic_cells/tests/test_warning.py +34 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/logic_cells/uniform.py +21 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/logic_cells/warning.py +22 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/output_cells/__init__.py +11 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/output_cells/automation.py +14 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/output_cells/base.py +65 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/output_cells/human.py +24 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/output_cells/journal.py +23 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/output_cells/llm.py +25 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/output_cells/tests/__init__.py +0 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/output_cells/tests/make.py +15 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/output_cells/tests/test_automation.py +58 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/output_cells/tests/test_base.py +86 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/output_cells/tests/test_human.py +112 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/output_cells/tests/test_llm.py +65 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/serialization.py +7 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/tests/__init__.py +0 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/tests/helpers.py +25 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/tests/test_cell_shortcuts.py +126 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/tests/test_journal.py +44 -0
- llm_tool_cli-0.2.0/llm_tool_cli/protocol/tests/test_serialization.py +33 -0
- llm_tool_cli-0.2.0/llm_tool_cli/py.typed +0 -0
- llm_tool_cli-0.2.0/llm_tool_cli/skills/__init__.py +6 -0
- llm_tool_cli-0.2.0/llm_tool_cli/skills/errors.py +12 -0
- llm_tool_cli-0.2.0/llm_tool_cli/skills/fixtures.py +14 -0
- llm_tool_cli-0.2.0/llm_tool_cli/skills/tests/__init__.py +0 -0
- llm_tool_cli-0.2.0/llm_tool_cli/skills/tests/test_fixtures.py +58 -0
- llm_tool_cli-0.2.0/pyproject.toml +103 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Python environments and build output
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
.venv/
|
|
5
|
+
*.egg-info/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
|
|
9
|
+
# Development caches and project state
|
|
10
|
+
.mypy_cache/
|
|
11
|
+
.pytest_cache/
|
|
12
|
+
.session/
|
|
13
|
+
.tach/
|
|
14
|
+
|
|
15
|
+
# Local credentials and editor backups
|
|
16
|
+
.env
|
|
17
|
+
.envrc
|
|
18
|
+
.pypirc
|
|
19
|
+
*~
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
Generated by [Changy](https://github.com/Tiendil/changy/tree/main).
|
|
6
|
+
|
|
7
|
+
## Unreleased
|
|
8
|
+
|
|
9
|
+
No changes.
|
|
10
|
+
|
|
11
|
+
## 0.2.0 on 2026-10-21
|
|
12
|
+
|
|
13
|
+
Moved common functionality from `donna` and `depmesh` into this library.
|
|
14
|
+
|
|
15
|
+
### Changes
|
|
16
|
+
|
|
17
|
+
- Added shared Pydantic entities, `Result[T]`, and structured environment errors.
|
|
18
|
+
- Added independent typed `ContextValue` bindings and separate frozen `StaticConfig`, `RuntimeConfig`, and `ProjectConfig` objects for application identity, effective protocol, and prepared project location. Commands that do not use a project need no project configuration.
|
|
19
|
+
- Added TOML 1.1 support.
|
|
20
|
+
- Added project loading and initialization functionality.
|
|
21
|
+
- Added independently reusable `ConfigVersion` and `SessionDir` fields.
|
|
22
|
+
- Added typed filesystem inputs and canonical `@/` project identifiers, with lexical normalization, mixed filesystem/identifier resolution, home expansion, and structured failures.
|
|
23
|
+
- Added shared glob matching and deterministic file enumeration using the same grammar:
|
|
24
|
+
|
|
25
|
+
- Case-sensitive `*`, `?`, bracket classes, and bracket escaping; hidden entries match like ordinary names.
|
|
26
|
+
- `**` may cross directory separators even inside a segment; `**/` may match zero directories.
|
|
27
|
+
- Named `{*name}` and `{**name}` captures support identifier matching.
|
|
28
|
+
- Enumeration returns unique regular files in path-component order and reports filesystem failures instead of returning partial results.
|
|
29
|
+
|
|
30
|
+
- Established logic cells as the shared domain-to-CLI output interface, with extensible projections into human, LLM, and automation output cells.
|
|
31
|
+
- Provided default content, warning, journal, and environment-error cells.
|
|
32
|
+
- Automation protocol now outputs compact Unicode JSON Lines with flattened metadata.
|
|
33
|
+
- Added a context-bound `OutputEmitter` and stream implementation.
|
|
34
|
+
- Added `output.get().journal(...)`, configurable journal-cell factories, and `JournalRecord`.
|
|
35
|
+
- Command processors execute argument lists without a shell and substitute whole-argument record placeholders; missing or null fields become empty arguments. Compact text journals occupy one physical line while automation retains normalized multiline messages.
|
|
36
|
+
- Added shared exit-status policy: success uses `0`, explicit invalid arguments `1`, configuration errors `2`, and other environment errors `3` by default.
|
|
37
|
+
- Added Typer application and command-context helpers.
|
|
38
|
+
- Added reusable `init`, `version`, and `skill` command registration.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Aliaksei Yaletski
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: llm-tool-cli
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Shared foundations for LLM-oriented Python command-line tools.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Tiendil/llm-tool-cli
|
|
6
|
+
Project-URL: Repository, https://github.com/Tiendil/llm-tool-cli
|
|
7
|
+
Project-URL: Issues, https://github.com/Tiendil/llm-tool-cli/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/Tiendil/llm-tool-cli/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Aliaksei Yaletski <a.eletsky@gmail.com>
|
|
10
|
+
License-Expression: BSD-3-Clause
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ai-agents,cli,developer-tools,llm
|
|
13
|
+
Classifier: Development Status :: 1 - Planning
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: <4.0,>=3.12
|
|
20
|
+
Requires-Dist: pydantic<3,>=2.12
|
|
21
|
+
Requires-Dist: tomli<3,>=2.4.1
|
|
22
|
+
Requires-Dist: typer~=0.25.1
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# llm-tool-cli
|
|
26
|
+
|
|
27
|
+
Reusable Python foundations for CLI tools used by humans, LLM agents, and automation.
|
|
28
|
+
Import it as `llm_tool_cli` to build your own tool; the library has no standalone CLI entry point.
|
|
29
|
+
|
|
30
|
+
At its current stage, this is a personal project of [Tiendil](https://tiendil.org), developed as the shared foundation for:
|
|
31
|
+
|
|
32
|
+
- [Donna](https://github.com/Tiendil/donna), which runs deterministic workflows for agents.
|
|
33
|
+
- [Depmesh](https://github.com/Tiendil/depmesh), which discovers dependencies between project artifacts.
|
|
34
|
+
|
|
35
|
+
## Goals
|
|
36
|
+
|
|
37
|
+
1. Provide the ability to quickly construct tools targeted at specific tasks for AI agents.
|
|
38
|
+
2. Experiment with approaches to improve the usability of CLI tools for agents.
|
|
39
|
+
3. Unify the behavior of Tiendil’s tools.
|
|
40
|
+
|
|
41
|
+
## Capabilities
|
|
42
|
+
|
|
43
|
+
- **CLI application foundations** — Give commands a consistent structure and execution lifecycle.
|
|
44
|
+
- **Unified output** — Present application results to people, LLM agents, and automation through a shared output model.
|
|
45
|
+
- **Configuration management** — Reuse project configuration handling while keeping application settings under your control.
|
|
46
|
+
- **Project filesystem operations** — Use a common interpretation of project paths and file selection.
|
|
47
|
+
- **Journaling and diagnostics** — Report progress and problems through a common output system.
|
|
48
|
+
- **Scoped application resources** — Make resources available throughout the parts of an invocation that need them.
|
|
49
|
+
|
|
50
|
+
The library also provides reusable subcommands for applications to register:
|
|
51
|
+
|
|
52
|
+
- `init` — Create project configuration from the application's template.
|
|
53
|
+
- `version` — Print the installed version of the application's distribution.
|
|
54
|
+
- `skill [DOCUMENT]` — Print packaged usage or other skill documentation supplied by the application.
|
|
55
|
+
|
|
56
|
+
## Output protocols
|
|
57
|
+
|
|
58
|
+
A protocol determines how a tool presents its results and diagnostics:
|
|
59
|
+
|
|
60
|
+
- `human` — Plain text blocks for people using a terminal.
|
|
61
|
+
- `llm` — Text with explicit cell boundaries and metadata for AI agents.
|
|
62
|
+
- `automation` — JSON Lines records for programs consuming output.
|
|
63
|
+
|
|
64
|
+
Select the protocol with the global `-p` option before the subcommand.
|
|
65
|
+
All three protocols use stdout by default.
|
|
66
|
+
|
|
67
|
+
## Data flow
|
|
68
|
+
|
|
69
|
+
**Domain code** performs the tool's actual work, such as finding dependencies, independently of CLI parsing and output formatting.
|
|
70
|
+
The **output emitter** is the shared output service that manages buffering, rendering, and writing.
|
|
71
|
+
|
|
72
|
+
1. A person, agent, or program invokes a command through the tool's CLI.
|
|
73
|
+
2. The CLI parses options and arguments and selects the command to run.
|
|
74
|
+
3. The command context chooses the effective protocol and makes an emitter available for the invocation.
|
|
75
|
+
4. The command calls domain code, which performs the requested work and constructs **logical cells**: typed data describing the results to present.
|
|
76
|
+
5. The command or domain code submits these cells to the emitter, which buffers ordinary results.
|
|
77
|
+
6. When delivery is due, each logical cell projects to zero or more **output cells** containing presentation data for the selected protocol.
|
|
78
|
+
7. Output cells format the prepared data.
|
|
79
|
+
8. The emitter writes the formatted output to its configured stream.
|
|
80
|
+
|
|
81
|
+
Submission and delivery happen at different times:
|
|
82
|
+
|
|
83
|
+
- Ordinary results wait for successful command completion or an explicit `flush()`; projection and formatting happen at delivery.
|
|
84
|
+
- Warnings are collected in submission order and delivered after results, or after reported errors when execution fails.
|
|
85
|
+
- Progress journal cells are processed when submitted and emitted immediately on success, so they can appear before results submitted earlier.
|
|
86
|
+
Use journal messages to keep agents informed about progress during long operations, while final results are still pending.
|
|
87
|
+
|
|
88
|
+
The [output contract](specs/behavior/protocol.md) describes the complete flow and delivery rules.
|
|
89
|
+
|
|
90
|
+
## Error handling
|
|
91
|
+
|
|
92
|
+
The library distinguishes **internal errors** from **environment errors**.
|
|
93
|
+
|
|
94
|
+
Internal errors indicate programming defects or violated assumptions in the tool itself.
|
|
95
|
+
They are raised as exceptions, with `InternalError` as the shared base for library-defined internal failures.
|
|
96
|
+
Unexpected exceptions propagate through command handling without flushing pending results.
|
|
97
|
+
|
|
98
|
+
Environment errors describe expected failures in an operation's documented contract, such as invalid project configuration or an unreadable input file.
|
|
99
|
+
They are structured `EnvironmentError` values: a code identifies the failure kind, a message explains it, and context supplies details such as affected paths.
|
|
100
|
+
Environment errors can also include suggested corrections in a `ways_to_fix` list.
|
|
101
|
+
The rendered diagnostic includes these suggestions, so an agent can select and apply a fix automatically, then retry the command.
|
|
102
|
+
|
|
103
|
+
Operations return these values through `Result[T]`: `Ok(value)` for success or `Err(errors)` for a list of environment errors.
|
|
104
|
+
|
|
105
|
+
At the CLI boundary, `command_context` handles unsuccessful `.unwrap()` calls by reporting those environment errors as diagnostic logical cells in the selected protocol.
|
|
106
|
+
It discards pending ordinary results, delivers collected warnings after the diagnostics, and exits with the highest status declared by the errors.
|
|
107
|
+
|
|
108
|
+
Warnings describe non-fatal problems and do not cause command failure.
|
|
109
|
+
See the [error architecture](specs/architecture/errors.md) and [command error-handling contract](specs/behavior/cli.md#command-error-handling) for details.
|
|
110
|
+
|
|
111
|
+
## Scoped resources
|
|
112
|
+
|
|
113
|
+
`ContextValue[T]` makes a typed resource available to code running within a chosen execution scope.
|
|
114
|
+
Each resource has its own independent binding.
|
|
115
|
+
Code reads the active value through `.get()`, such as `runtime_config.get()` or `output.get()`; domain code can access these bindings without depending on the CLI layer.
|
|
116
|
+
Values can still be passed explicitly as function arguments.
|
|
117
|
+
|
|
118
|
+
Shared configuration distinguishes three lifetimes:
|
|
119
|
+
|
|
120
|
+
- **Application identity — `StaticConfig`.** Initialized before CLI parsing, it contains the tool label and optional default configuration filename.
|
|
121
|
+
- **Command invocation — `RuntimeConfig`.** Contains the effective output protocol.
|
|
122
|
+
The shared command context establishes it together with an independently bound output emitter.
|
|
123
|
+
- **Selected project — `ProjectConfig`.** Contains the selected configuration path and the project directory derived from its parent.
|
|
124
|
+
Shared project preparation establishes this binding after successful configuration validation.
|
|
125
|
+
Commands that do not use a project need no project binding.
|
|
126
|
+
|
|
127
|
+
The application's loaded settings remain separate from `ProjectConfig`, following the application's schema and defaults.
|
|
128
|
+
|
|
129
|
+
Applications can define bindings for their own resources, such as caches or execution state, and choose the scope each resource needs.
|
|
130
|
+
For example, an operation can make its execution state available only while that operation runs.
|
|
131
|
+
Resources are prepared only where needed; these scopes do not have to follow a fixed hierarchy.
|
|
132
|
+
|
|
133
|
+
Use a binding's `scope(value)` context manager to temporarily supply or override its value; leaving the scope restores the previous binding.
|
|
134
|
+
Accessing an unset binding raises an internal error indicating missing setup.
|
|
135
|
+
Binding an object does not construct, copy, or close it; the code managing that resource owns its setup and cleanup.
|
|
136
|
+
|
|
137
|
+
See the [context architecture](specs/architecture/modules_layout.md#core-and-context) and [project scopes](specs/behavior/config.md#project-scopes) for details.
|
|
138
|
+
|
|
139
|
+
## Reusable configuration elements
|
|
140
|
+
|
|
141
|
+
Tools compose their own configuration schemas from shared fields and configuration blocks alongside application-specific settings.
|
|
142
|
+
Each shared element is independently reusable:
|
|
143
|
+
|
|
144
|
+
- **Configuration version — `ConfigVersion`.** Validates integer versions and provides clear diagnostics for invalid or unsupported values.
|
|
145
|
+
Each tool chooses its supported versions and defaults.
|
|
146
|
+
- **Session directory — `SessionDir`.** Validates a project-relative path, rejecting absolute paths and parent-directory references.
|
|
147
|
+
The tool owns its default location and session storage.
|
|
148
|
+
- **Journal configuration — `JournalConfig`.** Configures an optional command for forwarding journal records externally.
|
|
149
|
+
External forwarding is disabled by default.
|
|
150
|
+
|
|
151
|
+
For example, a tool can combine these elements with its own greeting setting:
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from pathlib import Path
|
|
155
|
+
from typing import Literal
|
|
156
|
+
|
|
157
|
+
import pydantic
|
|
158
|
+
|
|
159
|
+
from llm_tool_cli.config.fields import ConfigVersion, SessionDir
|
|
160
|
+
from llm_tool_cli.core.entities import BaseEntity
|
|
161
|
+
from llm_tool_cli.output.journal import JournalConfig
|
|
162
|
+
from llm_tool_cli.paths import RelativeProjectPath
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class Config(BaseEntity):
|
|
166
|
+
version: ConfigVersion[Literal[1]] = 1
|
|
167
|
+
session_dir: SessionDir = RelativeProjectPath(Path(".session"))
|
|
168
|
+
journal: JournalConfig = pydantic.Field(default_factory=JournalConfig)
|
|
169
|
+
greeting: str = "Hello"
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The version constraint and defaults above belong to this example tool.
|
|
173
|
+
See the [configuration contract](specs/behavior/config.md) for shared loading and validation behavior.
|
|
174
|
+
|
|
175
|
+
## Quick start
|
|
176
|
+
|
|
177
|
+
This example creates a greeting in domain code and submits it from a CLI command.
|
|
178
|
+
|
|
179
|
+
With the library available in your Python environment, save this as `example.py`:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
import typer
|
|
183
|
+
|
|
184
|
+
from llm_tool_cli.cli.application import create_app
|
|
185
|
+
from llm_tool_cli.cli.context import command_context
|
|
186
|
+
from llm_tool_cli.core.settings import ToolLabel, initialize
|
|
187
|
+
from llm_tool_cli.output import output
|
|
188
|
+
from llm_tool_cli.protocol.logic_cells import ContentCell
|
|
189
|
+
|
|
190
|
+
initialize(label=ToolLabel("DEMO"))
|
|
191
|
+
app = create_app(help="A greeting tool built with llm-tool-cli.")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def greeting(name: str) -> ContentCell:
|
|
195
|
+
return ContentCell(
|
|
196
|
+
kind="greeting",
|
|
197
|
+
media_type="text/plain",
|
|
198
|
+
content=f"Hello, {name}!",
|
|
199
|
+
meta={"type": "greeting", "name": name},
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@app.command()
|
|
204
|
+
def greet(context: typer.Context, name: str) -> None:
|
|
205
|
+
with command_context(context):
|
|
206
|
+
output.get().emit([greeting(name)]).unwrap()
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
if __name__ == "__main__":
|
|
210
|
+
app()
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The command context selects the protocol, establishes the active emitter, and delivers buffered results on successful completion.
|
|
214
|
+
No project configuration or journal setup is needed for this example.
|
|
215
|
+
|
|
216
|
+
Run the greeting command with each of the three protocols:
|
|
217
|
+
|
|
218
|
+
### Human
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
python example.py -p human greet Ada
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
```text
|
|
225
|
+
name: Ada
|
|
226
|
+
|
|
227
|
+
Hello, Ada!
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### LLM
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
python example.py -p llm greet Ada
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
```text
|
|
238
|
+
--DEMO-CELL <id> BEGIN--
|
|
239
|
+
kind=greeting
|
|
240
|
+
media_type=text/plain
|
|
241
|
+
name=Ada
|
|
242
|
+
type=greeting
|
|
243
|
+
|
|
244
|
+
Hello, Ada!
|
|
245
|
+
--DEMO-CELL <id> END--
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Automation
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
python example.py -p automation greet Ada
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
```jsonl
|
|
255
|
+
{"content":"Hello, Ada!","id":"<id>","name":"Ada","type":"greeting"}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
`<id>` represents a generated cell identifier.
|
|
259
|
+
|
|
260
|
+
## Integration
|
|
261
|
+
|
|
262
|
+
Use these extension points to adapt the library to your tool:
|
|
263
|
+
|
|
264
|
+
- **Custom logical cells** — Extend `LogicCell` to give protocols different presentations, or use `ContentCell` when they can share a payload.
|
|
265
|
+
[Depmesh](https://github.com/Tiendil/depmesh) uses custom logical cells to present grouped dependency lists as text and individual dependency records for automation.
|
|
266
|
+
- **Custom output cells** — Reuse the standard formatters or specialize them, such as by subclassing `HumanOutputCell` and selecting it from a logical cell's human projection.
|
|
267
|
+
Formatting stays separate from stream handling.
|
|
268
|
+
- **Application-owned configuration** — Supply your own schema and defaults, then use shared project scopes to locate and validate configuration and expose the active project.
|
|
269
|
+
See the [configuration contract](specs/behavior/config.md).
|
|
270
|
+
- **Scoped resources** — Add bindings for application-owned resources using the [scoping mechanism](#scoped-resources).
|
|
271
|
+
- **Journal delivery** — Use `output.get().journal("Starting work").unwrap()` to report explicit progress.
|
|
272
|
+
A journal-cell factory can add application context, and a journal processor can deliver records externally; the default processor performs no external delivery.
|
|
273
|
+
[Donna](https://github.com/Tiendil/donna) supplies a factory that adds workflow execution context to journal cells.
|
|
274
|
+
|
|
275
|
+
See the [specification index](specs/intro.md) for the complete contracts, including [CLI support](specs/behavior/cli.md) and [project paths](specs/behavior/paths.md).
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# llm-tool-cli
|
|
2
|
+
|
|
3
|
+
Reusable Python foundations for CLI tools used by humans, LLM agents, and automation.
|
|
4
|
+
Import it as `llm_tool_cli` to build your own tool; the library has no standalone CLI entry point.
|
|
5
|
+
|
|
6
|
+
At its current stage, this is a personal project of [Tiendil](https://tiendil.org), developed as the shared foundation for:
|
|
7
|
+
|
|
8
|
+
- [Donna](https://github.com/Tiendil/donna), which runs deterministic workflows for agents.
|
|
9
|
+
- [Depmesh](https://github.com/Tiendil/depmesh), which discovers dependencies between project artifacts.
|
|
10
|
+
|
|
11
|
+
## Goals
|
|
12
|
+
|
|
13
|
+
1. Provide the ability to quickly construct tools targeted at specific tasks for AI agents.
|
|
14
|
+
2. Experiment with approaches to improve the usability of CLI tools for agents.
|
|
15
|
+
3. Unify the behavior of Tiendil’s tools.
|
|
16
|
+
|
|
17
|
+
## Capabilities
|
|
18
|
+
|
|
19
|
+
- **CLI application foundations** — Give commands a consistent structure and execution lifecycle.
|
|
20
|
+
- **Unified output** — Present application results to people, LLM agents, and automation through a shared output model.
|
|
21
|
+
- **Configuration management** — Reuse project configuration handling while keeping application settings under your control.
|
|
22
|
+
- **Project filesystem operations** — Use a common interpretation of project paths and file selection.
|
|
23
|
+
- **Journaling and diagnostics** — Report progress and problems through a common output system.
|
|
24
|
+
- **Scoped application resources** — Make resources available throughout the parts of an invocation that need them.
|
|
25
|
+
|
|
26
|
+
The library also provides reusable subcommands for applications to register:
|
|
27
|
+
|
|
28
|
+
- `init` — Create project configuration from the application's template.
|
|
29
|
+
- `version` — Print the installed version of the application's distribution.
|
|
30
|
+
- `skill [DOCUMENT]` — Print packaged usage or other skill documentation supplied by the application.
|
|
31
|
+
|
|
32
|
+
## Output protocols
|
|
33
|
+
|
|
34
|
+
A protocol determines how a tool presents its results and diagnostics:
|
|
35
|
+
|
|
36
|
+
- `human` — Plain text blocks for people using a terminal.
|
|
37
|
+
- `llm` — Text with explicit cell boundaries and metadata for AI agents.
|
|
38
|
+
- `automation` — JSON Lines records for programs consuming output.
|
|
39
|
+
|
|
40
|
+
Select the protocol with the global `-p` option before the subcommand.
|
|
41
|
+
All three protocols use stdout by default.
|
|
42
|
+
|
|
43
|
+
## Data flow
|
|
44
|
+
|
|
45
|
+
**Domain code** performs the tool's actual work, such as finding dependencies, independently of CLI parsing and output formatting.
|
|
46
|
+
The **output emitter** is the shared output service that manages buffering, rendering, and writing.
|
|
47
|
+
|
|
48
|
+
1. A person, agent, or program invokes a command through the tool's CLI.
|
|
49
|
+
2. The CLI parses options and arguments and selects the command to run.
|
|
50
|
+
3. The command context chooses the effective protocol and makes an emitter available for the invocation.
|
|
51
|
+
4. The command calls domain code, which performs the requested work and constructs **logical cells**: typed data describing the results to present.
|
|
52
|
+
5. The command or domain code submits these cells to the emitter, which buffers ordinary results.
|
|
53
|
+
6. When delivery is due, each logical cell projects to zero or more **output cells** containing presentation data for the selected protocol.
|
|
54
|
+
7. Output cells format the prepared data.
|
|
55
|
+
8. The emitter writes the formatted output to its configured stream.
|
|
56
|
+
|
|
57
|
+
Submission and delivery happen at different times:
|
|
58
|
+
|
|
59
|
+
- Ordinary results wait for successful command completion or an explicit `flush()`; projection and formatting happen at delivery.
|
|
60
|
+
- Warnings are collected in submission order and delivered after results, or after reported errors when execution fails.
|
|
61
|
+
- Progress journal cells are processed when submitted and emitted immediately on success, so they can appear before results submitted earlier.
|
|
62
|
+
Use journal messages to keep agents informed about progress during long operations, while final results are still pending.
|
|
63
|
+
|
|
64
|
+
The [output contract](specs/behavior/protocol.md) describes the complete flow and delivery rules.
|
|
65
|
+
|
|
66
|
+
## Error handling
|
|
67
|
+
|
|
68
|
+
The library distinguishes **internal errors** from **environment errors**.
|
|
69
|
+
|
|
70
|
+
Internal errors indicate programming defects or violated assumptions in the tool itself.
|
|
71
|
+
They are raised as exceptions, with `InternalError` as the shared base for library-defined internal failures.
|
|
72
|
+
Unexpected exceptions propagate through command handling without flushing pending results.
|
|
73
|
+
|
|
74
|
+
Environment errors describe expected failures in an operation's documented contract, such as invalid project configuration or an unreadable input file.
|
|
75
|
+
They are structured `EnvironmentError` values: a code identifies the failure kind, a message explains it, and context supplies details such as affected paths.
|
|
76
|
+
Environment errors can also include suggested corrections in a `ways_to_fix` list.
|
|
77
|
+
The rendered diagnostic includes these suggestions, so an agent can select and apply a fix automatically, then retry the command.
|
|
78
|
+
|
|
79
|
+
Operations return these values through `Result[T]`: `Ok(value)` for success or `Err(errors)` for a list of environment errors.
|
|
80
|
+
|
|
81
|
+
At the CLI boundary, `command_context` handles unsuccessful `.unwrap()` calls by reporting those environment errors as diagnostic logical cells in the selected protocol.
|
|
82
|
+
It discards pending ordinary results, delivers collected warnings after the diagnostics, and exits with the highest status declared by the errors.
|
|
83
|
+
|
|
84
|
+
Warnings describe non-fatal problems and do not cause command failure.
|
|
85
|
+
See the [error architecture](specs/architecture/errors.md) and [command error-handling contract](specs/behavior/cli.md#command-error-handling) for details.
|
|
86
|
+
|
|
87
|
+
## Scoped resources
|
|
88
|
+
|
|
89
|
+
`ContextValue[T]` makes a typed resource available to code running within a chosen execution scope.
|
|
90
|
+
Each resource has its own independent binding.
|
|
91
|
+
Code reads the active value through `.get()`, such as `runtime_config.get()` or `output.get()`; domain code can access these bindings without depending on the CLI layer.
|
|
92
|
+
Values can still be passed explicitly as function arguments.
|
|
93
|
+
|
|
94
|
+
Shared configuration distinguishes three lifetimes:
|
|
95
|
+
|
|
96
|
+
- **Application identity — `StaticConfig`.** Initialized before CLI parsing, it contains the tool label and optional default configuration filename.
|
|
97
|
+
- **Command invocation — `RuntimeConfig`.** Contains the effective output protocol.
|
|
98
|
+
The shared command context establishes it together with an independently bound output emitter.
|
|
99
|
+
- **Selected project — `ProjectConfig`.** Contains the selected configuration path and the project directory derived from its parent.
|
|
100
|
+
Shared project preparation establishes this binding after successful configuration validation.
|
|
101
|
+
Commands that do not use a project need no project binding.
|
|
102
|
+
|
|
103
|
+
The application's loaded settings remain separate from `ProjectConfig`, following the application's schema and defaults.
|
|
104
|
+
|
|
105
|
+
Applications can define bindings for their own resources, such as caches or execution state, and choose the scope each resource needs.
|
|
106
|
+
For example, an operation can make its execution state available only while that operation runs.
|
|
107
|
+
Resources are prepared only where needed; these scopes do not have to follow a fixed hierarchy.
|
|
108
|
+
|
|
109
|
+
Use a binding's `scope(value)` context manager to temporarily supply or override its value; leaving the scope restores the previous binding.
|
|
110
|
+
Accessing an unset binding raises an internal error indicating missing setup.
|
|
111
|
+
Binding an object does not construct, copy, or close it; the code managing that resource owns its setup and cleanup.
|
|
112
|
+
|
|
113
|
+
See the [context architecture](specs/architecture/modules_layout.md#core-and-context) and [project scopes](specs/behavior/config.md#project-scopes) for details.
|
|
114
|
+
|
|
115
|
+
## Reusable configuration elements
|
|
116
|
+
|
|
117
|
+
Tools compose their own configuration schemas from shared fields and configuration blocks alongside application-specific settings.
|
|
118
|
+
Each shared element is independently reusable:
|
|
119
|
+
|
|
120
|
+
- **Configuration version — `ConfigVersion`.** Validates integer versions and provides clear diagnostics for invalid or unsupported values.
|
|
121
|
+
Each tool chooses its supported versions and defaults.
|
|
122
|
+
- **Session directory — `SessionDir`.** Validates a project-relative path, rejecting absolute paths and parent-directory references.
|
|
123
|
+
The tool owns its default location and session storage.
|
|
124
|
+
- **Journal configuration — `JournalConfig`.** Configures an optional command for forwarding journal records externally.
|
|
125
|
+
External forwarding is disabled by default.
|
|
126
|
+
|
|
127
|
+
For example, a tool can combine these elements with its own greeting setting:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from pathlib import Path
|
|
131
|
+
from typing import Literal
|
|
132
|
+
|
|
133
|
+
import pydantic
|
|
134
|
+
|
|
135
|
+
from llm_tool_cli.config.fields import ConfigVersion, SessionDir
|
|
136
|
+
from llm_tool_cli.core.entities import BaseEntity
|
|
137
|
+
from llm_tool_cli.output.journal import JournalConfig
|
|
138
|
+
from llm_tool_cli.paths import RelativeProjectPath
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class Config(BaseEntity):
|
|
142
|
+
version: ConfigVersion[Literal[1]] = 1
|
|
143
|
+
session_dir: SessionDir = RelativeProjectPath(Path(".session"))
|
|
144
|
+
journal: JournalConfig = pydantic.Field(default_factory=JournalConfig)
|
|
145
|
+
greeting: str = "Hello"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The version constraint and defaults above belong to this example tool.
|
|
149
|
+
See the [configuration contract](specs/behavior/config.md) for shared loading and validation behavior.
|
|
150
|
+
|
|
151
|
+
## Quick start
|
|
152
|
+
|
|
153
|
+
This example creates a greeting in domain code and submits it from a CLI command.
|
|
154
|
+
|
|
155
|
+
With the library available in your Python environment, save this as `example.py`:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
import typer
|
|
159
|
+
|
|
160
|
+
from llm_tool_cli.cli.application import create_app
|
|
161
|
+
from llm_tool_cli.cli.context import command_context
|
|
162
|
+
from llm_tool_cli.core.settings import ToolLabel, initialize
|
|
163
|
+
from llm_tool_cli.output import output
|
|
164
|
+
from llm_tool_cli.protocol.logic_cells import ContentCell
|
|
165
|
+
|
|
166
|
+
initialize(label=ToolLabel("DEMO"))
|
|
167
|
+
app = create_app(help="A greeting tool built with llm-tool-cli.")
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def greeting(name: str) -> ContentCell:
|
|
171
|
+
return ContentCell(
|
|
172
|
+
kind="greeting",
|
|
173
|
+
media_type="text/plain",
|
|
174
|
+
content=f"Hello, {name}!",
|
|
175
|
+
meta={"type": "greeting", "name": name},
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@app.command()
|
|
180
|
+
def greet(context: typer.Context, name: str) -> None:
|
|
181
|
+
with command_context(context):
|
|
182
|
+
output.get().emit([greeting(name)]).unwrap()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
if __name__ == "__main__":
|
|
186
|
+
app()
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The command context selects the protocol, establishes the active emitter, and delivers buffered results on successful completion.
|
|
190
|
+
No project configuration or journal setup is needed for this example.
|
|
191
|
+
|
|
192
|
+
Run the greeting command with each of the three protocols:
|
|
193
|
+
|
|
194
|
+
### Human
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
python example.py -p human greet Ada
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
```text
|
|
201
|
+
name: Ada
|
|
202
|
+
|
|
203
|
+
Hello, Ada!
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### LLM
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
python example.py -p llm greet Ada
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
```text
|
|
214
|
+
--DEMO-CELL <id> BEGIN--
|
|
215
|
+
kind=greeting
|
|
216
|
+
media_type=text/plain
|
|
217
|
+
name=Ada
|
|
218
|
+
type=greeting
|
|
219
|
+
|
|
220
|
+
Hello, Ada!
|
|
221
|
+
--DEMO-CELL <id> END--
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Automation
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
python example.py -p automation greet Ada
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
```jsonl
|
|
231
|
+
{"content":"Hello, Ada!","id":"<id>","name":"Ada","type":"greeting"}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`<id>` represents a generated cell identifier.
|
|
235
|
+
|
|
236
|
+
## Integration
|
|
237
|
+
|
|
238
|
+
Use these extension points to adapt the library to your tool:
|
|
239
|
+
|
|
240
|
+
- **Custom logical cells** — Extend `LogicCell` to give protocols different presentations, or use `ContentCell` when they can share a payload.
|
|
241
|
+
[Depmesh](https://github.com/Tiendil/depmesh) uses custom logical cells to present grouped dependency lists as text and individual dependency records for automation.
|
|
242
|
+
- **Custom output cells** — Reuse the standard formatters or specialize them, such as by subclassing `HumanOutputCell` and selecting it from a logical cell's human projection.
|
|
243
|
+
Formatting stays separate from stream handling.
|
|
244
|
+
- **Application-owned configuration** — Supply your own schema and defaults, then use shared project scopes to locate and validate configuration and expose the active project.
|
|
245
|
+
See the [configuration contract](specs/behavior/config.md).
|
|
246
|
+
- **Scoped resources** — Add bindings for application-owned resources using the [scoping mechanism](#scoped-resources).
|
|
247
|
+
- **Journal delivery** — Use `output.get().journal("Starting work").unwrap()` to report explicit progress.
|
|
248
|
+
A journal-cell factory can add application context, and a journal processor can deliver records externally; the default processor performs no external delivery.
|
|
249
|
+
[Donna](https://github.com/Tiendil/donna) supplies a factory that adds workflow execution context to journal cells.
|
|
250
|
+
|
|
251
|
+
See the [specification index](specs/intro.md) for the complete contracts, including [CLI support](specs/behavior/cli.md) and [project paths](specs/behavior/paths.md).
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Shared command-line options and policies."""
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
|
|
3
|
+
from llm_tool_cli.cli.options import ConfigOption, ProtocolOption
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def create_app(*, help: str) -> typer.Typer:
|
|
7
|
+
app = typer.Typer(
|
|
8
|
+
help=help,
|
|
9
|
+
add_completion=True,
|
|
10
|
+
context_settings={"help_option_names": ["-h", "--help"]},
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
@app.callback()
|
|
14
|
+
def initialize(
|
|
15
|
+
protocol: ProtocolOption = None,
|
|
16
|
+
config_path: ConfigOption = None,
|
|
17
|
+
) -> None:
|
|
18
|
+
"""Declare global options for command-specific runtime setup."""
|
|
19
|
+
# Typer uses this callback's signature to register --protocol/-p and
|
|
20
|
+
# --config on the root command group. Before calling it, the framework
|
|
21
|
+
# parses these options and stores them in the root context's params;
|
|
22
|
+
# this function does not need to store or forward the values itself.
|
|
23
|
+
# CommandContext reads them through context.find_root().params when the
|
|
24
|
+
# selected command starts. It can then choose the command's default
|
|
25
|
+
# protocol and bind RuntimeConfig, while leaving configuration path
|
|
26
|
+
# resolution and loading to commands that actually need a workspace.
|
|
27
|
+
# The callback therefore intentionally performs no initialization.
|
|
28
|
+
# Removing it would remove the global option declarations and could
|
|
29
|
+
# make a single-command application lose its command-group interface.
|
|
30
|
+
|
|
31
|
+
return app
|
|
File without changes
|