devlaunch 0.0.8__tar.gz → 0.0.9__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.
- {devlaunch-0.0.8 → devlaunch-0.0.9}/PKG-INFO +41 -3
- {devlaunch-0.0.8 → devlaunch-0.0.9}/README.md +40 -2
- devlaunch-0.0.9/devlaunch/aid.py +212 -0
- {devlaunch-0.0.8 → devlaunch-0.0.9}/devlaunch/completion.py +1 -1
- {devlaunch-0.0.8 → devlaunch-0.0.9}/devlaunch/completions/dl.bash +17 -2
- {devlaunch-0.0.8 → devlaunch-0.0.9}/devlaunch/dl.py +17 -4
- {devlaunch-0.0.8 → devlaunch-0.0.9}/pyproject.toml +2 -1
- {devlaunch-0.0.8 → devlaunch-0.0.9}/.gitignore +0 -0
- {devlaunch-0.0.8 → devlaunch-0.0.9}/LICENSE +0 -0
- {devlaunch-0.0.8 → devlaunch-0.0.9}/devlaunch/__init__.py +0 -0
- {devlaunch-0.0.8 → devlaunch-0.0.9}/devlaunch/completion_loader.py +0 -0
- {devlaunch-0.0.8 → devlaunch-0.0.9}/devlaunch/completions/__init__.py +0 -0
- {devlaunch-0.0.8 → devlaunch-0.0.9}/devlaunch/gh_auth.py +0 -0
- {devlaunch-0.0.8 → devlaunch-0.0.9}/devlaunch/worktree/__init__.py +0 -0
- {devlaunch-0.0.8 → devlaunch-0.0.9}/devlaunch/worktree/branch_manager.py +0 -0
- {devlaunch-0.0.8 → devlaunch-0.0.9}/devlaunch/worktree/config.py +0 -0
- {devlaunch-0.0.8 → devlaunch-0.0.9}/devlaunch/worktree/models.py +0 -0
- {devlaunch-0.0.8 → devlaunch-0.0.9}/devlaunch/worktree/repo_manager.py +0 -0
- {devlaunch-0.0.8 → devlaunch-0.0.9}/devlaunch/worktree/storage.py +0 -0
- {devlaunch-0.0.8 → devlaunch-0.0.9}/devlaunch/worktree/workspace_clone.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devlaunch
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.9
|
|
4
4
|
Summary: DevLaunch - A streamlined CLI for devpod workspaces
|
|
5
5
|
Project-URL: Source, https://github.com/blooop/devlaunch
|
|
6
6
|
Project-URL: Home, https://github.com/blooop/devlaunch
|
|
@@ -33,7 +33,7 @@ A streamlined CLI for [devpod](https://devpod.sh) with intuitive autocomplete an
|
|
|
33
33
|
[](https://github.com/blooop/devlaunch/pulls?q=is%3Amerged)
|
|
34
34
|
[](https://GitHub.com/blooop/devlaunch/releases/)
|
|
35
35
|
[](https://pypi.org/project/devlaunch/)
|
|
36
|
-
[](https://prefix.dev/channels/blooop/packages/devlaunch)
|
|
37
37
|
[](https://opensource.org/license/mit/)
|
|
38
38
|
[](https://www.python.org/downloads/)
|
|
39
39
|
[](https://pixi.sh)
|
|
@@ -60,7 +60,7 @@ If `devpod` is not on `PATH`, every command that needs it prints a single instal
|
|
|
60
60
|
|
|
61
61
|
### Shell Completions
|
|
62
62
|
|
|
63
|
-
After installation, set up shell completions
|
|
63
|
+
After installation, set up shell completions for `dl` and `aid`:
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
66
|
dl --install
|
|
@@ -76,6 +76,44 @@ dl <user/repo> <cmd> # Run workspace command (stop, code, etc.)
|
|
|
76
76
|
dl <user/repo> -- <command> # Run shell command in workspace
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
## aid: start a coding agent in a workspace
|
|
80
|
+
|
|
81
|
+
`aid` is `dl` with a coding agent started for you:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
aid <user/repo>[@branch] [prompt...] # Open the workspace, start the agent
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
It is a shortcut, not a second launcher. `aid` rewrites its command line into a
|
|
88
|
+
`dl` one and hands it to `dl` itself, so
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
aid blooop/devlaunch@fix/42 fix the flaky test
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
is exactly
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
dl blooop/devlaunch@fix/42 -- claude 'fix the flaky test'
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
That means an `aid` workspace *is* the `dl` workspace: same clone, same workspace
|
|
101
|
+
id, same container — started if stopped, attached to if already running, and never
|
|
102
|
+
rebuilt just because `aid` asked for it. Anything `dl` learns, `aid` gets.
|
|
103
|
+
|
|
104
|
+
| Option | Description |
|
|
105
|
+
|--------|-------------|
|
|
106
|
+
| `--claude`, `--codex`, `--gemini` | Pick the agent (default: `claude`) |
|
|
107
|
+
| `--devcontainer <variant\|path>` | Passed through to `dl` |
|
|
108
|
+
| `DEVLAUNCH_AID_AGENT=<agent>` | Change the default agent |
|
|
109
|
+
|
|
110
|
+
Everything after the workspace is the prompt, flags and all, so it never needs
|
|
111
|
+
quoting to survive `aid`'s own parsing. Managing workspaces — listing, stopping,
|
|
112
|
+
deleting, VS Code — stays with `dl`.
|
|
113
|
+
|
|
114
|
+
The agent's CLI has to be installed in the container; `aid` runs it there, it does
|
|
115
|
+
not install it.
|
|
116
|
+
|
|
79
117
|
## Workspace Sources
|
|
80
118
|
|
|
81
119
|
```bash
|
|
@@ -10,7 +10,7 @@ A streamlined CLI for [devpod](https://devpod.sh) with intuitive autocomplete an
|
|
|
10
10
|
[](https://github.com/blooop/devlaunch/pulls?q=is%3Amerged)
|
|
11
11
|
[](https://GitHub.com/blooop/devlaunch/releases/)
|
|
12
12
|
[](https://pypi.org/project/devlaunch/)
|
|
13
|
-
[](https://prefix.dev/channels/blooop/packages/devlaunch)
|
|
14
14
|
[](https://opensource.org/license/mit/)
|
|
15
15
|
[](https://www.python.org/downloads/)
|
|
16
16
|
[](https://pixi.sh)
|
|
@@ -37,7 +37,7 @@ If `devpod` is not on `PATH`, every command that needs it prints a single instal
|
|
|
37
37
|
|
|
38
38
|
### Shell Completions
|
|
39
39
|
|
|
40
|
-
After installation, set up shell completions
|
|
40
|
+
After installation, set up shell completions for `dl` and `aid`:
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
43
|
dl --install
|
|
@@ -53,6 +53,44 @@ dl <user/repo> <cmd> # Run workspace command (stop, code, etc.)
|
|
|
53
53
|
dl <user/repo> -- <command> # Run shell command in workspace
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
## aid: start a coding agent in a workspace
|
|
57
|
+
|
|
58
|
+
`aid` is `dl` with a coding agent started for you:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
aid <user/repo>[@branch] [prompt...] # Open the workspace, start the agent
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
It is a shortcut, not a second launcher. `aid` rewrites its command line into a
|
|
65
|
+
`dl` one and hands it to `dl` itself, so
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
aid blooop/devlaunch@fix/42 fix the flaky test
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
is exactly
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
dl blooop/devlaunch@fix/42 -- claude 'fix the flaky test'
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
That means an `aid` workspace *is* the `dl` workspace: same clone, same workspace
|
|
78
|
+
id, same container — started if stopped, attached to if already running, and never
|
|
79
|
+
rebuilt just because `aid` asked for it. Anything `dl` learns, `aid` gets.
|
|
80
|
+
|
|
81
|
+
| Option | Description |
|
|
82
|
+
|--------|-------------|
|
|
83
|
+
| `--claude`, `--codex`, `--gemini` | Pick the agent (default: `claude`) |
|
|
84
|
+
| `--devcontainer <variant\|path>` | Passed through to `dl` |
|
|
85
|
+
| `DEVLAUNCH_AID_AGENT=<agent>` | Change the default agent |
|
|
86
|
+
|
|
87
|
+
Everything after the workspace is the prompt, flags and all, so it never needs
|
|
88
|
+
quoting to survive `aid`'s own parsing. Managing workspaces — listing, stopping,
|
|
89
|
+
deleting, VS Code — stays with `dl`.
|
|
90
|
+
|
|
91
|
+
The agent's CLI has to be installed in the container; `aid` runs it there, it does
|
|
92
|
+
not install it.
|
|
93
|
+
|
|
56
94
|
## Workspace Sources
|
|
57
95
|
|
|
58
96
|
```bash
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""aid - AI Develop: `dl`, with a coding agent started for you.
|
|
2
|
+
|
|
3
|
+
aid is a shortcut, not a second launcher. It rewrites its own command line into
|
|
4
|
+
a `dl` one and hands that to :func:`devlaunch.dl.main`, so:
|
|
5
|
+
|
|
6
|
+
aid owner/repo@branch fix the flaky test
|
|
7
|
+
|
|
8
|
+
is exactly
|
|
9
|
+
|
|
10
|
+
dl owner/repo@branch -- claude 'fix the flaky test'
|
|
11
|
+
|
|
12
|
+
Everything that decides how a workspace is obtained — the bare repo cache, the
|
|
13
|
+
worktree clone, the workspace id, the devpod container, the fast attach to one
|
|
14
|
+
that is already running, the forwarded gh login — happens inside dl, once. There
|
|
15
|
+
is no container machinery in this module, deliberately: an aid that built its own
|
|
16
|
+
would drift from dl and start rebuilding containers dl would have reused.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import logging
|
|
22
|
+
import os
|
|
23
|
+
import shlex
|
|
24
|
+
import sys
|
|
25
|
+
from dataclasses import dataclass, field
|
|
26
|
+
from typing import Dict, List, Optional
|
|
27
|
+
|
|
28
|
+
from . import dl
|
|
29
|
+
|
|
30
|
+
# Base command per agent. The prompt, when there is one, is appended as a single
|
|
31
|
+
# quoted argument; each of these CLIs takes an initial prompt that way and then
|
|
32
|
+
# drops into its interactive session.
|
|
33
|
+
AGENT_COMMANDS: Dict[str, List[str]] = {
|
|
34
|
+
"claude": ["claude"],
|
|
35
|
+
"codex": ["codex"],
|
|
36
|
+
"gemini": ["gemini", "--prompt-interactive"],
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# Flags that pick the agent, e.g. `aid --gemini owner/repo ...`.
|
|
40
|
+
AGENT_FLAGS: Dict[str, str] = {f"--{name}": name for name in AGENT_COMMANDS}
|
|
41
|
+
|
|
42
|
+
DEFAULT_AGENT = "claude"
|
|
43
|
+
|
|
44
|
+
# Overrides the default agent for people who do not want to type a flag every
|
|
45
|
+
# time. A --flag on the command line still wins.
|
|
46
|
+
AGENT_ENV_VAR = "DEVLAUNCH_AID_AGENT"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class UsageError(Exception):
|
|
50
|
+
"""The aid command line could not be understood."""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(frozen=True)
|
|
54
|
+
class AidArgs:
|
|
55
|
+
"""An aid command line, split into the pieces the dl one is built from.
|
|
56
|
+
|
|
57
|
+
Only ever built by parse_aid_args, which refuses a command line without a
|
|
58
|
+
workspace, so spec is a str here and every field is ready to use.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
spec: str
|
|
62
|
+
agent: str = DEFAULT_AGENT
|
|
63
|
+
# dl options seen before the spec (`--devcontainer x`), passed through as-is.
|
|
64
|
+
dl_options: List[str] = field(default_factory=list)
|
|
65
|
+
prompt: str = ""
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def default_agent(env: Optional[Dict[str, str]] = None) -> str:
|
|
69
|
+
"""Return the agent to use when no flag picks one."""
|
|
70
|
+
environ = os.environ if env is None else env
|
|
71
|
+
name = environ.get(AGENT_ENV_VAR, "").strip()
|
|
72
|
+
if not name:
|
|
73
|
+
return DEFAULT_AGENT
|
|
74
|
+
if name not in AGENT_COMMANDS:
|
|
75
|
+
raise UsageError(
|
|
76
|
+
f"{AGENT_ENV_VAR}={name!r} is not a known agent. "
|
|
77
|
+
f"Choose one of: {', '.join(sorted(AGENT_COMMANDS))}."
|
|
78
|
+
)
|
|
79
|
+
return name
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def parse_aid_args(argv: List[str], env: Optional[Dict[str, str]] = None) -> AidArgs:
|
|
83
|
+
"""Split an aid command line into agent, dl options, workspace spec and prompt.
|
|
84
|
+
|
|
85
|
+
The first argument that is neither an agent flag nor a dl option is the
|
|
86
|
+
workspace spec; everything after it is the prompt, flags and all, so a
|
|
87
|
+
prompt never has to be quoted to protect it from aid's own parsing.
|
|
88
|
+
"""
|
|
89
|
+
agent = default_agent(env)
|
|
90
|
+
dl_options: List[str] = []
|
|
91
|
+
spec: Optional[str] = None
|
|
92
|
+
i = 0
|
|
93
|
+
while i < len(argv):
|
|
94
|
+
arg = argv[i]
|
|
95
|
+
if arg in AGENT_FLAGS:
|
|
96
|
+
agent = AGENT_FLAGS[arg]
|
|
97
|
+
i += 1
|
|
98
|
+
continue
|
|
99
|
+
if arg in dl.DL_VALUE_OPTIONS:
|
|
100
|
+
# Take the value with it; dl reports a missing one.
|
|
101
|
+
dl_options.extend(argv[i : i + 2])
|
|
102
|
+
i += 2
|
|
103
|
+
continue
|
|
104
|
+
if arg.startswith("-"):
|
|
105
|
+
dl_options.append(arg)
|
|
106
|
+
i += 1
|
|
107
|
+
continue
|
|
108
|
+
spec = arg
|
|
109
|
+
i += 1
|
|
110
|
+
break
|
|
111
|
+
|
|
112
|
+
if spec is None:
|
|
113
|
+
raise UsageError("aid needs a workspace: aid <user/repo>[@branch] [prompt]")
|
|
114
|
+
|
|
115
|
+
return AidArgs(spec=spec, agent=agent, dl_options=dl_options, prompt=" ".join(argv[i:]))
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def build_agent_command(agent: str, prompt: str = "") -> str:
|
|
119
|
+
"""Build the shell command that starts the agent inside the workspace.
|
|
120
|
+
|
|
121
|
+
Returned as one shell string because that is what dl's `-- <command>` form
|
|
122
|
+
takes. The prompt is quoted here rather than reassembled by the caller, so
|
|
123
|
+
the words the user typed reach the agent as the single argument they meant.
|
|
124
|
+
"""
|
|
125
|
+
try:
|
|
126
|
+
command = list(AGENT_COMMANDS[agent])
|
|
127
|
+
except KeyError:
|
|
128
|
+
raise UsageError(
|
|
129
|
+
f"Unknown agent {agent!r}. Choose one of: {', '.join(sorted(AGENT_COMMANDS))}."
|
|
130
|
+
) from None
|
|
131
|
+
if not prompt:
|
|
132
|
+
# No prompt to be interactive about: start the agent's plain session.
|
|
133
|
+
# gemini's --prompt-interactive would be a syntax error without one.
|
|
134
|
+
return shlex.quote(command[0])
|
|
135
|
+
command.append(prompt)
|
|
136
|
+
return shlex.join(command)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def build_dl_args(parsed: AidArgs) -> List[str]:
|
|
140
|
+
"""Turn a parsed aid command line into the dl one that does the work."""
|
|
141
|
+
return [
|
|
142
|
+
*parsed.dl_options,
|
|
143
|
+
parsed.spec,
|
|
144
|
+
"--",
|
|
145
|
+
build_agent_command(parsed.agent, parsed.prompt),
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def print_help() -> None:
|
|
150
|
+
"""Print usage help."""
|
|
151
|
+
agents = ", ".join(f"--{name}" for name in sorted(AGENT_COMMANDS))
|
|
152
|
+
print(
|
|
153
|
+
f"""aid - AI Develop: start a coding agent in a devlaunch workspace
|
|
154
|
+
|
|
155
|
+
aid is a shortcut for `dl <workspace> -- <agent> '<prompt>'`. The workspace is
|
|
156
|
+
opened by dl itself, so it is the same workspace, container and clone that
|
|
157
|
+
`dl <workspace>` gives you — started if it is stopped, attached to if it is
|
|
158
|
+
already running, and never rebuilt just because aid asked for it.
|
|
159
|
+
|
|
160
|
+
Usage:
|
|
161
|
+
aid <user/repo>[@branch] [prompt...] Open the workspace and start the agent
|
|
162
|
+
aid <workspace> [prompt...] Same, for an existing workspace or ./path
|
|
163
|
+
|
|
164
|
+
Options:
|
|
165
|
+
{agents}
|
|
166
|
+
Pick the agent (default: {DEFAULT_AGENT})
|
|
167
|
+
--devcontainer <variant|path> Passed through to dl
|
|
168
|
+
--help, -h Show this help
|
|
169
|
+
--version Show version
|
|
170
|
+
|
|
171
|
+
Environment:
|
|
172
|
+
{AGENT_ENV_VAR}=<agent> Change the default agent
|
|
173
|
+
|
|
174
|
+
Examples:
|
|
175
|
+
aid blooop/devlaunch # Start {DEFAULT_AGENT} in the workspace
|
|
176
|
+
aid blooop/devlaunch@fix/42 fix the bug # Open the branch, hand over the prompt
|
|
177
|
+
aid --gemini ./my-project explain this # Pick a different agent
|
|
178
|
+
|
|
179
|
+
Everything else — listing, stopping, deleting, VS Code — is dl's job:
|
|
180
|
+
dl --help
|
|
181
|
+
"""
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def main(argv: Optional[List[str]] = None) -> int:
|
|
186
|
+
"""Entry point for the aid command."""
|
|
187
|
+
args = sys.argv[1:] if argv is None else list(argv)
|
|
188
|
+
|
|
189
|
+
if not args or args[0] in ("--help", "-h"):
|
|
190
|
+
print_help()
|
|
191
|
+
return 0 if args else 1
|
|
192
|
+
|
|
193
|
+
if args[0] == "--version":
|
|
194
|
+
print(f"aid {dl.get_version()}")
|
|
195
|
+
return 0
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
parsed = parse_aid_args(args)
|
|
199
|
+
dl_args = build_dl_args(parsed)
|
|
200
|
+
except UsageError as e:
|
|
201
|
+
logging.error(str(e))
|
|
202
|
+
return 1
|
|
203
|
+
|
|
204
|
+
logging.info("aid -> dl %s", shlex.join(dl_args))
|
|
205
|
+
return dl.main(dl_args)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
if __name__ == "__main__":
|
|
209
|
+
try:
|
|
210
|
+
sys.exit(main())
|
|
211
|
+
except KeyboardInterrupt:
|
|
212
|
+
sys.exit(130)
|
|
@@ -34,7 +34,7 @@ def _completion_file_path() -> pathlib.Path:
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
def install_completions(rc_path: Optional[pathlib.Path] = None) -> int:
|
|
37
|
-
"""Install or refresh completion scripts for dl."""
|
|
37
|
+
"""Install or refresh completion scripts for dl and aid."""
|
|
38
38
|
completion_path = _completion_file_path().expanduser()
|
|
39
39
|
rc_target = (rc_path if rc_path is not None else pathlib.Path.home() / ".bashrc").expanduser()
|
|
40
40
|
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
# because temporary COMP_WORDBREAKS modification can have side effects with bash's
|
|
9
9
|
# internal completion state and doesn't reliably prevent word splitting in all
|
|
10
10
|
# bash versions. Direct parsing gives us full control over word boundaries.
|
|
11
|
+
#
|
|
12
|
+
# The same function serves `aid`, whose first argument is a dl workspace spec
|
|
13
|
+
# too. Only the flag list and what follows the spec differ, so the two places
|
|
14
|
+
# that care branch on $cmd rather than the script being copied for aid.
|
|
11
15
|
_dl_completion() {
|
|
12
16
|
local cur prev opts
|
|
13
17
|
COMPREPLY=()
|
|
@@ -44,8 +48,17 @@ _dl_completion() {
|
|
|
44
48
|
fi
|
|
45
49
|
fi
|
|
46
50
|
|
|
51
|
+
# The command being completed: dl or aid.
|
|
52
|
+
local cmd=""
|
|
53
|
+
if (( ${#words[@]} > 0 )); then
|
|
54
|
+
cmd="${words[0]##*/}"
|
|
55
|
+
fi
|
|
56
|
+
|
|
47
57
|
# Global command options (only valid as first arg)
|
|
48
58
|
local global_opts="--ls --install --help -h --version --devcontainer"
|
|
59
|
+
if [[ "$cmd" == aid ]]; then
|
|
60
|
+
global_opts="--claude --codex --gemini --devcontainer --help -h --version"
|
|
61
|
+
fi
|
|
49
62
|
|
|
50
63
|
# Workspace subcommands
|
|
51
64
|
local ws_cmds="stop rm code restart recreate reset --"
|
|
@@ -133,8 +146,9 @@ _dl_completion() {
|
|
|
133
146
|
return 0
|
|
134
147
|
fi
|
|
135
148
|
|
|
136
|
-
# Second argument (after workspace): subcommands
|
|
137
|
-
|
|
149
|
+
# Second argument (after workspace): subcommands. Everything after an aid
|
|
150
|
+
# workspace is the prompt, so there is nothing to offer there.
|
|
151
|
+
if [[ ${word_count} -eq 3 && "$cmd" != aid ]]; then
|
|
138
152
|
# Don't complete after global flags
|
|
139
153
|
# Extract the first argument (word after "dl") from the words array
|
|
140
154
|
local first=""
|
|
@@ -155,4 +169,5 @@ _dl_completion() {
|
|
|
155
169
|
|
|
156
170
|
# Use -o default for better completion behavior
|
|
157
171
|
complete -o default -F _dl_completion dl
|
|
172
|
+
complete -o default -F _dl_completion aid
|
|
158
173
|
# end dl completion
|
|
@@ -337,6 +337,12 @@ def _resolve_devcontainer_ref(ref: str) -> str:
|
|
|
337
337
|
return f".devcontainer/{ref}/devcontainer.json"
|
|
338
338
|
|
|
339
339
|
|
|
340
|
+
# dl options whose value is a separate argument. aid splits its own command line
|
|
341
|
+
# before handing it to dl and has to tell such a value from the workspace spec,
|
|
342
|
+
# so the list lives here, next to the parsing it describes.
|
|
343
|
+
DL_VALUE_OPTIONS = frozenset({"--devcontainer"})
|
|
344
|
+
|
|
345
|
+
|
|
340
346
|
def extract_devcontainer_flag(args: List[str]) -> tuple[List[str], Optional[str]]:
|
|
341
347
|
"""Pull `--devcontainer <name-or-path>` out of the argument list.
|
|
342
348
|
|
|
@@ -1003,24 +1009,31 @@ def _get_clone_manager() -> WorkspaceCloneManager:
|
|
|
1003
1009
|
return _cache["clone_manager"]
|
|
1004
1010
|
|
|
1005
1011
|
|
|
1006
|
-
def main() -> int:
|
|
1012
|
+
def main(argv: Optional[List[str]] = None) -> int:
|
|
1007
1013
|
"""Main entry point for dl CLI.
|
|
1008
1014
|
|
|
1009
1015
|
Thin wrapper so there is exactly one handler for a missing devpod, however
|
|
1010
1016
|
deep in the command it was noticed. The message goes to stderr because
|
|
1011
1017
|
stdout is parsed by the completion machinery (--repos, --completion-data).
|
|
1018
|
+
|
|
1019
|
+
argv is the argument list without the program name, defaulting to the real
|
|
1020
|
+
one. It is a parameter so that a sibling entry point can hand dl a command
|
|
1021
|
+
line it built and get dl's behaviour itself rather than a second copy of it
|
|
1022
|
+
(see aid.py) — a copy is what left aid rebuilding containers dl reuses.
|
|
1012
1023
|
"""
|
|
1013
1024
|
try:
|
|
1014
|
-
return _run_cli()
|
|
1025
|
+
return _run_cli(argv)
|
|
1015
1026
|
except DevpodNotInstalled as e:
|
|
1016
1027
|
print(e, file=sys.stderr)
|
|
1017
1028
|
return DEVPOD_MISSING_EXIT_CODE
|
|
1018
1029
|
|
|
1019
1030
|
|
|
1020
|
-
def _run_cli() -> int:
|
|
1031
|
+
def _run_cli(argv: Optional[List[str]] = None) -> int:
|
|
1021
1032
|
"""Dispatch a dl command line. See main() for the error handling around it."""
|
|
1033
|
+
if argv is None:
|
|
1034
|
+
argv = sys.argv[1:]
|
|
1022
1035
|
try:
|
|
1023
|
-
args, devcontainer = extract_devcontainer_flag(
|
|
1036
|
+
args, devcontainer = extract_devcontainer_flag(argv)
|
|
1024
1037
|
except ValueError as e:
|
|
1025
1038
|
logging.error(str(e))
|
|
1026
1039
|
return 1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "devlaunch"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.9"
|
|
4
4
|
authors = [{ name = "Austin Gregg-Smith", email = "blooop@gmail.com" }]
|
|
5
5
|
description = "DevLaunch - A streamlined CLI for devpod workspaces"
|
|
6
6
|
readme = "README.md"
|
|
@@ -59,6 +59,7 @@ include = ["devlaunch"]
|
|
|
59
59
|
|
|
60
60
|
[project.scripts]
|
|
61
61
|
dl = "devlaunch.dl:main"
|
|
62
|
+
aid = "devlaunch.aid:main"
|
|
62
63
|
|
|
63
64
|
# Environments
|
|
64
65
|
[tool.pixi.environments]
|
|
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
|