codicent-cli 0.7.1__tar.gz → 0.8.1__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.
- {codicent_cli-0.7.1 → codicent_cli-0.8.1}/PKG-INFO +124 -1
- {codicent_cli-0.7.1 → codicent_cli-0.8.1}/README.md +120 -0
- {codicent_cli-0.7.1 → codicent_cli-0.8.1}/app.py +223 -18
- {codicent_cli-0.7.1 → codicent_cli-0.8.1}/auth.py +64 -42
- {codicent_cli-0.7.1 → codicent_cli-0.8.1}/codicent_cli.egg-info/PKG-INFO +124 -1
- {codicent_cli-0.7.1 → codicent_cli-0.8.1}/codicent_cli.egg-info/SOURCES.txt +2 -0
- {codicent_cli-0.7.1 → codicent_cli-0.8.1}/codicent_cli.egg-info/requires.txt +3 -0
- {codicent_cli-0.7.1 → codicent_cli-0.8.1}/codicent_cli.egg-info/top_level.txt +1 -0
- {codicent_cli-0.7.1 → codicent_cli-0.8.1}/setup.py +6 -3
- codicent_cli-0.8.1/templates/default.json +47 -0
- codicent_cli-0.8.1/templates/document-processing.json +57 -0
- codicent_cli-0.8.1/templates/koi-studio.json +62 -0
- codicent_cli-0.8.1/wrap.py +877 -0
- codicent_cli-0.7.1/templates/default.json +0 -22
- codicent_cli-0.7.1/templates/koi-studio.json +0 -34
- {codicent_cli-0.7.1 → codicent_cli-0.8.1}/LICENSE +0 -0
- {codicent_cli-0.7.1 → codicent_cli-0.8.1}/codicent_cli.egg-info/dependency_links.txt +0 -0
- {codicent_cli-0.7.1 → codicent_cli-0.8.1}/codicent_cli.egg-info/entry_points.txt +0 -0
- {codicent_cli-0.7.1 → codicent_cli-0.8.1}/setup.cfg +0 -0
- {codicent_cli-0.7.1 → codicent_cli-0.8.1}/templates/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codicent-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.1
|
|
4
4
|
Summary: Command-line interface for the Codicent API
|
|
5
5
|
Home-page: https://github.com/izaxon/codicent-cli
|
|
6
6
|
Author: Johan Isaksson
|
|
@@ -27,6 +27,9 @@ Requires-Dist: rich
|
|
|
27
27
|
Requires-Dist: codicent-py
|
|
28
28
|
Requires-Dist: prompt_toolkit
|
|
29
29
|
Requires-Dist: requests
|
|
30
|
+
Requires-Dist: pyyaml
|
|
31
|
+
Requires-Dist: packaging
|
|
32
|
+
Requires-Dist: pyte
|
|
30
33
|
Dynamic: author
|
|
31
34
|
Dynamic: author-email
|
|
32
35
|
Dynamic: classifier
|
|
@@ -104,6 +107,28 @@ pip install git+https://github.com/izaxon/codicent-cli.git@v0.4.8
|
|
|
104
107
|
export CODICENT_TOKEN="YOUR_API_TOKEN"
|
|
105
108
|
```
|
|
106
109
|
|
|
110
|
+
Or use the built-in device-flow authentication:
|
|
111
|
+
```bash
|
|
112
|
+
codicent auth # stores token globally in ~/.codicent_token
|
|
113
|
+
codicent auth --local # stores token in .codicent_token in the current directory
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Per-folder Projects
|
|
117
|
+
|
|
118
|
+
You can have a different Codicent project per directory by storing a local token:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
cd ~/work/projectA
|
|
122
|
+
codicent auth --local # authenticates and saves token to ~/work/projectA/.codicent_token
|
|
123
|
+
|
|
124
|
+
cd ~/work/projectB
|
|
125
|
+
codicent auth --local # authenticates and saves token to ~/work/projectB/.codicent_token
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The CLI always checks the current directory first, then falls back to the global `~/.codicent_token`, and finally to the `CODICENT_TOKEN` environment variable.
|
|
129
|
+
|
|
130
|
+
> **Tip**: Add `.codicent_token` to your `.gitignore` to avoid accidentally committing tokens.
|
|
131
|
+
|
|
107
132
|
### Command Options
|
|
108
133
|
|
|
109
134
|
```
|
|
@@ -211,6 +236,102 @@ The CLI provides helpful error messages for common issues:
|
|
|
211
236
|
- **Input validation**: Prevents empty or overly long inputs
|
|
212
237
|
- **Keyboard interrupts**: Clean exit handling
|
|
213
238
|
|
|
239
|
+
## Project Init
|
|
240
|
+
|
|
241
|
+
Use `codicent init` to instantly set up a new project from a template. Each template posts a ready-to-use set of AI instructions, configuration, automation rules, and sample data.
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
codicent init --list # list templates
|
|
245
|
+
codicent init # use default template
|
|
246
|
+
codicent init --template koi-studio # CRM pipeline
|
|
247
|
+
codicent init --template document-processing # document intake
|
|
248
|
+
codicent init --template koi-studio --dry-run # preview without posting
|
|
249
|
+
codicent init --force # re-init even if already set up
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Available templates
|
|
253
|
+
|
|
254
|
+
| Template | Description |
|
|
255
|
+
|---|---|
|
|
256
|
+
| `default` | General-purpose: task workflow, auto-summarize notes via `#transform` |
|
|
257
|
+
| `koi-studio` | CRM: automatic lead extraction from raw text, follow-up email drafting |
|
|
258
|
+
| `document-processing` | Document intake: extract text, structure key fields with AI, review workflow |
|
|
259
|
+
|
|
260
|
+
### How it works
|
|
261
|
+
|
|
262
|
+
Templates are JSON files in `templates/`. Each defines:
|
|
263
|
+
- **`variables`** — prompted at init time (e.g. company name). Values with defaults are applied silently; values without defaults require manual entry.
|
|
264
|
+
- **`messages`** — posted in order. Each has an optional `label` shown in progress output.
|
|
265
|
+
|
|
266
|
+
Key message types posted by templates:
|
|
267
|
+
- `#instructions` — sets the AI assistant's persona and focus
|
|
268
|
+
- `#appconfig #data` — configures AI model, temperature, and token limit
|
|
269
|
+
- `#process #data` — defines pipeline stages (shown as kanban columns in the UI)
|
|
270
|
+
- `#transform #data` — automation rule: when a message with tag X arrives, run AI and produce a message with tag Y
|
|
271
|
+
- sample `#data` messages — so lists aren't empty on first login
|
|
272
|
+
|
|
273
|
+
### `--dry-run` mode
|
|
274
|
+
|
|
275
|
+
Prints every message that *would* be posted, with its label and a content preview, without any API calls or auth required. Useful for reviewing a template before committing.
|
|
276
|
+
|
|
277
|
+
## Wrap — AI CLI Remote Chat Bridge
|
|
278
|
+
|
|
279
|
+
Use `codicent wrap` to bridge a local AI CLI tool (`claude`, `codex`, `opencode`, etc.) to your codicent project chat. Remote users and the Codicent AI can interact with the locally running tool in real time through the existing codicent web UI.
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
codicent wrap claude # wrap Claude Code
|
|
283
|
+
codicent wrap opencode # wrap OpenCode
|
|
284
|
+
codicent wrap --label "Refactor sprint" codex # named session
|
|
285
|
+
codicent wrap --poll-interval 5 claude # slower polling
|
|
286
|
+
codicent wrap --binary /opt/ai/claude claude # explicit binary path
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### How it works
|
|
290
|
+
|
|
291
|
+
1. Starts the specified tool as a local subprocess with full stdin/stdout control.
|
|
292
|
+
2. Opens a **session** tagged `#wrap #wrap_<id>` in your codicent project.
|
|
293
|
+
3. **Output** from the tool is captured and posted to codicent tagged `#wrap #wrap_<id> #output`.
|
|
294
|
+
4. **Input** is received by polling for messages tagged `#wrap #wrap_<id> #input` — any message posted remotely with those tags is forwarded to the tool's stdin.
|
|
295
|
+
|
|
296
|
+
The session ID is printed on startup. To send text to the running tool from the codicent web UI, post a message with `#wrap_<id> #input` in the content.
|
|
297
|
+
|
|
298
|
+
### Startup output
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
🔗 Wrap session starting…
|
|
302
|
+
Session ID: wrap_a3f7c1b2
|
|
303
|
+
Project: myproject
|
|
304
|
+
Tool: claude (/usr/local/bin/claude)
|
|
305
|
+
Remote tag: #wrap_a3f7c1b2
|
|
306
|
+
Post messages tagged #wrap_a3f7c1b2 #input to send text to the tool.
|
|
307
|
+
Press Ctrl+C to stop the session.
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Configuration (`~/.codicent/wrap-config.yaml`)
|
|
311
|
+
|
|
312
|
+
```yaml
|
|
313
|
+
poll_interval: 2 # seconds between input polls
|
|
314
|
+
output_batch_ms: 500 # ms to batch output before posting
|
|
315
|
+
|
|
316
|
+
tools:
|
|
317
|
+
claude:
|
|
318
|
+
binary: claude # binary name or full path
|
|
319
|
+
codex:
|
|
320
|
+
binary: codex
|
|
321
|
+
opencode:
|
|
322
|
+
binary: opencode
|
|
323
|
+
|
|
324
|
+
auto_responses:
|
|
325
|
+
- pattern: "Do you want to continue"
|
|
326
|
+
reply: "y"
|
|
327
|
+
- pattern: "\\(y/n\\)"
|
|
328
|
+
reply: "y"
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Auto-response rules are matched against subprocess output using regex. When matched, the configured reply is written to stdin automatically (and posted to codicent for visibility).
|
|
332
|
+
|
|
333
|
+
If no config file exists, defaults are used: tools are resolved by name on `$PATH`, poll interval is 2 seconds.
|
|
334
|
+
|
|
214
335
|
## Development
|
|
215
336
|
|
|
216
337
|
### Running Tests
|
|
@@ -222,6 +343,7 @@ python -m pytest test_app.py -v
|
|
|
222
343
|
### Project Structure
|
|
223
344
|
|
|
224
345
|
- `app.py` - Main application logic (single-file architecture)
|
|
346
|
+
- `wrap.py` - Wrap command: AI CLI remote chat bridge
|
|
225
347
|
- `test_app.py` - Comprehensive test suite
|
|
226
348
|
- `setup.py` - Package configuration
|
|
227
349
|
- `requirements.txt` - Dependencies (now uses PyPI packages)
|
|
@@ -230,6 +352,7 @@ python -m pytest test_app.py -v
|
|
|
230
352
|
|
|
231
353
|
- **codicent-py**: Core API client for Codicent services (now available on PyPI)
|
|
232
354
|
- **rich**: Terminal formatting, markdown rendering, and animations
|
|
355
|
+
- **pyyaml**: YAML config file parsing for the wrap command
|
|
233
356
|
|
|
234
357
|
## Troubleshooting
|
|
235
358
|
|
|
@@ -62,6 +62,28 @@ pip install git+https://github.com/izaxon/codicent-cli.git@v0.4.8
|
|
|
62
62
|
export CODICENT_TOKEN="YOUR_API_TOKEN"
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
+
Or use the built-in device-flow authentication:
|
|
66
|
+
```bash
|
|
67
|
+
codicent auth # stores token globally in ~/.codicent_token
|
|
68
|
+
codicent auth --local # stores token in .codicent_token in the current directory
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Per-folder Projects
|
|
72
|
+
|
|
73
|
+
You can have a different Codicent project per directory by storing a local token:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
cd ~/work/projectA
|
|
77
|
+
codicent auth --local # authenticates and saves token to ~/work/projectA/.codicent_token
|
|
78
|
+
|
|
79
|
+
cd ~/work/projectB
|
|
80
|
+
codicent auth --local # authenticates and saves token to ~/work/projectB/.codicent_token
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The CLI always checks the current directory first, then falls back to the global `~/.codicent_token`, and finally to the `CODICENT_TOKEN` environment variable.
|
|
84
|
+
|
|
85
|
+
> **Tip**: Add `.codicent_token` to your `.gitignore` to avoid accidentally committing tokens.
|
|
86
|
+
|
|
65
87
|
### Command Options
|
|
66
88
|
|
|
67
89
|
```
|
|
@@ -169,6 +191,102 @@ The CLI provides helpful error messages for common issues:
|
|
|
169
191
|
- **Input validation**: Prevents empty or overly long inputs
|
|
170
192
|
- **Keyboard interrupts**: Clean exit handling
|
|
171
193
|
|
|
194
|
+
## Project Init
|
|
195
|
+
|
|
196
|
+
Use `codicent init` to instantly set up a new project from a template. Each template posts a ready-to-use set of AI instructions, configuration, automation rules, and sample data.
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
codicent init --list # list templates
|
|
200
|
+
codicent init # use default template
|
|
201
|
+
codicent init --template koi-studio # CRM pipeline
|
|
202
|
+
codicent init --template document-processing # document intake
|
|
203
|
+
codicent init --template koi-studio --dry-run # preview without posting
|
|
204
|
+
codicent init --force # re-init even if already set up
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Available templates
|
|
208
|
+
|
|
209
|
+
| Template | Description |
|
|
210
|
+
|---|---|
|
|
211
|
+
| `default` | General-purpose: task workflow, auto-summarize notes via `#transform` |
|
|
212
|
+
| `koi-studio` | CRM: automatic lead extraction from raw text, follow-up email drafting |
|
|
213
|
+
| `document-processing` | Document intake: extract text, structure key fields with AI, review workflow |
|
|
214
|
+
|
|
215
|
+
### How it works
|
|
216
|
+
|
|
217
|
+
Templates are JSON files in `templates/`. Each defines:
|
|
218
|
+
- **`variables`** — prompted at init time (e.g. company name). Values with defaults are applied silently; values without defaults require manual entry.
|
|
219
|
+
- **`messages`** — posted in order. Each has an optional `label` shown in progress output.
|
|
220
|
+
|
|
221
|
+
Key message types posted by templates:
|
|
222
|
+
- `#instructions` — sets the AI assistant's persona and focus
|
|
223
|
+
- `#appconfig #data` — configures AI model, temperature, and token limit
|
|
224
|
+
- `#process #data` — defines pipeline stages (shown as kanban columns in the UI)
|
|
225
|
+
- `#transform #data` — automation rule: when a message with tag X arrives, run AI and produce a message with tag Y
|
|
226
|
+
- sample `#data` messages — so lists aren't empty on first login
|
|
227
|
+
|
|
228
|
+
### `--dry-run` mode
|
|
229
|
+
|
|
230
|
+
Prints every message that *would* be posted, with its label and a content preview, without any API calls or auth required. Useful for reviewing a template before committing.
|
|
231
|
+
|
|
232
|
+
## Wrap — AI CLI Remote Chat Bridge
|
|
233
|
+
|
|
234
|
+
Use `codicent wrap` to bridge a local AI CLI tool (`claude`, `codex`, `opencode`, etc.) to your codicent project chat. Remote users and the Codicent AI can interact with the locally running tool in real time through the existing codicent web UI.
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
codicent wrap claude # wrap Claude Code
|
|
238
|
+
codicent wrap opencode # wrap OpenCode
|
|
239
|
+
codicent wrap --label "Refactor sprint" codex # named session
|
|
240
|
+
codicent wrap --poll-interval 5 claude # slower polling
|
|
241
|
+
codicent wrap --binary /opt/ai/claude claude # explicit binary path
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### How it works
|
|
245
|
+
|
|
246
|
+
1. Starts the specified tool as a local subprocess with full stdin/stdout control.
|
|
247
|
+
2. Opens a **session** tagged `#wrap #wrap_<id>` in your codicent project.
|
|
248
|
+
3. **Output** from the tool is captured and posted to codicent tagged `#wrap #wrap_<id> #output`.
|
|
249
|
+
4. **Input** is received by polling for messages tagged `#wrap #wrap_<id> #input` — any message posted remotely with those tags is forwarded to the tool's stdin.
|
|
250
|
+
|
|
251
|
+
The session ID is printed on startup. To send text to the running tool from the codicent web UI, post a message with `#wrap_<id> #input` in the content.
|
|
252
|
+
|
|
253
|
+
### Startup output
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
🔗 Wrap session starting…
|
|
257
|
+
Session ID: wrap_a3f7c1b2
|
|
258
|
+
Project: myproject
|
|
259
|
+
Tool: claude (/usr/local/bin/claude)
|
|
260
|
+
Remote tag: #wrap_a3f7c1b2
|
|
261
|
+
Post messages tagged #wrap_a3f7c1b2 #input to send text to the tool.
|
|
262
|
+
Press Ctrl+C to stop the session.
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Configuration (`~/.codicent/wrap-config.yaml`)
|
|
266
|
+
|
|
267
|
+
```yaml
|
|
268
|
+
poll_interval: 2 # seconds between input polls
|
|
269
|
+
output_batch_ms: 500 # ms to batch output before posting
|
|
270
|
+
|
|
271
|
+
tools:
|
|
272
|
+
claude:
|
|
273
|
+
binary: claude # binary name or full path
|
|
274
|
+
codex:
|
|
275
|
+
binary: codex
|
|
276
|
+
opencode:
|
|
277
|
+
binary: opencode
|
|
278
|
+
|
|
279
|
+
auto_responses:
|
|
280
|
+
- pattern: "Do you want to continue"
|
|
281
|
+
reply: "y"
|
|
282
|
+
- pattern: "\\(y/n\\)"
|
|
283
|
+
reply: "y"
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Auto-response rules are matched against subprocess output using regex. When matched, the configured reply is written to stdin automatically (and posted to codicent for visibility).
|
|
287
|
+
|
|
288
|
+
If no config file exists, defaults are used: tools are resolved by name on `$PATH`, poll interval is 2 seconds.
|
|
289
|
+
|
|
172
290
|
## Development
|
|
173
291
|
|
|
174
292
|
### Running Tests
|
|
@@ -180,6 +298,7 @@ python -m pytest test_app.py -v
|
|
|
180
298
|
### Project Structure
|
|
181
299
|
|
|
182
300
|
- `app.py` - Main application logic (single-file architecture)
|
|
301
|
+
- `wrap.py` - Wrap command: AI CLI remote chat bridge
|
|
183
302
|
- `test_app.py` - Comprehensive test suite
|
|
184
303
|
- `setup.py` - Package configuration
|
|
185
304
|
- `requirements.txt` - Dependencies (now uses PyPI packages)
|
|
@@ -188,6 +307,7 @@ python -m pytest test_app.py -v
|
|
|
188
307
|
|
|
189
308
|
- **codicent-py**: Core API client for Codicent services (now available on PyPI)
|
|
190
309
|
- **rich**: Terminal formatting, markdown rendering, and animations
|
|
310
|
+
- **pyyaml**: YAML config file parsing for the wrap command
|
|
191
311
|
|
|
192
312
|
## Troubleshooting
|
|
193
313
|
|
|
@@ -40,8 +40,10 @@ OPTIONS:
|
|
|
40
40
|
|
|
41
41
|
AUTHENTICATION:
|
|
42
42
|
auth Authenticate using device flow (project selected during web auth)
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
auth --local Authenticate and store token in the current directory (.codicent_token)
|
|
44
|
+
logout Clear stored global authentication
|
|
45
|
+
logout --local Clear only the local (.codicent_token) token in the current directory
|
|
46
|
+
status Check authentication status (shows whether local or global token is used)
|
|
45
47
|
|
|
46
48
|
FILE UPLOAD:
|
|
47
49
|
upload Upload files and post messages with tags
|
|
@@ -55,13 +57,54 @@ FILE UPLOAD:
|
|
|
55
57
|
PROJECT INIT:
|
|
56
58
|
init Initialize current project with a template
|
|
57
59
|
|
|
58
|
-
--list List available templates
|
|
60
|
+
--list List available templates with descriptions
|
|
59
61
|
--template NAME Use a specific template (default: 'default')
|
|
62
|
+
--dry-run Preview messages without posting (no auth needed)
|
|
63
|
+
--force Re-initialize even if project already has #instructions
|
|
64
|
+
|
|
65
|
+
Templates:
|
|
66
|
+
default General-purpose: task workflow + auto-summarize notes
|
|
67
|
+
koi-studio CRM: lead extraction + follow-up email drafting pipeline
|
|
68
|
+
document-processing Document intake: extract, structure, and review documents
|
|
60
69
|
|
|
61
70
|
Examples:
|
|
62
71
|
codicent init
|
|
63
72
|
codicent init --template koi-studio
|
|
73
|
+
codicent init --template document-processing --dry-run
|
|
64
74
|
codicent init --list
|
|
75
|
+
codicent init --force
|
|
76
|
+
|
|
77
|
+
WRAP (AI CLI Remote Bridge):
|
|
78
|
+
wrap Bridge a local AI CLI tool to a remote codicent chat session.
|
|
79
|
+
Remote users and AI can interact with the locally running tool.
|
|
80
|
+
|
|
81
|
+
<tool> Tool to wrap: claude, codex, opencode, or any CLI tool
|
|
82
|
+
[tool-args...] Arguments passed through to the wrapped tool
|
|
83
|
+
--label TEXT Session label shown in codicent UI (default: "<tool> session")
|
|
84
|
+
--binary PATH Explicit path to the tool binary
|
|
85
|
+
--poll-interval N Seconds between input polls (default: 2)
|
|
86
|
+
|
|
87
|
+
How it works:
|
|
88
|
+
1. Starts the tool as a local subprocess
|
|
89
|
+
2. Posts tool output to codicent tagged #wrap #wrap_<id> #output
|
|
90
|
+
3. Polls for messages tagged #wrap #wrap_<id> #input and writes them to stdin
|
|
91
|
+
4. Remote users tag messages with #wrap_<id> #input to send text to the tool
|
|
92
|
+
|
|
93
|
+
Config file (~/.codicent/wrap-config.yaml):
|
|
94
|
+
poll_interval: 2
|
|
95
|
+
output_batch_ms: 500
|
|
96
|
+
tools:
|
|
97
|
+
claude:
|
|
98
|
+
binary: claude
|
|
99
|
+
auto_responses:
|
|
100
|
+
- pattern: "Do you want to continue"
|
|
101
|
+
reply: "y"
|
|
102
|
+
|
|
103
|
+
Examples:
|
|
104
|
+
codicent wrap claude
|
|
105
|
+
codicent wrap --label "Refactor session" opencode
|
|
106
|
+
codicent wrap --poll-interval 5 codex
|
|
107
|
+
codicent wrap --binary /usr/local/bin/claude claude --model claude-opus-4-5
|
|
65
108
|
|
|
66
109
|
EXAMPLES:
|
|
67
110
|
codicent auth
|
|
@@ -70,8 +113,15 @@ EXAMPLES:
|
|
|
70
113
|
codicent "@mention Hello there"
|
|
71
114
|
codicent upload document.txt *.log
|
|
72
115
|
codicent init --template koi-studio
|
|
116
|
+
codicent wrap claude
|
|
117
|
+
codicent update
|
|
118
|
+
codicent update --install
|
|
73
119
|
echo "Help me debug this" | codicent
|
|
74
120
|
|
|
121
|
+
UPDATE:
|
|
122
|
+
update Check if a newer version is available on PyPI
|
|
123
|
+
--install Also upgrade immediately via pip
|
|
124
|
+
|
|
75
125
|
ENVIRONMENT:
|
|
76
126
|
CODICENT_TOKEN Your Codicent API token (fallback if no cached auth)
|
|
77
127
|
|
|
@@ -79,9 +129,11 @@ For more information, visit: https://github.com/izaxon/codicent-cli
|
|
|
79
129
|
"""
|
|
80
130
|
print(help_text.strip())
|
|
81
131
|
|
|
132
|
+
CURRENT_VERSION = "0.8.1"
|
|
133
|
+
|
|
82
134
|
def show_version():
|
|
83
135
|
"""Display version information."""
|
|
84
|
-
print("Codicent CLI
|
|
136
|
+
print(f"Codicent CLI v{CURRENT_VERSION}")
|
|
85
137
|
|
|
86
138
|
def validate_input(question):
|
|
87
139
|
"""Validate user input."""
|
|
@@ -115,6 +167,11 @@ def main():
|
|
|
115
167
|
logging.getLogger().setLevel(logging.ERROR)
|
|
116
168
|
sys.argv.remove("--quiet")
|
|
117
169
|
|
|
170
|
+
# Parse --local flag (store/remove token in current working directory)
|
|
171
|
+
use_local = "--local" in sys.argv
|
|
172
|
+
if use_local:
|
|
173
|
+
sys.argv.remove("--local")
|
|
174
|
+
|
|
118
175
|
# Initialize auth handler
|
|
119
176
|
auth = CodicentAuth()
|
|
120
177
|
|
|
@@ -123,8 +180,7 @@ def main():
|
|
|
123
180
|
command = sys.argv[1]
|
|
124
181
|
|
|
125
182
|
if command == "auth":
|
|
126
|
-
|
|
127
|
-
token = auth.get_token(force_reauth=True)
|
|
183
|
+
token = auth.get_token(force_reauth=True, local=use_local)
|
|
128
184
|
if token:
|
|
129
185
|
console.print("[green]✅ Authentication successful![/green]")
|
|
130
186
|
return 0
|
|
@@ -133,13 +189,13 @@ def main():
|
|
|
133
189
|
return 1
|
|
134
190
|
|
|
135
191
|
elif command == "logout":
|
|
136
|
-
auth.logout()
|
|
192
|
+
auth.logout(local=use_local)
|
|
137
193
|
return 0
|
|
138
194
|
|
|
139
195
|
elif command == "status":
|
|
140
|
-
|
|
141
|
-
if
|
|
142
|
-
console.print("[green]✅ Authenticated (
|
|
196
|
+
source = auth.get_token_source()
|
|
197
|
+
if source:
|
|
198
|
+
console.print(f"[green]✅ Authenticated[/green] [dim]({source})[/dim]")
|
|
143
199
|
return 0
|
|
144
200
|
else:
|
|
145
201
|
env_token = os.getenv("CODICENT_TOKEN")
|
|
@@ -152,6 +208,54 @@ def main():
|
|
|
152
208
|
console.print("[dim]Run 'codicent auth' to authenticate[/dim]")
|
|
153
209
|
return 1
|
|
154
210
|
|
|
211
|
+
# Handle update command
|
|
212
|
+
if len(sys.argv) > 1 and sys.argv[1] == "update":
|
|
213
|
+
do_install = "--install" in sys.argv
|
|
214
|
+
try:
|
|
215
|
+
resp = requests.get(
|
|
216
|
+
"https://pypi.org/pypi/codicent-cli/json",
|
|
217
|
+
timeout=10,
|
|
218
|
+
)
|
|
219
|
+
resp.raise_for_status()
|
|
220
|
+
latest = resp.json()["info"]["version"]
|
|
221
|
+
except Exception as e:
|
|
222
|
+
console.print(f"[red]❌ Could not check PyPI: {e}[/red]")
|
|
223
|
+
return 1
|
|
224
|
+
|
|
225
|
+
from packaging.version import Version
|
|
226
|
+
|
|
227
|
+
# Detect whether we're running inside a pipx-managed environment
|
|
228
|
+
is_pipx = "pipx" in sys.executable.replace("\\", "/")
|
|
229
|
+
|
|
230
|
+
if Version(latest) > Version(CURRENT_VERSION):
|
|
231
|
+
console.print(
|
|
232
|
+
f"[yellow]⬆ New version available:[/yellow] "
|
|
233
|
+
f"[bold]{latest}[/bold] (you have {CURRENT_VERSION})"
|
|
234
|
+
)
|
|
235
|
+
if do_install:
|
|
236
|
+
if is_pipx:
|
|
237
|
+
console.print("[dim]Running pipx upgrade codicent-cli...[/dim]")
|
|
238
|
+
result = subprocess.run(
|
|
239
|
+
["pipx", "upgrade", "codicent-cli"],
|
|
240
|
+
capture_output=False,
|
|
241
|
+
)
|
|
242
|
+
else:
|
|
243
|
+
console.print("[dim]Running pip install --upgrade codicent-cli...[/dim]")
|
|
244
|
+
result = subprocess.run(
|
|
245
|
+
[sys.executable, "-m", "pip", "install", "--upgrade", "codicent-cli"],
|
|
246
|
+
capture_output=False,
|
|
247
|
+
)
|
|
248
|
+
return result.returncode
|
|
249
|
+
else:
|
|
250
|
+
upgrade_cmd = "pipx upgrade codicent-cli" if is_pipx else "pip install --upgrade codicent-cli"
|
|
251
|
+
console.print(
|
|
252
|
+
f"[dim]Run [bold]codicent update --install[/bold] to upgrade, "
|
|
253
|
+
f"or [bold]{upgrade_cmd}[/bold][/dim]"
|
|
254
|
+
)
|
|
255
|
+
else:
|
|
256
|
+
console.print(f"[green]✅ Already up to date (v{CURRENT_VERSION})[/green]")
|
|
257
|
+
return 0
|
|
258
|
+
|
|
155
259
|
# Handle upload command
|
|
156
260
|
if len(sys.argv) > 1 and sys.argv[1] == "upload":
|
|
157
261
|
if len(sys.argv) < 3:
|
|
@@ -270,6 +374,9 @@ def main():
|
|
|
270
374
|
if len(sys.argv) > 1 and sys.argv[1] == "init":
|
|
271
375
|
templates_dir = os.path.join(os.path.dirname(__file__), "templates")
|
|
272
376
|
|
|
377
|
+
dry_run = "--dry-run" in sys.argv
|
|
378
|
+
force = "--force" in sys.argv
|
|
379
|
+
|
|
273
380
|
# --list: show available templates
|
|
274
381
|
if "--list" in sys.argv:
|
|
275
382
|
try:
|
|
@@ -279,7 +386,10 @@ def main():
|
|
|
279
386
|
template_path = os.path.join(templates_dir, f"{name}.json")
|
|
280
387
|
with open(template_path, encoding="utf-8") as fh:
|
|
281
388
|
meta = json.load(fh)
|
|
282
|
-
|
|
389
|
+
vars_info = ""
|
|
390
|
+
if meta.get("variables"):
|
|
391
|
+
vars_info = f" [dim]({len(meta['variables'])} variable(s))[/dim]"
|
|
392
|
+
console.print(f" [bold bright_cyan]{name}[/bold bright_cyan]{vars_info} — {meta.get('description', '')}")
|
|
283
393
|
except Exception as e:
|
|
284
394
|
console.print(f"[red]❌ Error listing templates: {e}[/red]")
|
|
285
395
|
return 0
|
|
@@ -299,6 +409,51 @@ def main():
|
|
|
299
409
|
console.print(f"[red]❌ Template '{template_name}' not found. Use 'codicent init --list' to see options.[/red]")
|
|
300
410
|
return 1
|
|
301
411
|
|
|
412
|
+
with open(template_path, encoding="utf-8") as fh:
|
|
413
|
+
template = json.load(fh)
|
|
414
|
+
|
|
415
|
+
# Resolve variables: prompt for any without defaults
|
|
416
|
+
variable_values = {}
|
|
417
|
+
template_variables = template.get("variables", {})
|
|
418
|
+
for var_name, var_meta in template_variables.items():
|
|
419
|
+
default = var_meta.get("default", "")
|
|
420
|
+
description = var_meta.get("description", var_name)
|
|
421
|
+
if dry_run:
|
|
422
|
+
# In dry-run, use defaults without prompting
|
|
423
|
+
variable_values[var_name] = default or f"{{{var_name}}}"
|
|
424
|
+
elif default:
|
|
425
|
+
variable_values[var_name] = default
|
|
426
|
+
console.print(f"[dim] {var_name}: {default} (default)[/dim]")
|
|
427
|
+
else:
|
|
428
|
+
value = input(f" {description}: ").strip()
|
|
429
|
+
if not value:
|
|
430
|
+
console.print(f"[red]❌ Variable '{var_name}' is required.[/red]")
|
|
431
|
+
return 1
|
|
432
|
+
variable_values[var_name] = value
|
|
433
|
+
|
|
434
|
+
def apply_variables(text, project):
|
|
435
|
+
result = text.replace("{project}", project)
|
|
436
|
+
for var_name, var_value in variable_values.items():
|
|
437
|
+
result = result.replace("{" + var_name + "}", var_value)
|
|
438
|
+
return result
|
|
439
|
+
|
|
440
|
+
# --- Dry-run mode: print plan without calling API ---
|
|
441
|
+
if dry_run:
|
|
442
|
+
messages = template.get("messages", [])
|
|
443
|
+
console.print(f"\n[bold]Dry run — template [bright_cyan]{template_name}[/bright_cyan] ({len(messages)} messages)[/bold]")
|
|
444
|
+
console.print(f"[dim]No messages will be posted. Remove --dry-run to execute.[/dim]\n")
|
|
445
|
+
for i, entry in enumerate(messages, 1):
|
|
446
|
+
label = entry.get("label", f"Message {i}")
|
|
447
|
+
content = apply_variables(entry["content"], "{project}")
|
|
448
|
+
# Show first 120 chars of content
|
|
449
|
+
preview = content.replace("\n", " ↵ ")
|
|
450
|
+
if len(preview) > 120:
|
|
451
|
+
preview = preview[:117] + "..."
|
|
452
|
+
console.print(f" [bold]{i}.[/bold] [bright_cyan]{label}[/bright_cyan]")
|
|
453
|
+
console.print(f" [dim]{preview}[/dim]")
|
|
454
|
+
console.print()
|
|
455
|
+
return 0
|
|
456
|
+
|
|
302
457
|
# Get authentication token
|
|
303
458
|
token = auth.get_cached_token()
|
|
304
459
|
if not token:
|
|
@@ -325,23 +480,41 @@ def main():
|
|
|
325
480
|
console.print(f"[red]❌ Failed to initialize Codicent API client: {e}[/red]")
|
|
326
481
|
return 1
|
|
327
482
|
|
|
328
|
-
|
|
329
|
-
|
|
483
|
+
# Idempotency check: warn if #instructions already exists in this project
|
|
484
|
+
if not force:
|
|
485
|
+
try:
|
|
486
|
+
headers = {"Authorization": f"Bearer {token}"}
|
|
487
|
+
check_url = f"{codicent.base_url}api/GetMessages"
|
|
488
|
+
resp = requests.get(
|
|
489
|
+
check_url,
|
|
490
|
+
params={"mention": project, "tags": "instructions", "length": "1"},
|
|
491
|
+
headers=headers,
|
|
492
|
+
verify=codicent.verify_https,
|
|
493
|
+
timeout=10,
|
|
494
|
+
)
|
|
495
|
+
if resp.ok and resp.json():
|
|
496
|
+
console.print(f"[yellow]⚠ Project [bold]{project}[/bold] already has #instructions — it may have been initialized before.[/yellow]")
|
|
497
|
+
console.print("[dim]Pass --force to initialize anyway, or --dry-run to preview what would be posted.[/dim]")
|
|
498
|
+
return 1
|
|
499
|
+
except Exception:
|
|
500
|
+
logger.debug("Idempotency check failed (skipping)")
|
|
330
501
|
|
|
331
502
|
messages = template.get("messages", [])
|
|
332
503
|
console.print(f"Initializing [bold]{project}[/bold] with template [bold bright_cyan]{template_name}[/bold bright_cyan] ({len(messages)} messages)...")
|
|
333
504
|
|
|
334
505
|
posted = 0
|
|
335
506
|
failed = 0
|
|
336
|
-
for entry in messages:
|
|
337
|
-
|
|
507
|
+
for i, entry in enumerate(messages, 1):
|
|
508
|
+
label = entry.get("label", f"Message {i}")
|
|
509
|
+
content = apply_variables(entry["content"], project)
|
|
338
510
|
msg_type = entry.get("type", "info")
|
|
339
511
|
try:
|
|
340
|
-
with console.status(f"[dim]
|
|
512
|
+
with console.status(f"[dim]({i}/{len(messages)}) {label}...[/dim]", spinner="dots"):
|
|
341
513
|
codicent.post_message(content, type=msg_type)
|
|
514
|
+
console.print(f" [green]✓[/green] [dim]{label}[/dim]")
|
|
342
515
|
posted += 1
|
|
343
516
|
except Exception as e:
|
|
344
|
-
console.print(f"[red]
|
|
517
|
+
console.print(f" [red]✗ {label}: {e}[/red]")
|
|
345
518
|
failed += 1
|
|
346
519
|
|
|
347
520
|
console.print()
|
|
@@ -350,6 +523,38 @@ def main():
|
|
|
350
523
|
console.print(f"[red]❌ {failed} message(s) failed.[/red]")
|
|
351
524
|
return 0 if failed == 0 else 1
|
|
352
525
|
|
|
526
|
+
# Handle wrap command
|
|
527
|
+
if len(sys.argv) > 1 and sys.argv[1] == "wrap":
|
|
528
|
+
from wrap import run_wrap_command
|
|
529
|
+
|
|
530
|
+
wrap_token = auth.get_cached_token()
|
|
531
|
+
if not wrap_token:
|
|
532
|
+
wrap_token = os.getenv("CODICENT_TOKEN")
|
|
533
|
+
if not wrap_token:
|
|
534
|
+
console.print("[red]❌ No authentication found.[/red]")
|
|
535
|
+
console.print("[dim]Run 'codicent auth' to authenticate, or set CODICENT_TOKEN environment variable[/dim]")
|
|
536
|
+
return 1
|
|
537
|
+
|
|
538
|
+
# Extract project and base_url from JWT token
|
|
539
|
+
try:
|
|
540
|
+
wrap_payload = wrap_token.split(".")[1]
|
|
541
|
+
wrap_payload += '=' * (-len(wrap_payload) % 4)
|
|
542
|
+
wrap_jwt = json.loads(base64.urlsafe_b64decode(wrap_payload).decode('utf-8'))
|
|
543
|
+
wrap_project = wrap_jwt["project"]
|
|
544
|
+
wrap_base_url = wrap_jwt.get("baseUrl", "https://codicent.com/")
|
|
545
|
+
except Exception as e:
|
|
546
|
+
console.print(f"[red]❌ Failed to extract project from token: {e}[/red]")
|
|
547
|
+
return 1
|
|
548
|
+
|
|
549
|
+
# Pass everything after "wrap" as args
|
|
550
|
+
wrap_args = sys.argv[2:]
|
|
551
|
+
return run_wrap_command(
|
|
552
|
+
args=wrap_args,
|
|
553
|
+
token=wrap_token,
|
|
554
|
+
base_url=wrap_base_url,
|
|
555
|
+
project=wrap_project,
|
|
556
|
+
)
|
|
557
|
+
|
|
353
558
|
# Get authentication token
|
|
354
559
|
token = auth.get_cached_token()
|
|
355
560
|
if not token:
|
|
@@ -578,7 +783,7 @@ def main():
|
|
|
578
783
|
|
|
579
784
|
header = Text()
|
|
580
785
|
header.append("Codicent CLI", style="bold bright_cyan")
|
|
581
|
-
header.append("
|
|
786
|
+
header.append(f" v{CURRENT_VERSION}\n", style="bright_cyan")
|
|
582
787
|
header.append("Type your questions or use Ctrl+C to exit.\n", style="dim")
|
|
583
788
|
header.append("\n")
|
|
584
789
|
header.append("Tip: ", style="bold dim")
|