delegate-agent-cli 0.11.0__py3-none-any.whl
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.
- delegate_agent/__init__.py +5 -0
- delegate_agent/archived_logs.py +44 -0
- delegate_agent/argv_builders.py +423 -0
- delegate_agent/argv_utils.py +36 -0
- delegate_agent/capability_commands.py +99 -0
- delegate_agent/cli.py +987 -0
- delegate_agent/cli_parser.py +1627 -0
- delegate_agent/command_errors.py +29 -0
- delegate_agent/command_help.py +1264 -0
- delegate_agent/config.py +1117 -0
- delegate_agent/config_commands.py +143 -0
- delegate_agent/constants.py +50 -0
- delegate_agent/describe_payload.py +1018 -0
- delegate_agent/errors.py +32 -0
- delegate_agent/git_utils.py +180 -0
- delegate_agent/harness_events.py +719 -0
- delegate_agent/inspection_commands.py +104 -0
- delegate_agent/isolation.py +316 -0
- delegate_agent/json_types.py +18 -0
- delegate_agent/log_output.py +78 -0
- delegate_agent/private_io.py +109 -0
- delegate_agent/profile_commands.py +42 -0
- delegate_agent/profile_guard.py +116 -0
- delegate_agent/profiles.py +389 -0
- delegate_agent/prompt_instructions.py +39 -0
- delegate_agent/prompt_transport.py +12 -0
- delegate_agent/reasoning.py +916 -0
- delegate_agent/redaction.py +193 -0
- delegate_agent/rendering.py +573 -0
- delegate_agent/request_build.py +1681 -0
- delegate_agent/request_models.py +191 -0
- delegate_agent/retention.py +321 -0
- delegate_agent/run_metadata.py +78 -0
- delegate_agent/run_output_commands.py +645 -0
- delegate_agent/run_registry.py +747 -0
- delegate_agent/run_status.py +300 -0
- delegate_agent/runner.py +1830 -0
- delegate_agent/safe_workspace.py +821 -0
- delegate_agent/snapshot_view.py +229 -0
- delegate_agent/wait_cancel_commands.py +559 -0
- delegate_agent/worktree_commands.py +218 -0
- delegate_agent/worktree_execution.py +654 -0
- delegate_agent/worktree_gc.py +529 -0
- delegate_agent/worktree_mgmt.py +782 -0
- delegate_agent/worktree_records.py +232 -0
- delegate_agent/worktree_remove.py +547 -0
- delegate_agent/worktree_summary.py +242 -0
- delegate_agent/wsl.py +65 -0
- delegate_agent_cli-0.11.0.dist-info/METADATA +325 -0
- delegate_agent_cli-0.11.0.dist-info/RECORD +54 -0
- delegate_agent_cli-0.11.0.dist-info/WHEEL +5 -0
- delegate_agent_cli-0.11.0.dist-info/entry_points.txt +2 -0
- delegate_agent_cli-0.11.0.dist-info/licenses/LICENSE +21 -0
- delegate_agent_cli-0.11.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import TextIO
|
|
7
|
+
|
|
8
|
+
from delegate_agent import config as delegate_config
|
|
9
|
+
from delegate_agent import rendering as delegate_rendering
|
|
10
|
+
from delegate_agent import wsl
|
|
11
|
+
from delegate_agent.errors import EXIT_OK, DelegateError
|
|
12
|
+
from delegate_agent.json_types import JsonObject
|
|
13
|
+
from delegate_agent.private_io import ensure_private_file
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class ConfigCommand:
|
|
18
|
+
action: str
|
|
19
|
+
force: bool = False
|
|
20
|
+
json_mode: bool = False
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
PROFILE_CONFIG_NAMES = delegate_config.PROFILE_CONFIG_NAMES
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _target_path() -> Path:
|
|
27
|
+
raw = delegate_config.config_path()
|
|
28
|
+
if wsl.should_reject_windows_path(str(raw)):
|
|
29
|
+
raise DelegateError("windows_path", wsl.windows_path_message("DELEGATE_CONFIG", str(raw)))
|
|
30
|
+
return raw
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _profile_overlay(config: JsonObject, profile: str) -> JsonObject | None:
|
|
34
|
+
profiles = config.get("profiles")
|
|
35
|
+
if not isinstance(profiles, dict):
|
|
36
|
+
return None
|
|
37
|
+
definitions = profiles.get("definitions")
|
|
38
|
+
if not isinstance(definitions, dict):
|
|
39
|
+
return None
|
|
40
|
+
definition = definitions.get(profile)
|
|
41
|
+
if not isinstance(definition, dict):
|
|
42
|
+
return None
|
|
43
|
+
detect_from = profiles.get("detectFrom", [])
|
|
44
|
+
overlay_profiles: JsonObject = {
|
|
45
|
+
"default": profile,
|
|
46
|
+
"definitions": {profile: definition},
|
|
47
|
+
}
|
|
48
|
+
if isinstance(detect_from, list):
|
|
49
|
+
overlay_profiles["detectFrom"] = [
|
|
50
|
+
item for item in detect_from if isinstance(item, str) and item.strip()
|
|
51
|
+
]
|
|
52
|
+
return {"profiles": overlay_profiles}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _write_missing_profile_configs(base_path: Path, config: JsonObject) -> JsonObject:
|
|
56
|
+
created: list[str] = []
|
|
57
|
+
existing: list[str] = []
|
|
58
|
+
skipped: list[str] = []
|
|
59
|
+
for profile in PROFILE_CONFIG_NAMES:
|
|
60
|
+
path = delegate_config.profile_config_path(base_path, profile)
|
|
61
|
+
if path.exists():
|
|
62
|
+
existing.append(str(path))
|
|
63
|
+
continue
|
|
64
|
+
overlay = _profile_overlay(config, profile)
|
|
65
|
+
if overlay is None:
|
|
66
|
+
skipped.append(profile)
|
|
67
|
+
continue
|
|
68
|
+
path.write_text(json.dumps(overlay, indent=2) + "\n", encoding="utf-8")
|
|
69
|
+
ensure_private_file(path)
|
|
70
|
+
created.append(str(path))
|
|
71
|
+
return {
|
|
72
|
+
"created": created,
|
|
73
|
+
"existing": existing,
|
|
74
|
+
"skipped": skipped,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _read_config(path: Path) -> JsonObject:
|
|
79
|
+
try:
|
|
80
|
+
return delegate_config.read_config_file(path)
|
|
81
|
+
except delegate_config.ConfigError as exc:
|
|
82
|
+
raise DelegateError(exc.error, exc.message) from exc
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _validated_effective_config(config: JsonObject) -> JsonObject:
|
|
86
|
+
merged = delegate_config.merge_config_layer(delegate_config.embedded_default_config(), config)
|
|
87
|
+
try:
|
|
88
|
+
delegate_config.validate_config(merged)
|
|
89
|
+
except delegate_config.ConfigError as exc:
|
|
90
|
+
raise DelegateError(exc.error, exc.message) from exc
|
|
91
|
+
return merged
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def emit(command: ConfigCommand, stdout: TextIO) -> int:
|
|
95
|
+
if command.action not in {"init", "sync-profiles"}:
|
|
96
|
+
raise DelegateError("invalid_config_command", f"Unknown config action: {command.action}")
|
|
97
|
+
path = _target_path()
|
|
98
|
+
if command.action == "sync-profiles":
|
|
99
|
+
if not path.exists():
|
|
100
|
+
raise DelegateError(
|
|
101
|
+
"config_not_found",
|
|
102
|
+
f"Config does not exist at {path}; run delegate config init first.",
|
|
103
|
+
)
|
|
104
|
+
payload = _validated_effective_config(_read_config(path))
|
|
105
|
+
profile_configs = _write_missing_profile_configs(path, payload)
|
|
106
|
+
result: JsonObject = {
|
|
107
|
+
"ok": True,
|
|
108
|
+
"path": str(path),
|
|
109
|
+
"action": "sync-profiles",
|
|
110
|
+
"profileConfigs": profile_configs,
|
|
111
|
+
}
|
|
112
|
+
if command.json_mode:
|
|
113
|
+
delegate_rendering.print_json(result, stdout)
|
|
114
|
+
else:
|
|
115
|
+
for created in profile_configs["created"]:
|
|
116
|
+
print(f"wrote profile config: {created}", file=stdout)
|
|
117
|
+
if not profile_configs["created"]:
|
|
118
|
+
print("profile configs already present", file=stdout)
|
|
119
|
+
return EXIT_OK
|
|
120
|
+
if path.exists() and not command.force:
|
|
121
|
+
raise DelegateError(
|
|
122
|
+
"config_exists",
|
|
123
|
+
f"Config already exists at {path}; pass --force to overwrite it.",
|
|
124
|
+
)
|
|
125
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
126
|
+
payload = delegate_config.example_config()
|
|
127
|
+
path.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
|
|
128
|
+
ensure_private_file(path)
|
|
129
|
+
profile_configs = _write_missing_profile_configs(path, payload)
|
|
130
|
+
result: JsonObject = {
|
|
131
|
+
"ok": True,
|
|
132
|
+
"path": str(path),
|
|
133
|
+
"action": "init",
|
|
134
|
+
"force": command.force,
|
|
135
|
+
"profileConfigs": profile_configs,
|
|
136
|
+
}
|
|
137
|
+
if command.json_mode:
|
|
138
|
+
delegate_rendering.print_json(result, stdout)
|
|
139
|
+
else:
|
|
140
|
+
print(f"wrote config: {path}", file=stdout)
|
|
141
|
+
for created in profile_configs["created"]:
|
|
142
|
+
print(f"wrote profile config: {created}", file=stdout)
|
|
143
|
+
return EXIT_OK
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Cross-cutting CLI mode vocabulary and validation.
|
|
2
|
+
|
|
3
|
+
A dependency leaf shared by the parser, request builder, argv builders, and
|
|
4
|
+
execution layers so they agree on the launch mode vocabulary without
|
|
5
|
+
importing ``cli``.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from delegate_agent.errors import DelegateError
|
|
11
|
+
|
|
12
|
+
MODE_SAFE = "safe"
|
|
13
|
+
MODE_WORK = "work"
|
|
14
|
+
MODE_CALL = "call"
|
|
15
|
+
VALID_MODES = {MODE_SAFE, MODE_WORK, MODE_CALL}
|
|
16
|
+
|
|
17
|
+
# Canonical engine/harness vocabulary. This tuple's order is the registry order
|
|
18
|
+
# reused wherever engines are enumerated (the describe payload, help prose, the
|
|
19
|
+
# runs/worktree --harness filters). Membership checks and the prose enumeration
|
|
20
|
+
# both derive from it so the list can never drift out of sync.
|
|
21
|
+
KNOWN_ENGINES = ("cursor", "droid", "codex", "kimi", "claude", "grok")
|
|
22
|
+
# "cursor, droid, codex, kimi, claude, or grok" — for error messages and help text.
|
|
23
|
+
ENGINES_PROSE = f"{', '.join(KNOWN_ENGINES[:-1])}, or {KNOWN_ENGINES[-1]}"
|
|
24
|
+
|
|
25
|
+
# Engines launched without a model-alias positional argument.
|
|
26
|
+
MODELESS_ENGINES = tuple(engine for engine in KNOWN_ENGINES if engine != "droid")
|
|
27
|
+
# Engines whose binary is a simple <engine>.binary config key.
|
|
28
|
+
BINARY_CONFIG_ENGINES = tuple(engine for engine in KNOWN_ENGINES if engine != "cursor")
|
|
29
|
+
# Modeless engines accepted by input JSON's optional model field.
|
|
30
|
+
MODELESS_NONCURSOR_ENGINES = tuple(
|
|
31
|
+
engine for engine in KNOWN_ENGINES if engine not in {"cursor", "droid"}
|
|
32
|
+
)
|
|
33
|
+
# Engines whose safe-review prompt prefix is injected by request_build.effective_prompt.
|
|
34
|
+
SAFE_REVIEW_PREFIX_INJECTED_HERE_ENGINES = tuple(
|
|
35
|
+
engine for engine in KNOWN_ENGINES if engine in {"codex", "droid", "claude", "grok"}
|
|
36
|
+
)
|
|
37
|
+
# Stable public summary order; membership is still derived from the modeless engine set.
|
|
38
|
+
MODEL_SUMMARY_ENGINES = tuple(
|
|
39
|
+
engine for engine in ("cursor", "codex", "claude", "grok", "kimi") if engine in MODELESS_ENGINES
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def validate_mode(mode: str) -> None:
|
|
44
|
+
if mode not in VALID_MODES:
|
|
45
|
+
raise DelegateError(
|
|
46
|
+
"invalid_mode",
|
|
47
|
+
"Mode must be safe, work, or call. Valid forms: "
|
|
48
|
+
"delegate <harness> safe|work <prompt>; "
|
|
49
|
+
"delegate droid <model-alias> safe|work <prompt>.",
|
|
50
|
+
)
|