@@ -6,13 +6,16 @@ Command-line interface plugin for UTCP, enabling integration with command-line t
66
77## Features
88
9- - ** Command Execution** : Run any command-line tool as a UTCP tool
9+ - ** Multi-Command Execution** : Execute multiple commands sequentially in a single subprocess
10+ - ** State Preservation** : Directory changes and environment persist between commands
11+ - ** Cross-Platform Script Generation** : PowerShell on Windows, Bash on Unix/Linux/macOS
12+ - ** Flexible Output Control** : Choose which command outputs to include in final result
13+ - ** Argument Substitution** : ` UTCP_ARG_argname_UTCP_END ` placeholder system
14+ - ** Output Referencing** : Access previous command outputs with ` $CMD_0_OUTPUT ` , ` $CMD_1_OUTPUT `
1015- ** Environment Variables** : Secure credential and configuration passing
1116- ** Working Directory Control** : Execute commands in specific directories
12- - ** Input/Output Handling** : Support for stdin, stdout, stderr processing
13- - ** Cross-Platform** : Works on Windows, macOS, and Linux
1417- ** Timeout Management** : Configurable execution timeouts
15- - ** Argument Validation ** : Optional input sanitization
18+ - ** Error Handling ** : Comprehensive subprocess error management
1619
1720## Installation
1821
@@ -25,109 +28,184 @@ pip install utcp-cli
2528``` python
2629from utcp.utcp_client import UtcpClient
2730
28- # Basic CLI tool
31+ # Multi-step CLI tool
2932client = await UtcpClient.create(config = {
3033 " manual_call_templates" : [{
31- " name" : " file_tools " ,
34+ " name" : " file_analysis " ,
3235 " call_template_type" : " cli" ,
33- " command_name" : " ls -la ${path} "
36+ " commands" : [
37+ {
38+ " command" : " cd UTCP_ARG_target_dir_UTCP_END" ,
39+ " append_to_final_output" : false
40+ },
41+ {
42+ " command" : " find . -type f -name '*.py' | wc -l"
43+ }
44+ ]
3445 }]
3546})
3647
37- result = await client.call_tool(" file_tools.list " , {" path " : " /home " })
48+ result = await client.call_tool(" file_analysis.count_python_files " , {" target_dir " : " /project " })
3849```
3950
4051## Configuration Examples
4152
42- ### Basic Command
53+ ### Basic Multi- Command Operation
4354``` json
4455{
45- "name" : " file_ops " ,
56+ "name" : " file_analysis " ,
4657 "call_template_type" : " cli" ,
47- "command_name" : " ls -la ${path}" ,
58+ "commands" : [
59+ {
60+ "command" : " cd UTCP_ARG_target_dir_UTCP_END" ,
61+ "append_to_final_output" : false
62+ },
63+ {
64+ "command" : " ls -la"
65+ }
66+ ],
4867 "working_dir" : " /tmp"
4968}
5069```
5170
52- ### With Environment Variables
71+ ### With Environment Variables and Output Control
5372``` json
5473{
55- "name" : " python_script " ,
74+ "name" : " python_pipeline " ,
5675 "call_template_type" : " cli" ,
57- "command_name" : " python script.py ${input}" ,
76+ "commands" : [
77+ {
78+ "command" : " python setup.py install" ,
79+ "append_to_final_output" : false
80+ },
81+ {
82+ "command" : " python script.py --input UTCP_ARG_input_file_UTCP_END --result \" $CMD_0_OUTPUT\" " ,
83+ "append_to_final_output" : true
84+ }
85+ ],
5886 "env_vars" : {
5987 "PYTHONPATH" : " /custom/path" ,
6088 "API_KEY" : " ${API_KEY}"
6189 }
6290}
6391```
6492
65- ### Processing JSON with jq
93+ ### Cross-Platform Git Operations
6694``` json
6795{
68- "name" : " json_processor " ,
96+ "name" : " git_analysis " ,
6997 "call_template_type" : " cli" ,
70- "command_name" : " jq '.data'" ,
71- "stdin" : " ${json_input}" ,
72- "timeout" : 10
98+ "commands" : [
99+ {
100+ "command" : " git clone UTCP_ARG_repo_url_UTCP_END temp_repo" ,
101+ "append_to_final_output" : false
102+ },
103+ {
104+ "command" : " cd temp_repo" ,
105+ "append_to_final_output" : false
106+ },
107+ {
108+ "command" : " git log --oneline -10" ,
109+ "append_to_final_output" : true
110+ },
111+ {
112+ "command" : " echo \" Repository has $(find . -name '*.py' | wc -l) Python files\" " ,
113+ "append_to_final_output" : true
114+ }
115+ ],
116+ "env_vars" : {
117+ "GIT_AUTHOR_NAME" : " UTCP Bot" ,
118+ "GIT_AUTHOR_EMAIL" : " bot@utcp.dev"
119+ }
73120}
74121```
75122
76- ### Git Operations
123+ ### Referencing Previous Command Output
77124``` json
78125{
79- "name" : " git_tools " ,
126+ "name" : " conditional_processor " ,
80127 "call_template_type" : " cli" ,
81- "command_name" : " git ${operation} ${args}" ,
82- "working_dir" : " ${repo_path}" ,
83- "env_vars" : {
84- "GIT_AUTHOR_NAME" : " ${author_name}" ,
85- "GIT_AUTHOR_EMAIL" : " ${author_email}"
86- }
128+ "commands" : [
129+ {
130+ "command" : " git status --porcelain" ,
131+ "append_to_final_output" : false
132+ },
133+ {
134+ "command" : " echo \" Changes detected: $CMD_0_OUTPUT\" " ,
135+ "append_to_final_output" : true
136+ }
137+ ]
87138}
88139```
89140
90- ## Security Considerations
141+ ## Cross-Platform Considerations
91142
92- - Commands run in isolated subprocesses
93- - Environment variables provide secure credential passing
94- - Working directory restrictions limit file system access
95- - Input validation prevents command injection
143+ ### Command Syntax
144+ Commands should use appropriate syntax for the target platform:
96145
146+ ** Windows (PowerShell):**
97147``` json
98148{
99- "name" : " safe_grep" ,
100- "call_template_type" : " cli" ,
101- "command_name" : " grep ${pattern} ${file}" ,
102- "working_dir" : " /safe/directory" ,
103- "allowed_args" : {
104- "pattern" : " ^[a-zA-Z0-9_-]+$" ,
105- "file" : " ^[a-zA-Z0-9_./-]+\\ .txt$"
106- }
149+ "commands" : [
150+ {"command" : " Get-ChildItem UTCP_ARG_path_UTCP_END" },
151+ {"command" : " Set-Location UTCP_ARG_dir_UTCP_END" }
152+ ]
107153}
108154```
109155
156+ ** Unix/Linux/macOS (Bash):**
157+ ``` json
158+ {
159+ "commands" : [
160+ {"command" : " ls -la UTCP_ARG_path_UTCP_END" },
161+ {"command" : " cd UTCP_ARG_dir_UTCP_END" }
162+ ]
163+ }
164+ ```
165+
166+ ### Universal Commands
167+ Some commands work across platforms:
168+ ``` json
169+ {
170+ "commands" : [
171+ {"command" : " git status" },
172+ {"command" : " python --version" },
173+ {"command" : " node -v" }
174+ ]
175+ }
176+ ```
177+
178+ ## Security Considerations
179+
180+ - Commands execute in isolated subprocesses with controlled environment
181+ - Environment variables provide secure credential passing
182+ - Previous command outputs should be used carefully to avoid injection
183+ - Commands should use platform-appropriate syntax
184+
110185## Error Handling
111186
112187``` python
113188from utcp.exceptions import ToolCallError
114- import subprocess
115189
116190try :
117- result = await client.call_tool(" cli_tool.command" , {" arg" : " value" })
191+ result = await client.call_tool(" cli_tool.multi_command" , {
192+ " repo_url" : " https://github.com/example/repo.git" ,
193+ " target_dir" : " analysis_temp"
194+ })
118195except ToolCallError as e:
119- if isinstance (e.__cause__, subprocess.CalledProcessError):
120- print (f " Command failed with exit code { e.__cause__.returncode} " )
121- print (f " stderr: { e.__cause__.stderr} " )
196+ print (f " CLI tool execution failed: { e} " )
197+ # Script execution failed - check individual command outputs
122198```
123199
124200## Common Use Cases
125201
126- - ** File Operations** : ls, find, grep, awk, sed
127- - ** Data Processing** : jq, sort, uniq, cut
128- - ** System Monitoring** : ps, top, df, netstat
129- - ** Development Tools** : git, npm, pip, docker
130- - ** Custom Scripts** : Python, bash, PowerShell scripts
202+ - ** Multi-step Builds** : setup → compile → test → package
203+ - ** Git Workflows** : clone → analyze → commit → push
204+ - ** Data Pipelines** : fetch → transform → validate → output
205+ - ** File Operations** : navigate → search → process → report
206+ - ** Development Tools** : install dependencies → run tests → generate docs
207+ - ** System Administration** : check status → backup → cleanup → verify
208+ - ** Custom Workflows** : Any sequence of command-line operations
131209
132210## Testing CLI Tools
133211
@@ -136,17 +214,25 @@ import pytest
136214from utcp.utcp_client import UtcpClient
137215
138216@pytest.mark.asyncio
139- async def test_cli_tool ():
217+ async def test_multi_command_cli_tool ():
140218 client = await UtcpClient.create(config = {
141219 " manual_call_templates" : [{
142220 " name" : " test_cli" ,
143221 " call_template_type" : " cli" ,
144- " command_name" : " echo ${message} "
222+ " commands" : [
223+ {
224+ " command" : " echo UTCP_ARG_message_UTCP_END" ,
225+ " append_to_final_output" : false
226+ },
227+ {
228+ " command" : " echo \" Previous: $CMD_0_OUTPUT\" "
229+ }
230+ ]
145231 }]
146232 })
147233
148- result = await client.call_tool(" test_cli.echo " , {" message" : " hello" })
149- assert " hello" in result[ " stdout " ]
234+ result = await client.call_tool(" test_cli.echo_chain " , {" message" : " hello" })
235+ assert " Previous: hello" in result
150236```
151237
152238## Related Documentation
0 commit comments