devin-cli 1.1.9__tar.gz → 1.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.
- {devin_cli-1.1.9 → devin_cli-1.2.0}/CHANGELOG.md +9 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/PKG-INFO +1 -1
- {devin_cli-1.1.9 → devin_cli-1.2.0}/pyproject.toml +1 -1
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/v1/sessions.py +12 -8
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/cli.py +42 -8
- {devin_cli-1.1.9 → devin_cli-1.2.0}/.github/workflows/pypi-publish.yml +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/.gitignore +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/LICENSE +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/README.md +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/assets/logo.png +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/generate_facades.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/setup.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/__init__.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/__init__.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/attachments.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/client.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/consumption.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/knowledge.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/members.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/organizations.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/playbooks.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/repositories.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/schedules.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/secrets.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/sessions.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/v1/__init__.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/v1/attachments.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/v1/knowledge.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/v1/playbooks.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/v1/secrets.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/v3/__init__.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/v3/attachments.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/v3/consumption.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/v3/knowledge.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/v3/members.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/v3/organizations.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/v3/playbooks.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/v3/repositories.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/v3/schedules.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/v3/secrets.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/api/v3/sessions.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/src/devin_cli/config.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/tests/__init__.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/tests/conftest.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/tests/test_api/test_attachments.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/tests/test_api/test_knowledge.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/tests/test_api/test_playbooks.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/tests/test_api/test_repositories.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/tests/test_api/test_schedules.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/tests/test_api/test_secrets.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/tests/test_api/test_sessions.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/tests/test_cli.py +0 -0
- {devin_cli-1.1.9 → devin_cli-1.2.0}/tests/test_config.py +0 -0
|
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.2.0] - 2026-03-11
|
|
9
|
+
### Added
|
|
10
|
+
- `sessions create` / `create-session` now exposes all 8 previously missing v3 API parameters: `--playbook-id`, `--tag`, `--repo`, `--knowledge-id`, `--secret-id`, `--session-link`, `--attachment-url`, `--create-as-user-id`.
|
|
11
|
+
- Profile feedback on session create: prints which profile and API version is being used.
|
|
12
|
+
- Explicit warning when v3-only flags (`--advanced-mode`, `--playbook-id`, `--repo`, `--secret-id`, `--session-link`, `--attachment-url`, `--create-as-user-id`) are passed on a v1 profile — instead of silently ignored.
|
|
13
|
+
### Fixed
|
|
14
|
+
- Removed `**kwargs` anti-pattern from `v1/sessions.create_session`. Typos in keyword args now raise `TypeError` instead of silently vanishing.
|
|
15
|
+
- v3-only params added as explicit no-ops in v1 `create_session` so the shared CLI call site works without `TypeError`.
|
|
16
|
+
|
|
8
17
|
## [1.1.9] - 2026-03-11
|
|
9
18
|
### Fixed
|
|
10
19
|
- `devin watch` terminal status message was printed inside a `Live` context, causing output corruption. Now printed after the `Live` block exits.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devin-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.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
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import List, Optional
|
|
2
2
|
from devin_cli.api.client import client
|
|
3
3
|
|
|
4
|
-
def list_sessions(limit: int = 100, offset: int = 0, tags: List[str] = None):
|
|
4
|
+
def list_sessions(limit: int = 100, offset: int = 0, tags: Optional[List[str]] = None):
|
|
5
5
|
params = {"limit": limit, "offset": offset}
|
|
6
6
|
if tags:
|
|
7
7
|
params["tags"] = tags
|
|
@@ -10,21 +10,25 @@ def list_sessions(limit: int = 100, offset: int = 0, tags: List[str] = None):
|
|
|
10
10
|
def create_session(
|
|
11
11
|
prompt: str,
|
|
12
12
|
idempotent: bool = False,
|
|
13
|
-
snapshot_id: str = None,
|
|
14
|
-
playbook_id: str = None,
|
|
13
|
+
snapshot_id: Optional[str] = None,
|
|
14
|
+
playbook_id: Optional[str] = None,
|
|
15
15
|
unlisted: bool = False,
|
|
16
|
-
tags: List[str] = None,
|
|
17
|
-
session_secrets: List[dict] = None,
|
|
16
|
+
tags: Optional[List[str]] = None,
|
|
17
|
+
session_secrets: Optional[List[dict]] = None,
|
|
18
18
|
title: Optional[str] = None,
|
|
19
19
|
knowledge_ids: Optional[List[str]] = None,
|
|
20
20
|
secret_ids: Optional[List[str]] = None,
|
|
21
21
|
max_acu_limit: Optional[int] = None,
|
|
22
|
-
|
|
22
|
+
advanced_mode: Optional[str] = None,
|
|
23
|
+
repos: Optional[List[str]] = None,
|
|
24
|
+
session_links: Optional[List[str]] = None,
|
|
25
|
+
attachment_urls: Optional[List[str]] = None,
|
|
26
|
+
create_as_user_id: Optional[str] = None,
|
|
23
27
|
):
|
|
24
28
|
data = {
|
|
25
29
|
"prompt": prompt,
|
|
26
30
|
"idempotent": idempotent,
|
|
27
|
-
"unlisted": unlisted
|
|
31
|
+
"unlisted": unlisted,
|
|
28
32
|
}
|
|
29
33
|
if snapshot_id:
|
|
30
34
|
data["snapshot_id"] = snapshot_id
|
|
@@ -42,7 +46,7 @@ def create_session(
|
|
|
42
46
|
data["secret_ids"] = secret_ids
|
|
43
47
|
if max_acu_limit:
|
|
44
48
|
data["max_acu_limit"] = max_acu_limit
|
|
45
|
-
|
|
49
|
+
|
|
46
50
|
return client.post("sessions", json=data)
|
|
47
51
|
|
|
48
52
|
def get_session(session_id: str):
|
|
@@ -155,13 +155,38 @@ def create_session_cmd(
|
|
|
155
155
|
title: Optional[str] = typer.Option(None, "--title", "-t", help="Custom session title"),
|
|
156
156
|
org: Optional[str] = typer.Option(None, "--org", help="Override organization ID"),
|
|
157
157
|
max_acu: Optional[int] = typer.Option(None, "--max-acu", help="Maximum ACU limit"),
|
|
158
|
-
advanced_mode: Optional[str] = typer.Option(None, "--advanced-mode", help="
|
|
158
|
+
advanced_mode: Optional[str] = typer.Option(None, "--advanced-mode", help="Advanced mode type: 'cli' or 'browser' (v3 only)"),
|
|
159
|
+
playbook_id: Optional[str] = typer.Option(None, "--playbook-id", help="Playbook ID to apply (v3 only)"),
|
|
160
|
+
tags: Optional[List[str]] = typer.Option(None, "--tag", help="Session tags (repeatable)"),
|
|
161
|
+
repos: Optional[List[str]] = typer.Option(None, "--repo", help="Repository URLs to attach (v3 only, repeatable)"),
|
|
162
|
+
knowledge_ids: Optional[List[str]] = typer.Option(None, "--knowledge-id", help="Knowledge IDs to attach (repeatable)"),
|
|
163
|
+
secret_ids: Optional[List[str]] = typer.Option(None, "--secret-id", help="Secret IDs to inject (v3 only, repeatable)"),
|
|
164
|
+
session_links: Optional[List[str]] = typer.Option(None, "--session-link", help="Session URLs to link as context (v3 only, repeatable)"),
|
|
165
|
+
attachment_urls: Optional[List[str]] = typer.Option(None, "--attachment-url", help="Attachment URLs to attach (v3 only, repeatable)"),
|
|
166
|
+
create_as_user_id: Optional[str] = typer.Option(None, "--create-as-user-id", help="Enterprise: create session on behalf of this user ID (v3 only)"),
|
|
159
167
|
force: bool = typer.Option(False, "--force", help="Force creation even if duplicate prompt is detected"),
|
|
160
168
|
wait: bool = typer.Option(False, "--wait", "-w", help="Block until the session reaches a terminal status"),
|
|
161
169
|
interval: int = typer.Option(5, "--interval", help="Polling interval in seconds when --wait is used"),
|
|
162
170
|
):
|
|
163
171
|
"""Create a new Devin session."""
|
|
164
172
|
if org: config.temporary_org_id = org
|
|
173
|
+
|
|
174
|
+
api_ver = config.api_version
|
|
175
|
+
console.print(f"[dim]Using profile: {config.active_profile or 'default'} ({api_ver})[/dim]")
|
|
176
|
+
|
|
177
|
+
v3_only_used = [x for x, v in [
|
|
178
|
+
("--advanced-mode", advanced_mode),
|
|
179
|
+
("--playbook-id", playbook_id),
|
|
180
|
+
("--repos", repos),
|
|
181
|
+
("--secret-ids", secret_ids),
|
|
182
|
+
("--session-links", session_links),
|
|
183
|
+
("--attachment-urls", attachment_urls),
|
|
184
|
+
("--create-as-user-id", create_as_user_id),
|
|
185
|
+
] if v]
|
|
186
|
+
if api_ver == "v1" and v3_only_used:
|
|
187
|
+
console.print(f"[bold yellow]Warning:[/bold yellow] The following flags are v3-only and will be ignored on a v1 profile: {', '.join(v3_only_used)}")
|
|
188
|
+
console.print(" Run [bold cyan]devin configure[/bold cyan] to switch to a v3 profile.")
|
|
189
|
+
|
|
165
190
|
if file:
|
|
166
191
|
prompt_text = file.read_text()
|
|
167
192
|
elif prompt:
|
|
@@ -169,10 +194,10 @@ def create_session_cmd(
|
|
|
169
194
|
else:
|
|
170
195
|
console.print("[bold red]Error:[/bold red] Must provide prompt or --file")
|
|
171
196
|
raise typer.Exit(1)
|
|
172
|
-
|
|
197
|
+
|
|
173
198
|
prompt_hash = hashlib.sha256(prompt_text.encode('utf-8')).hexdigest()
|
|
174
199
|
existing_sid = config.get_session_by_prompt_hash(prompt_hash)
|
|
175
|
-
|
|
200
|
+
|
|
176
201
|
if existing_sid and not force:
|
|
177
202
|
console.print(f"[bold yellow]Duplicate Detected:[/bold yellow] You recently created a session with this exact prompt.")
|
|
178
203
|
console.print(f"Existing Session ID: [bold cyan]{existing_sid}[/bold cyan]")
|
|
@@ -182,13 +207,21 @@ def create_session_cmd(
|
|
|
182
207
|
|
|
183
208
|
with console.status("[bold green]Creating session...[/bold green]"):
|
|
184
209
|
resp = sessions.create_session(
|
|
185
|
-
prompt=prompt_text,
|
|
186
|
-
title=title,
|
|
210
|
+
prompt=prompt_text,
|
|
211
|
+
title=title,
|
|
187
212
|
max_acu_limit=max_acu,
|
|
188
|
-
advanced_mode=advanced_mode
|
|
213
|
+
advanced_mode=advanced_mode,
|
|
214
|
+
playbook_id=playbook_id,
|
|
215
|
+
tags=tags or None,
|
|
216
|
+
repos=repos or None,
|
|
217
|
+
knowledge_ids=knowledge_ids or None,
|
|
218
|
+
secret_ids=secret_ids or None,
|
|
219
|
+
session_links=session_links or None,
|
|
220
|
+
attachment_urls=attachment_urls or None,
|
|
221
|
+
create_as_user_id=create_as_user_id,
|
|
189
222
|
)
|
|
190
223
|
sid = resp.get("session_id")
|
|
191
|
-
|
|
224
|
+
|
|
192
225
|
if "advanced_mode_url" in resp:
|
|
193
226
|
adv_url = resp["advanced_mode_url"]
|
|
194
227
|
console.print(f"[bold yellow]Advanced Mode Authorization Required![/bold yellow]")
|
|
@@ -197,7 +230,7 @@ def create_session_cmd(
|
|
|
197
230
|
if typer.confirm("Open browser now?"):
|
|
198
231
|
import webbrowser
|
|
199
232
|
webbrowser.open(adv_url)
|
|
200
|
-
|
|
233
|
+
|
|
201
234
|
if sid:
|
|
202
235
|
config.current_session_id = sid
|
|
203
236
|
config.save_prompt_hash(prompt_hash, sid)
|
|
@@ -219,6 +252,7 @@ def create_session_cmd(
|
|
|
219
252
|
else:
|
|
220
253
|
console.print("[yellow]Session created, but no ID returned immediately (awaiting advanced mode setup).[/yellow]")
|
|
221
254
|
|
|
255
|
+
|
|
222
256
|
@session_app.command("list")
|
|
223
257
|
@handle_api_error
|
|
224
258
|
def list_sessions_cmd(
|
|
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
|