lv24-opencode-consilium 1.10.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.
- lv24_opencode_consilium-1.10.0/LICENSE +21 -0
- lv24_opencode_consilium-1.10.0/PKG-INFO +99 -0
- lv24_opencode_consilium-1.10.0/README.md +82 -0
- lv24_opencode_consilium-1.10.0/pyproject.toml +32 -0
- lv24_opencode_consilium-1.10.0/setup.cfg +4 -0
- lv24_opencode_consilium-1.10.0/src/lv24_opencode_consilium/__init__.py +5 -0
- lv24_opencode_consilium-1.10.0/src/lv24_opencode_consilium/__main__.py +6 -0
- lv24_opencode_consilium-1.10.0/src/lv24_opencode_consilium/cli.py +52 -0
- lv24_opencode_consilium-1.10.0/src/lv24_opencode_consilium.egg-info/PKG-INFO +99 -0
- lv24_opencode_consilium-1.10.0/src/lv24_opencode_consilium.egg-info/SOURCES.txt +12 -0
- lv24_opencode_consilium-1.10.0/src/lv24_opencode_consilium.egg-info/dependency_links.txt +1 -0
- lv24_opencode_consilium-1.10.0/src/lv24_opencode_consilium.egg-info/entry_points.txt +3 -0
- lv24_opencode_consilium-1.10.0/src/lv24_opencode_consilium.egg-info/top_level.txt +1 -0
- lv24_opencode_consilium-1.10.0/tests/test_cli.py +185 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LevapVeeskela
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lv24-opencode-consilium
|
|
3
|
+
Version: 1.10.0
|
|
4
|
+
Summary: Python launcher for the lv24-opencode-consilium (Consilium) Node.js CLI
|
|
5
|
+
Author: LevapVeeskela
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/LevapVeeskela/lv24-opencode-consilium
|
|
8
|
+
Project-URL: Repository, https://github.com/LevapVeeskela/lv24-opencode-consilium
|
|
9
|
+
Project-URL: Issues, https://github.com/LevapVeeskela/lv24-opencode-consilium/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/LevapVeeskela/lv24-opencode-consilium/tree/master/docs
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# lv24-opencode-consilium (Python launcher)
|
|
19
|
+
|
|
20
|
+
A thin Python launcher for **Consilium**, the multi-agent review and audit CLI
|
|
21
|
+
for the [OpenCode](https://opencode.ai/) ecosystem. Consilium itself is a
|
|
22
|
+
Node.js command-line tool published on npm as
|
|
23
|
+
[`lv24-opencode-consilium`](https://www.npmjs.com/package/lv24-opencode-consilium).
|
|
24
|
+
This package does not reimplement Consilium and does not bundle Node.js: the
|
|
25
|
+
`consilium` command delegates to the npm package through `npx`.
|
|
26
|
+
|
|
27
|
+
The npm package remains the canonical distribution. The PyPI package only
|
|
28
|
+
contains this launcher.
|
|
29
|
+
|
|
30
|
+
## Prerequisites
|
|
31
|
+
|
|
32
|
+
- **Node.js 18 or newer**, including npm and npx
|
|
33
|
+
([nodejs.org](https://nodejs.org/)).
|
|
34
|
+
- The **OpenCode CLI** installed and available on `PATH`
|
|
35
|
+
([OpenCode docs](https://opencode.ai/docs/)).
|
|
36
|
+
- Network access on first run, or an npm cache that already contains the
|
|
37
|
+
requested package, because `npx` may download it.
|
|
38
|
+
- Python 3.9 or newer.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install lv24-opencode-consilium
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This installs two equivalent console scripts:
|
|
47
|
+
|
|
48
|
+
- `consilium`
|
|
49
|
+
- `lv24-opencode-consilium`
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
consilium --help
|
|
55
|
+
consilium "review this repository"
|
|
56
|
+
consilium review --model opencode/gpt-5
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
All arguments are forwarded unchanged to the npm CLI. The child process
|
|
60
|
+
inherits the terminal, environment, and exit code. You can also run:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
python -m lv24_opencode_consilium --help
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
If `npx` cannot be found, the launcher exits with code `127` and prints an
|
|
67
|
+
installation hint. An interrupted run exits with code `130`.
|
|
68
|
+
|
|
69
|
+
## Version pinning
|
|
70
|
+
|
|
71
|
+
By default the launcher runs the npm package version matching its own version
|
|
72
|
+
(currently `1.10.0`). Override it with `CONSILIUM_NPM_VERSION` to use any npm
|
|
73
|
+
version or tag:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
CONSILIUM_NPM_VERSION=latest consilium --help
|
|
77
|
+
CONSILIUM_NPM_VERSION=1.9.0 consilium --help
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
PowerShell:
|
|
81
|
+
|
|
82
|
+
```powershell
|
|
83
|
+
$env:CONSILIUM_NPM_VERSION = "latest"; consilium --help
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Links
|
|
87
|
+
|
|
88
|
+
- Main repository and documentation:
|
|
89
|
+
<https://github.com/LevapVeeskela/lv24-opencode-consilium>
|
|
90
|
+
- Documentation:
|
|
91
|
+
<https://github.com/LevapVeeskela/lv24-opencode-consilium/tree/master/docs>
|
|
92
|
+
- Issues:
|
|
93
|
+
<https://github.com/LevapVeeskela/lv24-opencode-consilium/issues>
|
|
94
|
+
- npm package:
|
|
95
|
+
<https://www.npmjs.com/package/lv24-opencode-consilium>
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
MIT
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# lv24-opencode-consilium (Python launcher)
|
|
2
|
+
|
|
3
|
+
A thin Python launcher for **Consilium**, the multi-agent review and audit CLI
|
|
4
|
+
for the [OpenCode](https://opencode.ai/) ecosystem. Consilium itself is a
|
|
5
|
+
Node.js command-line tool published on npm as
|
|
6
|
+
[`lv24-opencode-consilium`](https://www.npmjs.com/package/lv24-opencode-consilium).
|
|
7
|
+
This package does not reimplement Consilium and does not bundle Node.js: the
|
|
8
|
+
`consilium` command delegates to the npm package through `npx`.
|
|
9
|
+
|
|
10
|
+
The npm package remains the canonical distribution. The PyPI package only
|
|
11
|
+
contains this launcher.
|
|
12
|
+
|
|
13
|
+
## Prerequisites
|
|
14
|
+
|
|
15
|
+
- **Node.js 18 or newer**, including npm and npx
|
|
16
|
+
([nodejs.org](https://nodejs.org/)).
|
|
17
|
+
- The **OpenCode CLI** installed and available on `PATH`
|
|
18
|
+
([OpenCode docs](https://opencode.ai/docs/)).
|
|
19
|
+
- Network access on first run, or an npm cache that already contains the
|
|
20
|
+
requested package, because `npx` may download it.
|
|
21
|
+
- Python 3.9 or newer.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install lv24-opencode-consilium
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This installs two equivalent console scripts:
|
|
30
|
+
|
|
31
|
+
- `consilium`
|
|
32
|
+
- `lv24-opencode-consilium`
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
consilium --help
|
|
38
|
+
consilium "review this repository"
|
|
39
|
+
consilium review --model opencode/gpt-5
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
All arguments are forwarded unchanged to the npm CLI. The child process
|
|
43
|
+
inherits the terminal, environment, and exit code. You can also run:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
python -m lv24_opencode_consilium --help
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
If `npx` cannot be found, the launcher exits with code `127` and prints an
|
|
50
|
+
installation hint. An interrupted run exits with code `130`.
|
|
51
|
+
|
|
52
|
+
## Version pinning
|
|
53
|
+
|
|
54
|
+
By default the launcher runs the npm package version matching its own version
|
|
55
|
+
(currently `1.10.0`). Override it with `CONSILIUM_NPM_VERSION` to use any npm
|
|
56
|
+
version or tag:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
CONSILIUM_NPM_VERSION=latest consilium --help
|
|
60
|
+
CONSILIUM_NPM_VERSION=1.9.0 consilium --help
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
PowerShell:
|
|
64
|
+
|
|
65
|
+
```powershell
|
|
66
|
+
$env:CONSILIUM_NPM_VERSION = "latest"; consilium --help
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Links
|
|
70
|
+
|
|
71
|
+
- Main repository and documentation:
|
|
72
|
+
<https://github.com/LevapVeeskela/lv24-opencode-consilium>
|
|
73
|
+
- Documentation:
|
|
74
|
+
<https://github.com/LevapVeeskela/lv24-opencode-consilium/tree/master/docs>
|
|
75
|
+
- Issues:
|
|
76
|
+
<https://github.com/LevapVeeskela/lv24-opencode-consilium/issues>
|
|
77
|
+
- npm package:
|
|
78
|
+
<https://www.npmjs.com/package/lv24-opencode-consilium>
|
|
79
|
+
|
|
80
|
+
## License
|
|
81
|
+
|
|
82
|
+
MIT
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "lv24-opencode-consilium"
|
|
7
|
+
version = "1.10.0"
|
|
8
|
+
description = "Python launcher for the lv24-opencode-consilium (Consilium) Node.js CLI"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "LevapVeeskela" },
|
|
15
|
+
]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[project.urls]
|
|
22
|
+
Homepage = "https://github.com/LevapVeeskela/lv24-opencode-consilium"
|
|
23
|
+
Repository = "https://github.com/LevapVeeskela/lv24-opencode-consilium"
|
|
24
|
+
Issues = "https://github.com/LevapVeeskela/lv24-opencode-consilium/issues"
|
|
25
|
+
Documentation = "https://github.com/LevapVeeskela/lv24-opencode-consilium/tree/master/docs"
|
|
26
|
+
|
|
27
|
+
[project.scripts]
|
|
28
|
+
consilium = "lv24_opencode_consilium.cli:main"
|
|
29
|
+
lv24-opencode-consilium = "lv24_opencode_consilium.cli:main"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["src"]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Command-line entry point that delegates to the npm Consilium CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Optional, Sequence
|
|
10
|
+
|
|
11
|
+
from . import __version__
|
|
12
|
+
|
|
13
|
+
NPM_PACKAGE = "lv24-opencode-consilium"
|
|
14
|
+
NPM_VERSION_ENV = "CONSILIUM_NPM_VERSION"
|
|
15
|
+
|
|
16
|
+
_NPX_MISSING_MESSAGE = """\
|
|
17
|
+
consilium: npx was not found on PATH.
|
|
18
|
+
|
|
19
|
+
This package is a Python launcher for the Node.js Consilium CLI; it does not
|
|
20
|
+
bundle Node.js. Install Node.js 18 or newer (npm and npx are included) from
|
|
21
|
+
https://nodejs.org/ and make sure the OpenCode CLI is installed and available:
|
|
22
|
+
https://opencode.ai/docs/
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def npm_spec() -> str:
|
|
27
|
+
"""Return the npm package spec, honoring CONSILIUM_NPM_VERSION."""
|
|
28
|
+
version = os.environ.get(NPM_VERSION_ENV, "").strip() or __version__
|
|
29
|
+
return "{0}@{1}".format(NPM_PACKAGE, version)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
33
|
+
"""Run the npm CLI through npx and return its exit code."""
|
|
34
|
+
args = list(sys.argv[1:] if argv is None else argv)
|
|
35
|
+
|
|
36
|
+
npx = shutil.which("npx")
|
|
37
|
+
if npx is None:
|
|
38
|
+
sys.stderr.write(_NPX_MISSING_MESSAGE)
|
|
39
|
+
return 127
|
|
40
|
+
|
|
41
|
+
command = [npx, "--yes", npm_spec(), *args]
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
completed = subprocess.run(command, check=False)
|
|
45
|
+
return completed.returncode
|
|
46
|
+
except KeyboardInterrupt:
|
|
47
|
+
return 130
|
|
48
|
+
except OSError as exc:
|
|
49
|
+
sys.stderr.write(
|
|
50
|
+
"consilium: failed to start {0}: {1}\n".format(npx, exc)
|
|
51
|
+
)
|
|
52
|
+
return 127
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lv24-opencode-consilium
|
|
3
|
+
Version: 1.10.0
|
|
4
|
+
Summary: Python launcher for the lv24-opencode-consilium (Consilium) Node.js CLI
|
|
5
|
+
Author: LevapVeeskela
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/LevapVeeskela/lv24-opencode-consilium
|
|
8
|
+
Project-URL: Repository, https://github.com/LevapVeeskela/lv24-opencode-consilium
|
|
9
|
+
Project-URL: Issues, https://github.com/LevapVeeskela/lv24-opencode-consilium/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/LevapVeeskela/lv24-opencode-consilium/tree/master/docs
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# lv24-opencode-consilium (Python launcher)
|
|
19
|
+
|
|
20
|
+
A thin Python launcher for **Consilium**, the multi-agent review and audit CLI
|
|
21
|
+
for the [OpenCode](https://opencode.ai/) ecosystem. Consilium itself is a
|
|
22
|
+
Node.js command-line tool published on npm as
|
|
23
|
+
[`lv24-opencode-consilium`](https://www.npmjs.com/package/lv24-opencode-consilium).
|
|
24
|
+
This package does not reimplement Consilium and does not bundle Node.js: the
|
|
25
|
+
`consilium` command delegates to the npm package through `npx`.
|
|
26
|
+
|
|
27
|
+
The npm package remains the canonical distribution. The PyPI package only
|
|
28
|
+
contains this launcher.
|
|
29
|
+
|
|
30
|
+
## Prerequisites
|
|
31
|
+
|
|
32
|
+
- **Node.js 18 or newer**, including npm and npx
|
|
33
|
+
([nodejs.org](https://nodejs.org/)).
|
|
34
|
+
- The **OpenCode CLI** installed and available on `PATH`
|
|
35
|
+
([OpenCode docs](https://opencode.ai/docs/)).
|
|
36
|
+
- Network access on first run, or an npm cache that already contains the
|
|
37
|
+
requested package, because `npx` may download it.
|
|
38
|
+
- Python 3.9 or newer.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install lv24-opencode-consilium
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This installs two equivalent console scripts:
|
|
47
|
+
|
|
48
|
+
- `consilium`
|
|
49
|
+
- `lv24-opencode-consilium`
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
consilium --help
|
|
55
|
+
consilium "review this repository"
|
|
56
|
+
consilium review --model opencode/gpt-5
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
All arguments are forwarded unchanged to the npm CLI. The child process
|
|
60
|
+
inherits the terminal, environment, and exit code. You can also run:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
python -m lv24_opencode_consilium --help
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
If `npx` cannot be found, the launcher exits with code `127` and prints an
|
|
67
|
+
installation hint. An interrupted run exits with code `130`.
|
|
68
|
+
|
|
69
|
+
## Version pinning
|
|
70
|
+
|
|
71
|
+
By default the launcher runs the npm package version matching its own version
|
|
72
|
+
(currently `1.10.0`). Override it with `CONSILIUM_NPM_VERSION` to use any npm
|
|
73
|
+
version or tag:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
CONSILIUM_NPM_VERSION=latest consilium --help
|
|
77
|
+
CONSILIUM_NPM_VERSION=1.9.0 consilium --help
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
PowerShell:
|
|
81
|
+
|
|
82
|
+
```powershell
|
|
83
|
+
$env:CONSILIUM_NPM_VERSION = "latest"; consilium --help
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Links
|
|
87
|
+
|
|
88
|
+
- Main repository and documentation:
|
|
89
|
+
<https://github.com/LevapVeeskela/lv24-opencode-consilium>
|
|
90
|
+
- Documentation:
|
|
91
|
+
<https://github.com/LevapVeeskela/lv24-opencode-consilium/tree/master/docs>
|
|
92
|
+
- Issues:
|
|
93
|
+
<https://github.com/LevapVeeskela/lv24-opencode-consilium/issues>
|
|
94
|
+
- npm package:
|
|
95
|
+
<https://www.npmjs.com/package/lv24-opencode-consilium>
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/lv24_opencode_consilium/__init__.py
|
|
5
|
+
src/lv24_opencode_consilium/__main__.py
|
|
6
|
+
src/lv24_opencode_consilium/cli.py
|
|
7
|
+
src/lv24_opencode_consilium.egg-info/PKG-INFO
|
|
8
|
+
src/lv24_opencode_consilium.egg-info/SOURCES.txt
|
|
9
|
+
src/lv24_opencode_consilium.egg-info/dependency_links.txt
|
|
10
|
+
src/lv24_opencode_consilium.egg-info/entry_points.txt
|
|
11
|
+
src/lv24_opencode_consilium.egg-info/top_level.txt
|
|
12
|
+
tests/test_cli.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
lv24_opencode_consilium
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Tests for the Python launcher that delegates to the npm CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import contextlib
|
|
6
|
+
import io
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import stat
|
|
10
|
+
import sys
|
|
11
|
+
import tempfile
|
|
12
|
+
import unittest
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from unittest import mock
|
|
15
|
+
|
|
16
|
+
SRC_DIR = Path(__file__).resolve().parents[1] / "src"
|
|
17
|
+
if str(SRC_DIR) not in sys.path:
|
|
18
|
+
sys.path.insert(0, str(SRC_DIR))
|
|
19
|
+
|
|
20
|
+
from lv24_opencode_consilium import __version__
|
|
21
|
+
from lv24_opencode_consilium.cli import NPM_PACKAGE, NPM_VERSION_ENV, main
|
|
22
|
+
|
|
23
|
+
_FAKE_NPX = '''\
|
|
24
|
+
import json
|
|
25
|
+
import os
|
|
26
|
+
import sys
|
|
27
|
+
|
|
28
|
+
record = {
|
|
29
|
+
"argv": sys.argv[1:],
|
|
30
|
+
"env": {
|
|
31
|
+
key: value
|
|
32
|
+
for key, value in os.environ.items()
|
|
33
|
+
if key.startswith("CONSILIUM_") or key == "PATH"
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
with open(os.environ["CONSILIUM_TEST_LOG"], "a", encoding="utf-8") as handle:
|
|
37
|
+
handle.write(json.dumps(record, sort_keys=True) + "\\n")
|
|
38
|
+
raise SystemExit(3)
|
|
39
|
+
'''
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class MainTests(unittest.TestCase):
|
|
43
|
+
def setUp(self):
|
|
44
|
+
self._tempdir = tempfile.TemporaryDirectory()
|
|
45
|
+
self.addCleanup(self._tempdir.cleanup)
|
|
46
|
+
self.root = Path(self._tempdir.name)
|
|
47
|
+
self.bin_dir = self.root / "bin"
|
|
48
|
+
self.bin_dir.mkdir()
|
|
49
|
+
self.empty_dir = self.root / "empty"
|
|
50
|
+
self.empty_dir.mkdir()
|
|
51
|
+
self.log_path = self.root / "calls.jsonl"
|
|
52
|
+
|
|
53
|
+
self._saved_env = {
|
|
54
|
+
key: os.environ.get(key)
|
|
55
|
+
for key in ("CONSILIUM_NPM_VERSION", "CONSILIUM_TEST_LOG")
|
|
56
|
+
}
|
|
57
|
+
self.addCleanup(self._restore_env)
|
|
58
|
+
os.environ.pop("CONSILIUM_NPM_VERSION", None)
|
|
59
|
+
os.environ["CONSILIUM_TEST_LOG"] = str(self.log_path)
|
|
60
|
+
|
|
61
|
+
self._write_fake_npx()
|
|
62
|
+
|
|
63
|
+
def _restore_env(self):
|
|
64
|
+
for key, value in self._saved_env.items():
|
|
65
|
+
if value is None:
|
|
66
|
+
os.environ.pop(key, None)
|
|
67
|
+
else:
|
|
68
|
+
os.environ[key] = value
|
|
69
|
+
|
|
70
|
+
def _write_fake_npx(self):
|
|
71
|
+
script = self.bin_dir / "fake_npx.py"
|
|
72
|
+
script.write_text(_FAKE_NPX, encoding="utf-8")
|
|
73
|
+
|
|
74
|
+
if os.name == "nt":
|
|
75
|
+
launcher = self.bin_dir / "npx.cmd"
|
|
76
|
+
content = '@echo off\r\n"{0}" "%~dp0fake_npx.py" %*\r\n'.format(
|
|
77
|
+
sys.executable
|
|
78
|
+
)
|
|
79
|
+
with launcher.open("w", encoding="utf-8", newline="") as handle:
|
|
80
|
+
handle.write(content)
|
|
81
|
+
else:
|
|
82
|
+
launcher = self.bin_dir / "npx"
|
|
83
|
+
content = '#!/bin/sh\nexec "{0}" "$(dirname "$0")/fake_npx.py" "$@"\n'.format(
|
|
84
|
+
sys.executable
|
|
85
|
+
)
|
|
86
|
+
launcher.write_text(content, encoding="utf-8")
|
|
87
|
+
launcher.chmod(
|
|
88
|
+
launcher.stat().st_mode
|
|
89
|
+
| stat.S_IXUSR
|
|
90
|
+
| stat.S_IXGRP
|
|
91
|
+
| stat.S_IXOTH
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def _invoke(self, args, *, env=None, path=None, cwd=None):
|
|
95
|
+
call_env = {
|
|
96
|
+
"PATH": str(self.bin_dir if path is None else path),
|
|
97
|
+
"CONSILIUM_TEST_LOG": str(self.log_path),
|
|
98
|
+
}
|
|
99
|
+
if os.name == "nt":
|
|
100
|
+
call_env["PATHEXT"] = ".COM;.EXE;.BAT;.CMD"
|
|
101
|
+
if env:
|
|
102
|
+
call_env.update(env)
|
|
103
|
+
|
|
104
|
+
stderr = io.StringIO()
|
|
105
|
+
previous_cwd = os.getcwd()
|
|
106
|
+
if cwd is not None:
|
|
107
|
+
os.chdir(cwd)
|
|
108
|
+
try:
|
|
109
|
+
with mock.patch.dict(os.environ, call_env, clear=False):
|
|
110
|
+
with contextlib.redirect_stderr(stderr):
|
|
111
|
+
code = main(list(args))
|
|
112
|
+
finally:
|
|
113
|
+
if cwd is not None:
|
|
114
|
+
os.chdir(previous_cwd)
|
|
115
|
+
return code, stderr.getvalue()
|
|
116
|
+
|
|
117
|
+
def _calls(self):
|
|
118
|
+
if not self.log_path.exists():
|
|
119
|
+
return []
|
|
120
|
+
return [
|
|
121
|
+
json.loads(line)
|
|
122
|
+
for line in self.log_path.read_text(encoding="utf-8").splitlines()
|
|
123
|
+
if line
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
def test_forwards_arguments_and_returns_child_exit_code(self):
|
|
127
|
+
code, stderr = self._invoke(["review", "the", "repo"])
|
|
128
|
+
|
|
129
|
+
self.assertEqual(code, 3)
|
|
130
|
+
self.assertEqual(stderr, "")
|
|
131
|
+
calls = self._calls()
|
|
132
|
+
self.assertEqual(len(calls), 1)
|
|
133
|
+
self.assertEqual(
|
|
134
|
+
calls[0]["argv"],
|
|
135
|
+
[
|
|
136
|
+
"--yes",
|
|
137
|
+
"{0}@{1}".format(NPM_PACKAGE, __version__),
|
|
138
|
+
"review",
|
|
139
|
+
"the",
|
|
140
|
+
"repo",
|
|
141
|
+
],
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
def test_version_matches_expected_release(self):
|
|
145
|
+
self.assertEqual(__version__, "1.10.0")
|
|
146
|
+
|
|
147
|
+
def test_uses_env_override_and_inherits_it(self):
|
|
148
|
+
code, stderr = self._invoke(
|
|
149
|
+
["--help"], env={NPM_VERSION_ENV: "latest"}
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
self.assertEqual(code, 3)
|
|
153
|
+
self.assertEqual(stderr, "")
|
|
154
|
+
call = self._calls()[0]
|
|
155
|
+
self.assertEqual(
|
|
156
|
+
call["argv"],
|
|
157
|
+
["--yes", "{0}@latest".format(NPM_PACKAGE), "--help"],
|
|
158
|
+
)
|
|
159
|
+
self.assertEqual(call["env"][NPM_VERSION_ENV], "latest")
|
|
160
|
+
|
|
161
|
+
def test_missing_npx_returns_127_with_install_hint(self):
|
|
162
|
+
code, stderr = self._invoke(
|
|
163
|
+
[], path=self.empty_dir, cwd=self.empty_dir
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
self.assertEqual(code, 127)
|
|
167
|
+
self.assertIn("npx", stderr)
|
|
168
|
+
self.assertIn("Node.js", stderr)
|
|
169
|
+
self.assertIn("npm", stderr)
|
|
170
|
+
self.assertIn("OpenCode", stderr)
|
|
171
|
+
self.assertEqual(self._calls(), [])
|
|
172
|
+
|
|
173
|
+
def test_keyboard_interrupt_returns_130(self):
|
|
174
|
+
with mock.patch(
|
|
175
|
+
"lv24_opencode_consilium.cli.subprocess.run",
|
|
176
|
+
side_effect=KeyboardInterrupt,
|
|
177
|
+
):
|
|
178
|
+
code, stderr = self._invoke([])
|
|
179
|
+
|
|
180
|
+
self.assertEqual(code, 130)
|
|
181
|
+
self.assertEqual(stderr, "")
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
if __name__ == "__main__":
|
|
185
|
+
unittest.main()
|