devin-cli 1.2.4__tar.gz → 1.3.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.
- {devin_cli-1.2.4 → devin_cli-1.3.0}/PKG-INFO +11 -2
- {devin_cli-1.2.4 → devin_cli-1.3.0}/README.md +10 -1
- devin_cli-1.3.0/patch_cli.py +78 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/pyproject.toml +1 -1
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/cli.py +60 -9
- devin_cli-1.3.0/test_json.py +46 -0
- devin_cli-1.3.0/test_json_wrapper.py +47 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/tests/test_cli.py +1 -1
- {devin_cli-1.2.4 → devin_cli-1.3.0}/.github/workflows/pypi-publish.yml +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/.gitignore +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/CHANGELOG.md +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/LICENSE +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/assets/logo.png +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/generate_facades.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/setup.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/__init__.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/__init__.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/attachments.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/client.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/consumption.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/knowledge.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/members.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/organizations.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/playbooks.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/repositories.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/schedules.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/secrets.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/sessions.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/v1/__init__.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/v1/attachments.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/v1/knowledge.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/v1/playbooks.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/v1/secrets.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/v1/sessions.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/v3/__init__.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/v3/attachments.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/v3/consumption.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/v3/knowledge.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/v3/members.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/v3/organizations.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/v3/playbooks.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/v3/repositories.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/v3/schedules.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/v3/secrets.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/api/v3/sessions.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/src/devin_cli/config.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/tests/__init__.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/tests/conftest.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/tests/test_api/test_attachments.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/tests/test_api/test_knowledge.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/tests/test_api/test_playbooks.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/tests/test_api/test_repositories.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/tests/test_api/test_schedules.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/tests/test_api/test_secrets.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/tests/test_api/test_sessions.py +0 -0
- {devin_cli-1.2.4 → devin_cli-1.3.0}/tests/test_config.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devin-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Unofficial CLI for Devin AI - The first AI Software Engineer
|
|
5
5
|
Project-URL: Homepage, https://github.com/revanthpobala/devin-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/revanthpobala/devin-cli.git
|
|
@@ -87,7 +87,16 @@ devin watch
|
|
|
87
87
|
|
|
88
88
|
---
|
|
89
89
|
|
|
90
|
-
##
|
|
90
|
+
## Authentication & Usage
|
|
91
|
+
|
|
92
|
+
### AI Agent Integration (JSON Output)
|
|
93
|
+
For external automation and AI agent architectures relying on the CLI, `devin-cli` supports a global `--json` flag. This will suppress all visual output/terminal colors and instead uniformly return raw JSON objects for stdout and API errors, making the CLI completely deterministic to parse.
|
|
94
|
+
```bash
|
|
95
|
+
devin --json sessions create "My prompt"
|
|
96
|
+
# { "session_id": "...", "status": "running" }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Multi-Profile Support
|
|
91
100
|
|
|
92
101
|
```bash
|
|
93
102
|
devin configure --profile personal
|
|
@@ -51,7 +51,16 @@ devin watch
|
|
|
51
51
|
|
|
52
52
|
---
|
|
53
53
|
|
|
54
|
-
##
|
|
54
|
+
## Authentication & Usage
|
|
55
|
+
|
|
56
|
+
### AI Agent Integration (JSON Output)
|
|
57
|
+
For external automation and AI agent architectures relying on the CLI, `devin-cli` supports a global `--json` flag. This will suppress all visual output/terminal colors and instead uniformly return raw JSON objects for stdout and API errors, making the CLI completely deterministic to parse.
|
|
58
|
+
```bash
|
|
59
|
+
devin --json sessions create "My prompt"
|
|
60
|
+
# { "session_id": "...", "status": "running" }
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Multi-Profile Support
|
|
55
64
|
|
|
56
65
|
```bash
|
|
57
66
|
devin configure --profile personal
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
with open("src/devin_cli/cli.py", "r") as f:
|
|
4
|
+
lines = f.read().splitlines()
|
|
5
|
+
|
|
6
|
+
# We want to find each `def ..._cmd(...)` block.
|
|
7
|
+
# Then, if it contains `resp = ` and doesn't end with `return`, we insert `return resp`.
|
|
8
|
+
|
|
9
|
+
new_lines = []
|
|
10
|
+
i = 0
|
|
11
|
+
|
|
12
|
+
in_cmd = False
|
|
13
|
+
cmd_indent = ""
|
|
14
|
+
cmd_has_resp = False
|
|
15
|
+
cmd_lines = []
|
|
16
|
+
|
|
17
|
+
def finish_cmd():
|
|
18
|
+
global cmd_lines, new_lines, cmd_has_resp
|
|
19
|
+
|
|
20
|
+
if not cmd_lines: return
|
|
21
|
+
|
|
22
|
+
# check if has resp
|
|
23
|
+
text = "\n".join(cmd_lines)
|
|
24
|
+
if cmd_has_resp and "return" not in cmd_lines[-1]:
|
|
25
|
+
# find the indentation of the last non-empty line
|
|
26
|
+
for l in reversed(cmd_lines):
|
|
27
|
+
if l.strip():
|
|
28
|
+
indent = len(l) - len(l.lstrip())
|
|
29
|
+
break
|
|
30
|
+
else:
|
|
31
|
+
indent = 4
|
|
32
|
+
|
|
33
|
+
# Don't add if it's already there
|
|
34
|
+
if not cmd_lines[-1].strip().startswith("return"):
|
|
35
|
+
cmd_lines.append(" " * indent + "return resp")
|
|
36
|
+
|
|
37
|
+
new_lines.extend(cmd_lines)
|
|
38
|
+
cmd_lines = []
|
|
39
|
+
cmd_has_resp = False
|
|
40
|
+
|
|
41
|
+
while i < len(lines):
|
|
42
|
+
line = lines[i]
|
|
43
|
+
|
|
44
|
+
if line.startswith("def ") and "_cmd(" in line:
|
|
45
|
+
finish_cmd() # finish previous if any
|
|
46
|
+
in_cmd = True
|
|
47
|
+
cmd_indent = "def"
|
|
48
|
+
cmd_lines.append(line)
|
|
49
|
+
elif in_cmd:
|
|
50
|
+
if line.startswith("def "): # another function started
|
|
51
|
+
finish_cmd()
|
|
52
|
+
in_cmd = True
|
|
53
|
+
cmd_lines.append(line)
|
|
54
|
+
elif line and not line.startswith(" ") and not line.startswith("("):
|
|
55
|
+
# top level code or decorator
|
|
56
|
+
if line.startswith("@"):
|
|
57
|
+
finish_cmd()
|
|
58
|
+
new_lines.append(line)
|
|
59
|
+
in_cmd = False
|
|
60
|
+
else:
|
|
61
|
+
finish_cmd()
|
|
62
|
+
new_lines.append(line)
|
|
63
|
+
in_cmd = False
|
|
64
|
+
else:
|
|
65
|
+
if "resp =" in line or "resp = " in line:
|
|
66
|
+
cmd_has_resp = True
|
|
67
|
+
cmd_lines.append(line)
|
|
68
|
+
else:
|
|
69
|
+
new_lines.append(line)
|
|
70
|
+
|
|
71
|
+
i += 1
|
|
72
|
+
|
|
73
|
+
finish_cmd()
|
|
74
|
+
|
|
75
|
+
with open("src/devin_cli/cli.py", "w") as f:
|
|
76
|
+
f.write("\n".join(new_lines) + "\n")
|
|
77
|
+
|
|
78
|
+
print("Patched src/devin_cli/cli.py")
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import typer
|
|
2
2
|
import time
|
|
3
3
|
import json
|
|
4
|
+
import requests
|
|
5
|
+
import functools
|
|
4
6
|
import yaml
|
|
5
7
|
import asyncio
|
|
6
8
|
from pathlib import Path
|
|
@@ -80,10 +82,17 @@ def main(
|
|
|
80
82
|
ctx: typer.Context,
|
|
81
83
|
profile: Optional[str] = typer.Option(None, "--profile", "-p", help="Configuration profile to use (e.g., service, personal)"),
|
|
82
84
|
version: Optional[bool] = typer.Option(None, "--version", "-v", help="Show the application's version and exit."),
|
|
85
|
+
json_format: bool = typer.Option(False, "--json", help="Output raw JSON (for AI agents & scripting)"),
|
|
83
86
|
):
|
|
84
87
|
"""
|
|
85
88
|
Unofficial CLI for Devin AI v3.
|
|
86
89
|
"""
|
|
90
|
+
if json_format:
|
|
91
|
+
import os
|
|
92
|
+
import warnings
|
|
93
|
+
os.environ["DEVIN_OUTPUT_FORMAT"] = "json"
|
|
94
|
+
warnings.filterwarnings("ignore")
|
|
95
|
+
|
|
87
96
|
if version:
|
|
88
97
|
console.print(f"devin CLI version: {__version__}")
|
|
89
98
|
raise typer.Exit()
|
|
@@ -97,16 +106,50 @@ def handle_api_error(func):
|
|
|
97
106
|
"""Decorator to handle API errors gracefully."""
|
|
98
107
|
@functools.wraps(func)
|
|
99
108
|
def wrapper(*args, **kwargs):
|
|
109
|
+
import os
|
|
110
|
+
is_json = os.environ.get("DEVIN_OUTPUT_FORMAT") == "json"
|
|
111
|
+
original_print = console.print
|
|
100
112
|
try:
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
if is_json:
|
|
114
|
+
console.print = lambda *a, **k: None
|
|
115
|
+
|
|
116
|
+
result = func(*args, **kwargs)
|
|
117
|
+
|
|
118
|
+
if is_json:
|
|
119
|
+
console.print = original_print
|
|
120
|
+
if result is not None:
|
|
121
|
+
print(json.dumps(result, indent=2))
|
|
122
|
+
|
|
123
|
+
return result
|
|
124
|
+
except BaseException as e:
|
|
125
|
+
if is_json:
|
|
126
|
+
console.print = original_print
|
|
127
|
+
if isinstance(e, typer.Exit):
|
|
128
|
+
raise e
|
|
129
|
+
elif isinstance(e, requests.exceptions.RequestException):
|
|
130
|
+
if is_json:
|
|
131
|
+
print(json.dumps({"error": "Connection error", "details": str(e)}, indent=2))
|
|
132
|
+
else:
|
|
133
|
+
console.print(f"[bold red]Connection Error:[/bold red] Failed to connect to {config.base_url}")
|
|
134
|
+
console.print(f"[dim]{str(e)}[/dim]")
|
|
135
|
+
raise typer.Exit(1)
|
|
136
|
+
elif hasattr(e, "response"): # Standard APIError handling
|
|
137
|
+
if is_json:
|
|
138
|
+
try:
|
|
139
|
+
err_json = e.response.json()
|
|
140
|
+
except:
|
|
141
|
+
err_json = {"error": str(e)}
|
|
142
|
+
print(json.dumps(err_json, indent=2))
|
|
143
|
+
else:
|
|
144
|
+
console.print(f"[bold red]API Error:[/bold red] {e}")
|
|
145
|
+
raise typer.Exit(1)
|
|
146
|
+
else:
|
|
147
|
+
if is_json:
|
|
148
|
+
print(json.dumps({"error": str(e)}, indent=2))
|
|
149
|
+
else:
|
|
150
|
+
console.print(f"[bold red]Error:[/bold red] {e}")
|
|
151
|
+
raise typer.Exit(1)
|
|
152
|
+
|
|
110
153
|
return wrapper
|
|
111
154
|
|
|
112
155
|
def get_current_session_id():
|
|
@@ -408,6 +451,7 @@ def list_knowledge_cmd(org: Optional[str] = typer.Option(None, "--org")):
|
|
|
408
451
|
table.add_row(item.get("id"), item.get("title") or item.get("name"))
|
|
409
452
|
console.print(table)
|
|
410
453
|
|
|
454
|
+
return resp
|
|
411
455
|
@knowledge_app.command("create")
|
|
412
456
|
@handle_api_error
|
|
413
457
|
def create_knowledge_cmd(
|
|
@@ -576,6 +620,7 @@ def list_schedules_cmd(org: Optional[str] = typer.Option(None, "--org")):
|
|
|
576
620
|
table.add_row(item.get("id"), item.get("title"), item.get("cron"))
|
|
577
621
|
console.print(table)
|
|
578
622
|
|
|
623
|
+
return resp
|
|
579
624
|
@schedule_app.command("create")
|
|
580
625
|
@handle_api_error
|
|
581
626
|
def create_schedule_cmd(
|
|
@@ -676,6 +721,7 @@ def whoami_cmd():
|
|
|
676
721
|
resp = members.get_self()
|
|
677
722
|
console.print(Panel(json.dumps(resp, indent=2), title="Who Am I"))
|
|
678
723
|
|
|
724
|
+
return resp
|
|
679
725
|
@enterprise_app.command("list-orgs")
|
|
680
726
|
@handle_api_error
|
|
681
727
|
def list_orgs_cmd():
|
|
@@ -683,6 +729,7 @@ def list_orgs_cmd():
|
|
|
683
729
|
resp = organizations.list_organizations()
|
|
684
730
|
console.print(Panel(json.dumps(resp, indent=2), title="Organizations"))
|
|
685
731
|
|
|
732
|
+
return resp
|
|
686
733
|
# --- Global Commands ---
|
|
687
734
|
@app.command("use")
|
|
688
735
|
def use_session_cmd(session_id: str):
|
|
@@ -711,6 +758,7 @@ def status_cmd():
|
|
|
711
758
|
if url:
|
|
712
759
|
console.print(f"[bold]URL:[/bold] [underline]{url}[/underline]")
|
|
713
760
|
|
|
761
|
+
return resp
|
|
714
762
|
@app.command("open")
|
|
715
763
|
@handle_api_error
|
|
716
764
|
def open_cmd(
|
|
@@ -865,6 +913,7 @@ def upload_cmd(path: Path = typer.Argument(..., help="File to upload")):
|
|
|
865
913
|
resp = attachments.upload_file(str(path))
|
|
866
914
|
console.print(f"[green]Uploaded:[/green] {resp}")
|
|
867
915
|
|
|
916
|
+
return resp
|
|
868
917
|
@app.command("list-knowledge")
|
|
869
918
|
@handle_api_error
|
|
870
919
|
def list_knowledge_top_cmd():
|
|
@@ -878,6 +927,7 @@ def list_knowledge_top_cmd():
|
|
|
878
927
|
table.add_row(item.get("id"), item.get("title") or item.get("name"))
|
|
879
928
|
console.print(table)
|
|
880
929
|
|
|
930
|
+
return resp
|
|
881
931
|
@app.command("attach")
|
|
882
932
|
@handle_api_error
|
|
883
933
|
def attach_cmd(
|
|
@@ -1006,6 +1056,7 @@ def list_secrets_top_cmd():
|
|
|
1006
1056
|
table.add_row(item.get("id", ""), item.get("name", ""))
|
|
1007
1057
|
console.print(table)
|
|
1008
1058
|
|
|
1059
|
+
return resp
|
|
1009
1060
|
@app.command("delete-secret")
|
|
1010
1061
|
@handle_api_error
|
|
1011
1062
|
def delete_secret_top_cmd(secret_id: str = typer.Argument(...)):
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
from functools import wraps
|
|
4
|
+
import builtins
|
|
5
|
+
|
|
6
|
+
class Console:
|
|
7
|
+
def print(self, *args, **kwargs):
|
|
8
|
+
builtins.print("RICH_PRINT:", *args)
|
|
9
|
+
|
|
10
|
+
console = Console()
|
|
11
|
+
|
|
12
|
+
def handle_api_error(func):
|
|
13
|
+
@wraps(func)
|
|
14
|
+
def wrapper(*args, **kwargs):
|
|
15
|
+
try:
|
|
16
|
+
is_json = os.environ.get("DEVIN_OUTPUT_FORMAT") == "json"
|
|
17
|
+
if is_json:
|
|
18
|
+
original_print = console.print
|
|
19
|
+
console.print = lambda *args, **kwargs: None
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
result = func(*args, **kwargs)
|
|
23
|
+
finally:
|
|
24
|
+
if is_json:
|
|
25
|
+
console.print = original_print
|
|
26
|
+
|
|
27
|
+
if is_json and result is not None:
|
|
28
|
+
builtins.print(json.dumps(result, indent=2))
|
|
29
|
+
return result
|
|
30
|
+
except Exception as e:
|
|
31
|
+
builtins.print("ERROR:", e)
|
|
32
|
+
return wrapper
|
|
33
|
+
|
|
34
|
+
@handle_api_error
|
|
35
|
+
def create_session_cmd():
|
|
36
|
+
console.print("[green]Session created[/green]")
|
|
37
|
+
resp = {"session_id": "123", "status": "running"}
|
|
38
|
+
return resp
|
|
39
|
+
|
|
40
|
+
print("--- NORMAL MODE ---")
|
|
41
|
+
os.environ.pop("DEVIN_OUTPUT_FORMAT", None)
|
|
42
|
+
create_session_cmd()
|
|
43
|
+
|
|
44
|
+
print("\n--- JSON MODE ---")
|
|
45
|
+
os.environ["DEVIN_OUTPUT_FORMAT"] = "json"
|
|
46
|
+
create_session_cmd()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
from functools import wraps
|
|
4
|
+
import builtins
|
|
5
|
+
|
|
6
|
+
class Console:
|
|
7
|
+
def print(self, *args, **kwargs):
|
|
8
|
+
builtins.print("RICH_PRINT:", *args)
|
|
9
|
+
|
|
10
|
+
console = Console()
|
|
11
|
+
|
|
12
|
+
def handle_api_error(func):
|
|
13
|
+
@wraps(func)
|
|
14
|
+
def wrapper(*args, **kwargs):
|
|
15
|
+
try:
|
|
16
|
+
is_json = os.environ.get("DEVIN_OUTPUT_FORMAT") == "json"
|
|
17
|
+
if is_json:
|
|
18
|
+
original_print = console.print
|
|
19
|
+
console.print = lambda *args, **kwargs: None
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
result = func(*args, **kwargs)
|
|
23
|
+
finally:
|
|
24
|
+
if is_json:
|
|
25
|
+
console.print = original_print
|
|
26
|
+
|
|
27
|
+
if is_json and result is not None:
|
|
28
|
+
builtins.print(json.dumps(result, indent=2))
|
|
29
|
+
return result
|
|
30
|
+
except Exception as e:
|
|
31
|
+
builtins.print("ERROR:", e)
|
|
32
|
+
return wrapper
|
|
33
|
+
|
|
34
|
+
@handle_api_error
|
|
35
|
+
def create_session_cmd():
|
|
36
|
+
console.print("[green]Session created[/green]")
|
|
37
|
+
resp = {"session_id": "123", "status": "running"}
|
|
38
|
+
return resp
|
|
39
|
+
|
|
40
|
+
print("--- NORMAL MODE ---")
|
|
41
|
+
if "DEVIN_OUTPUT_FORMAT" in os.environ:
|
|
42
|
+
del os.environ["DEVIN_OUTPUT_FORMAT"]
|
|
43
|
+
create_session_cmd()
|
|
44
|
+
|
|
45
|
+
print("\n--- JSON MODE ---")
|
|
46
|
+
os.environ["DEVIN_OUTPUT_FORMAT"] = "json"
|
|
47
|
+
create_session_cmd()
|
|
@@ -22,6 +22,6 @@ def test_cli_handles_api_error():
|
|
|
22
22
|
# command is now 'sessions create'
|
|
23
23
|
result = runner.invoke(app, ["sessions", "create", "test prompt"])
|
|
24
24
|
assert result.exit_code == 1
|
|
25
|
-
assert "
|
|
25
|
+
assert "Error" in result.stdout
|
|
26
26
|
assert "API BOOM" in result.stdout
|
|
27
27
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|