jusi-gigacode 0.1.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.
@@ -0,0 +1,7 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .pytest_cache/
4
+ .venv/
5
+ dist/
6
+ build/
7
+ *.egg-info/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 notawhaleble
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,133 @@
1
+ Metadata-Version: 2.5
2
+ Name: jusi-gigacode
3
+ Version: 0.1.0
4
+ Summary: GigaCode exact-provider plugin for the Jusi ACP family
5
+ Project-URL: Repository, https://github.com/notawhaleble/jusi-gigacode
6
+ Author: notawhaleble
7
+ License: MIT License
8
+
9
+ Copyright (c) 2026 notawhaleble
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+ License-File: LICENSE
29
+ Keywords: acp,agent,gigacode,jupyter,jusi
30
+ Classifier: Development Status :: 3 - Alpha
31
+ Classifier: Framework :: Jupyter
32
+ Classifier: License :: OSI Approved :: MIT License
33
+ Classifier: Programming Language :: Python :: 3
34
+ Classifier: Programming Language :: Python :: 3.10
35
+ Classifier: Programming Language :: Python :: 3.11
36
+ Classifier: Programming Language :: Python :: 3.12
37
+ Classifier: Programming Language :: Python :: 3.13
38
+ Requires-Python: >=3.10
39
+ Requires-Dist: jusi-acp<0.2,>=0.1.0
40
+ Provides-Extra: dev
41
+ Requires-Dist: pytest>=8; extra == 'dev'
42
+ Description-Content-Type: text/markdown
43
+
44
+ # jusi-gigacode
45
+
46
+ `jusi-gigacode` is the exact GigaCode provider for the shared Jusi ACP family.
47
+ It launches GigaCode over Agent Client Protocol while `jusi-acp` supplies the
48
+ `%%acp` magic, interactive VisiData application, permission choices, durable
49
+ follow-ups, cancellation, session controls, completion, and editor actions.
50
+
51
+ ## Installation
52
+
53
+ Install this package, `jusi-acp`, and Jusi in the Python environment used by the
54
+ target Jupyter kernel. The `gigacode` executable must be available on that
55
+ target's `PATH`.
56
+
57
+ ```bash
58
+ pip install jusi-gigacode
59
+ ```
60
+
61
+ For development from sibling checkouts:
62
+
63
+ ```bash
64
+ python3.12 -m venv .venv
65
+ .venv/bin/pip install -e ../jusi-acp -e '.[dev]'
66
+ .venv/bin/pytest
67
+ ```
68
+
69
+ ## Configuration
70
+
71
+ Add an ACP alias to `~/.jusi/jusi.toml` on the kernel target:
72
+
73
+ ```toml
74
+ [acp.work]
75
+ provider = "gigacode"
76
+ path = "/path/to/project"
77
+ additional_directories = ["../shared"]
78
+ model = "provider-owned-model-name"
79
+ ```
80
+
81
+ Then run:
82
+
83
+ ```python
84
+ %%acp work
85
+ Inspect the project and implement the requested change.
86
+ ```
87
+
88
+ The provider launches `gigacode --acp`. A live GigaCode handshake has confirmed
89
+ ACP protocol version 1, the browser-backed `gigacode` authentication method,
90
+ session load/resume support, and HTTP/SSE MCP support.
91
+
92
+ Provider-specific alias options are:
93
+
94
+ - `executable`: executable name or path; defaults to `gigacode`.
95
+ - `model`: passed as `--model VALUE`.
96
+ - `approval_mode`: passed as `--approval-mode VALUE`. Leaving it unset allows
97
+ ACP permission requests to reach the interactive Jusi client.
98
+ - `arguments`: an array of additional command-line arguments.
99
+ - `environment`: environment-variable overrides for the GigaCode process.
100
+ - `auth_method`: ACP authentication method ID. It defaults to `gigacode`, as
101
+ advertised by GigaCode, or may be set to an empty string to skip the proactive
102
+ authentication call.
103
+
104
+ The executable inherits the target environment, including desktop/session
105
+ variables used by browser launchers. With the default authentication method,
106
+ `jusi-acp` calls ACP `authenticate` before creating the session. GigaCode owns
107
+ opening its authorization URL and waiting for the callback; after it completes,
108
+ the original cell body is submitted automatically.
109
+
110
+ Session controls and family commands include:
111
+
112
+ ```python
113
+ %%acp work --load SESSION_ID
114
+ Replay and continue a stored session.
115
+ ```
116
+
117
+ ```python
118
+ %%acp work --resume SESSION_ID
119
+ Continue a stored session without replay.
120
+ ```
121
+
122
+ Follow-up cells can use `/mode ID`, `/config ID VALUE`, `/auth METHOD_ID`, and
123
+ `/cancel`. Slash commands advertised by GigaCode are also completed and sent as
124
+ ordinary ACP prompts.
125
+
126
+ ## Verified ACP handshake
127
+
128
+ GigaCode identifies its agent implementation as `qwen-code` with the title
129
+ `GigaCode`. It advertises authentication method ID `gigacode`; authentication is
130
+ handled by the GigaCode backend through its browser flow. The initialize call
131
+ waits for that flow to finish, after which `jusi-acp` completes ACP
132
+ authentication, establishes the session, and submits the original cell body
133
+ without requiring the user to repeat it.
@@ -0,0 +1,90 @@
1
+ # jusi-gigacode
2
+
3
+ `jusi-gigacode` is the exact GigaCode provider for the shared Jusi ACP family.
4
+ It launches GigaCode over Agent Client Protocol while `jusi-acp` supplies the
5
+ `%%acp` magic, interactive VisiData application, permission choices, durable
6
+ follow-ups, cancellation, session controls, completion, and editor actions.
7
+
8
+ ## Installation
9
+
10
+ Install this package, `jusi-acp`, and Jusi in the Python environment used by the
11
+ target Jupyter kernel. The `gigacode` executable must be available on that
12
+ target's `PATH`.
13
+
14
+ ```bash
15
+ pip install jusi-gigacode
16
+ ```
17
+
18
+ For development from sibling checkouts:
19
+
20
+ ```bash
21
+ python3.12 -m venv .venv
22
+ .venv/bin/pip install -e ../jusi-acp -e '.[dev]'
23
+ .venv/bin/pytest
24
+ ```
25
+
26
+ ## Configuration
27
+
28
+ Add an ACP alias to `~/.jusi/jusi.toml` on the kernel target:
29
+
30
+ ```toml
31
+ [acp.work]
32
+ provider = "gigacode"
33
+ path = "/path/to/project"
34
+ additional_directories = ["../shared"]
35
+ model = "provider-owned-model-name"
36
+ ```
37
+
38
+ Then run:
39
+
40
+ ```python
41
+ %%acp work
42
+ Inspect the project and implement the requested change.
43
+ ```
44
+
45
+ The provider launches `gigacode --acp`. A live GigaCode handshake has confirmed
46
+ ACP protocol version 1, the browser-backed `gigacode` authentication method,
47
+ session load/resume support, and HTTP/SSE MCP support.
48
+
49
+ Provider-specific alias options are:
50
+
51
+ - `executable`: executable name or path; defaults to `gigacode`.
52
+ - `model`: passed as `--model VALUE`.
53
+ - `approval_mode`: passed as `--approval-mode VALUE`. Leaving it unset allows
54
+ ACP permission requests to reach the interactive Jusi client.
55
+ - `arguments`: an array of additional command-line arguments.
56
+ - `environment`: environment-variable overrides for the GigaCode process.
57
+ - `auth_method`: ACP authentication method ID. It defaults to `gigacode`, as
58
+ advertised by GigaCode, or may be set to an empty string to skip the proactive
59
+ authentication call.
60
+
61
+ The executable inherits the target environment, including desktop/session
62
+ variables used by browser launchers. With the default authentication method,
63
+ `jusi-acp` calls ACP `authenticate` before creating the session. GigaCode owns
64
+ opening its authorization URL and waiting for the callback; after it completes,
65
+ the original cell body is submitted automatically.
66
+
67
+ Session controls and family commands include:
68
+
69
+ ```python
70
+ %%acp work --load SESSION_ID
71
+ Replay and continue a stored session.
72
+ ```
73
+
74
+ ```python
75
+ %%acp work --resume SESSION_ID
76
+ Continue a stored session without replay.
77
+ ```
78
+
79
+ Follow-up cells can use `/mode ID`, `/config ID VALUE`, `/auth METHOD_ID`, and
80
+ `/cancel`. Slash commands advertised by GigaCode are also completed and sent as
81
+ ordinary ACP prompts.
82
+
83
+ ## Verified ACP handshake
84
+
85
+ GigaCode identifies its agent implementation as `qwen-code` with the title
86
+ `GigaCode`. It advertises authentication method ID `gigacode`; authentication is
87
+ handled by the GigaCode backend through its browser flow. The initialize call
88
+ waits for that flow to finish, after which `jusi-acp` completes ACP
89
+ authentication, establishes the session, and submits the original cell body
90
+ without requiring the user to repeat it.
@@ -0,0 +1,43 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.21"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "jusi-gigacode"
7
+ version = "0.1.0"
8
+ description = "GigaCode exact-provider plugin for the Jusi ACP family"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { file = "LICENSE" }
12
+ authors = [{ name = "notawhaleble" }]
13
+ keywords = ["acp", "gigacode", "jusi", "jupyter", "agent"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Framework :: Jupyter",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ ]
24
+ dependencies = [
25
+ "jusi-acp>=0.1.0,<0.2",
26
+ ]
27
+
28
+ [project.optional-dependencies]
29
+ dev = ["pytest>=8"]
30
+
31
+ [project.urls]
32
+ Repository = "https://github.com/notawhaleble/jusi-gigacode"
33
+
34
+ [project.entry-points."jusi.plugins.v1"]
35
+ gigacode = "jusi_gigacode.catalog:catalog_entry"
36
+
37
+ [tool.hatch.build.targets.wheel]
38
+ packages = ["src/jusi_gigacode"]
39
+
40
+ [tool.pytest.ini_options]
41
+ testpaths = ["tests"]
42
+ pythonpath = ["src", "../jusi-acp/src"]
43
+
@@ -0,0 +1,11 @@
1
+ """GigaCode exact-provider plugin for Jusi ACP."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ try:
6
+ __version__ = version("jusi-gigacode")
7
+ except PackageNotFoundError: # source checkout
8
+ __version__ = "0.1.0"
9
+
10
+ __all__ = ["__version__"]
11
+
@@ -0,0 +1,27 @@
1
+ """Side-effect-free Jusi catalog entry for the exact GigaCode provider."""
2
+ from __future__ import annotations
3
+
4
+ from typing import Any
5
+
6
+ from . import __version__
7
+
8
+
9
+ def catalog_entry() -> dict[str, Any]:
10
+ # Keep this claim byte-for-byte compatible with jusi_acp.family_claim().
11
+ # Discovery must not import the worker or start the ACP application.
12
+ return {
13
+ "plugin_id": "gigacode",
14
+ "plugin_version": __version__,
15
+ "distribution": "jusi-gigacode",
16
+ "families": [{
17
+ "family_id": "acp",
18
+ "magic_name": "acp",
19
+ "capabilities": ["execute", "followup", "complete", "interrupt", "editor_actions"],
20
+ "presentation": {"syntax": "markdown", "indent": "markdown"},
21
+ }],
22
+ "kernel_extensions": ["jusi_gigacode.kernel"],
23
+ "worker_entry_point": "jusi_gigacode.worker:create_worker",
24
+ "media_types": ["text/x-ansi"],
25
+ "interaction": "terminal_interactive",
26
+ }
27
+
@@ -0,0 +1,12 @@
1
+ """Exact-provider attestation backed by the shared ACP kernel dispatcher."""
2
+
3
+ from jusi_acp import KernelProviderAdapter
4
+
5
+ from . import __version__
6
+
7
+ _adapter = KernelProviderAdapter("gigacode", __version__)
8
+
9
+ jusi_kernel_adapter_v1 = _adapter.manifest
10
+ configure_jusi_runtime_v1 = _adapter.configure
11
+ load_ipython_extension = _adapter.load_ipython_extension
12
+
@@ -0,0 +1,93 @@
1
+ """GigaCode launch validation and shared ACP worker construction."""
2
+ from __future__ import annotations
3
+
4
+ from collections.abc import Mapping
5
+ import os
6
+ from pathlib import Path
7
+ import shutil
8
+ from typing import Any
9
+
10
+ from jusi_acp import ACPWorker, AgentLaunch, ProviderSpec
11
+
12
+ from . import __version__
13
+
14
+ _COMMON_KEYS = {"provider", "path", "additional_directories", "mcp_servers"}
15
+ _PROVIDER_KEYS = {
16
+ "executable", "arguments", "model", "approval_mode", "environment", "auth_method",
17
+ }
18
+
19
+
20
+ def resolve_launch(configuration: Mapping[str, Any], cwd: Path) -> AgentLaunch:
21
+ """Validate one alias and construct a GigaCode ACP invocation."""
22
+ unknown = sorted(set(configuration) - _COMMON_KEYS - _PROVIDER_KEYS)
23
+ if unknown:
24
+ raise ValueError(f"unsupported GigaCode option(s): {', '.join(unknown)}")
25
+
26
+ executable = _string(configuration.get("executable", "gigacode"), "executable")
27
+ resolved_executable = _resolve_executable(executable or "gigacode")
28
+ arguments = _string_list(configuration.get("arguments", []), "arguments")
29
+
30
+ argv = [resolved_executable, "--acp"]
31
+ model = _string(configuration.get("model", ""), "model")
32
+ if model:
33
+ argv.extend(("--model", model))
34
+ approval_mode = _string(configuration.get("approval_mode", ""), "approval_mode")
35
+ if approval_mode:
36
+ argv.extend(("--approval-mode", approval_mode))
37
+ argv.extend(arguments)
38
+
39
+ environment = configuration.get("environment", {})
40
+ if not isinstance(environment, Mapping):
41
+ raise TypeError("environment must be a table of string values")
42
+ clean_environment: dict[str, str] = {}
43
+ for key, value in environment.items():
44
+ clean_key = _string(key, "environment name")
45
+ clean_value = _string(value, f"environment value for {clean_key!r}", allow_empty=True)
46
+ if not clean_key or "=" in clean_key:
47
+ raise ValueError("environment names must be non-empty and cannot contain '='")
48
+ clean_environment[clean_key] = clean_value
49
+
50
+ # GigaCode advertises this browser-backed method in its ACP v1 initialize
51
+ # response. Set auth_method = "" only when proactive authentication should
52
+ # be skipped for an already authenticated or specially configured install.
53
+ auth_method = _string(configuration.get("auth_method", "gigacode"), "auth_method")
54
+ return AgentLaunch(tuple(argv), cwd, clean_environment, auth_method)
55
+
56
+
57
+ def _resolve_executable(value: str) -> str:
58
+ expanded = os.path.expanduser(value)
59
+ if os.path.sep in expanded:
60
+ path = Path(expanded).resolve()
61
+ if not path.is_file():
62
+ raise ValueError(f"GigaCode executable does not exist: {path}")
63
+ if not os.access(path, os.X_OK):
64
+ raise ValueError(f"GigaCode executable is not executable: {path}")
65
+ return str(path)
66
+ found = shutil.which(expanded)
67
+ if found is None:
68
+ raise ValueError(f"GigaCode executable {expanded!r} was not found on PATH")
69
+ return found
70
+
71
+
72
+ def _string(value: Any, label: str, *, allow_empty: bool = True) -> str:
73
+ if not isinstance(value, str):
74
+ raise TypeError(f"{label} must be a string")
75
+ if any(character in value for character in ("\x00", "\n", "\r")):
76
+ raise ValueError(f"{label} contains an invalid control character")
77
+ result = value.strip()
78
+ if not allow_empty and not result:
79
+ raise ValueError(f"{label} cannot be empty")
80
+ return result
81
+
82
+
83
+ def _string_list(value: Any, label: str) -> list[str]:
84
+ if not isinstance(value, list):
85
+ raise TypeError(f"{label} must be an array of strings")
86
+ return [_string(item, f"{label} item", allow_empty=False) for item in value]
87
+
88
+
89
+ PROVIDER = ProviderSpec("gigacode", __version__, resolve_launch)
90
+
91
+
92
+ def create_worker(context: Any) -> ACPWorker:
93
+ return ACPWorker(context, PROVIDER)
@@ -0,0 +1,30 @@
1
+ from importlib.metadata import entry_points
2
+
3
+ from jusi.plugin_api import validate_catalog_claims
4
+ from jusi_acp import family_claim
5
+
6
+ from jusi_gigacode import __version__
7
+ from jusi_gigacode.catalog import catalog_entry
8
+
9
+
10
+ def test_catalog_is_an_exact_compatible_acp_provider() -> None:
11
+ entry = catalog_entry()
12
+ assert entry["plugin_id"] == "gigacode"
13
+ assert entry["plugin_version"] == __version__
14
+ assert entry["distribution"] == "jusi-gigacode"
15
+ assert entry["families"] == [family_claim()]
16
+ assert entry["kernel_extensions"] == ["jusi_gigacode.kernel"]
17
+ assert entry["worker_entry_point"] == "jusi_gigacode.worker:create_worker"
18
+ validate_catalog_claims({
19
+ "protocol_version": 1,
20
+ "catalog_version": 1,
21
+ "discovery_id": "jusi_gigacode_tests",
22
+ "plugins": [entry],
23
+ })
24
+
25
+
26
+ def test_installed_distribution_exposes_exact_entry_point() -> None:
27
+ matches = [item for item in entry_points(group="jusi.plugins.v1") if item.name == "gigacode"]
28
+ if matches:
29
+ assert matches[0].value == "jusi_gigacode.catalog:catalog_entry"
30
+
@@ -0,0 +1,11 @@
1
+ from jusi_gigacode import __version__
2
+ from jusi_gigacode.kernel import jusi_kernel_adapter_v1
3
+
4
+
5
+ def test_kernel_attests_exact_provider_and_family() -> None:
6
+ assert jusi_kernel_adapter_v1() == {
7
+ "plugin_id": "gigacode",
8
+ "plugin_version": __version__,
9
+ "families": [{"family_id": "acp", "magic_name": "acp"}],
10
+ }
11
+
@@ -0,0 +1,97 @@
1
+ import json
2
+ from pathlib import Path
3
+ from types import SimpleNamespace
4
+
5
+ import pytest
6
+
7
+ from jusi_acp import ACPWorker
8
+ from jusi_gigacode.worker import create_worker, resolve_launch
9
+
10
+
11
+ def test_default_launch_uses_acp_and_browser_auth(tmp_path: Path, monkeypatch) -> None: # type: ignore[no-untyped-def]
12
+ monkeypatch.setattr("jusi_gigacode.worker.shutil.which", lambda command: "/tools/gigacode")
13
+ launch = resolve_launch({"provider": "gigacode", "path": str(tmp_path)}, tmp_path)
14
+ assert launch.argv == ("/tools/gigacode", "--acp")
15
+ assert launch.cwd == tmp_path
16
+ assert launch.auth_method == "gigacode"
17
+
18
+
19
+ def test_launch_maps_provider_options(tmp_path: Path, monkeypatch) -> None: # type: ignore[no-untyped-def]
20
+ monkeypatch.setattr("jusi_gigacode.worker.shutil.which", lambda command: "/tools/custom")
21
+ launch = resolve_launch({
22
+ "executable": "custom",
23
+ "model": "coder-model",
24
+ "approval_mode": "default",
25
+ "arguments": ["--debug"],
26
+ "environment": {"GIGACODE_PROFILE": "work"},
27
+ "auth_method": "gigacode-login",
28
+ }, tmp_path)
29
+ assert launch.argv == (
30
+ "/tools/custom", "--acp", "--model", "coder-model",
31
+ "--approval-mode", "default", "--debug",
32
+ )
33
+ assert launch.environment == {"GIGACODE_PROFILE": "work"}
34
+ assert launch.auth_method == "gigacode-login"
35
+
36
+
37
+ def test_auth_can_be_disabled_for_an_already_authenticated_install(tmp_path: Path, monkeypatch) -> None: # type: ignore[no-untyped-def]
38
+ monkeypatch.setattr("jusi_gigacode.worker.shutil.which", lambda command: "/tools/gigacode")
39
+ assert resolve_launch({"auth_method": ""}, tmp_path).auth_method == ""
40
+
41
+
42
+ @pytest.mark.parametrize("configuration, message", [
43
+ ({"unknown": True}, "unsupported GigaCode option"),
44
+ ({"arguments": "--debug"}, "arguments must be an array"),
45
+ ({"environment": []}, "environment must be a table"),
46
+ ({"model": 7}, "model must be a string"),
47
+ ])
48
+ def test_invalid_provider_configuration_is_rejected(
49
+ tmp_path: Path, monkeypatch, configuration: dict, message: str
50
+ ) -> None: # type: ignore[no-untyped-def]
51
+ monkeypatch.setattr("jusi_gigacode.worker.shutil.which", lambda command: "/tools/gigacode")
52
+ with pytest.raises((TypeError, ValueError), match=message):
53
+ resolve_launch(configuration, tmp_path)
54
+
55
+
56
+ def test_missing_executable_is_rejected(tmp_path: Path, monkeypatch) -> None: # type: ignore[no-untyped-def]
57
+ monkeypatch.setattr("jusi_gigacode.worker.shutil.which", lambda command: None)
58
+ with pytest.raises(ValueError, match="not found on PATH"):
59
+ resolve_launch({}, tmp_path)
60
+
61
+
62
+ def test_factory_returns_shared_worker_for_exact_provider() -> None:
63
+ worker = create_worker(SimpleNamespace(plugin_id="gigacode"))
64
+ try:
65
+ assert isinstance(worker, ACPWorker)
66
+ assert worker.provider.plugin_id == "gigacode"
67
+ finally:
68
+ worker.close()
69
+
70
+
71
+ def test_exact_worker_hands_authenticated_launch_to_shared_application(
72
+ tmp_path: Path, monkeypatch
73
+ ) -> None: # type: ignore[no-untyped-def]
74
+ monkeypatch.setattr("jusi_gigacode.worker.shutil.which", lambda command: "/tools/gigacode")
75
+ worker = create_worker(SimpleNamespace(plugin_id="gigacode"))
76
+ try:
77
+ result = worker.handle("execute", {
78
+ "alias": "work",
79
+ "body": "continue after login",
80
+ "cwd": str(tmp_path),
81
+ "configuration": {"auth_method": "gigacode-login"},
82
+ "additional_directories": [],
83
+ "mcp_servers": [],
84
+ "session_action": "new",
85
+ "session_id": "",
86
+ })
87
+ surface = result.core_requests[0]
88
+ payload = json.loads(Path(surface.argv[3]).read_text(encoding="utf-8"))
89
+ assert payload["launch"] == {
90
+ "argv": ["/tools/gigacode", "--acp"],
91
+ "cwd": str(tmp_path),
92
+ "environment": {},
93
+ "auth_method": "gigacode-login",
94
+ }
95
+ assert payload["submission"]["body"] == "continue after login"
96
+ finally:
97
+ worker.close()