agencode 0.1.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.
agencli/core/config.py ADDED
@@ -0,0 +1,179 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import asdict, dataclass, field
4
+ from pathlib import Path
5
+ import tomllib
6
+
7
+ import tomli_w
8
+
9
+ from agencli.core.keystore import OPENAI_COMPATIBLE_API_KEY, get_secret, set_secret
10
+ from agencli.core.paths import AgenCLIPaths, default_paths, ensure_dirs
11
+
12
+
13
+ @dataclass(slots=True)
14
+ class OpenAICompatibleConfig:
15
+ provider_name: str = "openai-compatible"
16
+ base_url: str = ""
17
+ model: str = ""
18
+ model_kind: str = "chat"
19
+ api_key_env: str = "OPENAI_API_KEY"
20
+ api_key_name: str = OPENAI_COMPATIBLE_API_KEY
21
+
22
+
23
+ def _infer_api_key_env(provider_name: str, base_url: str, configured_env: str | None = None) -> str:
24
+ if configured_env:
25
+ return configured_env
26
+
27
+ lowered_provider = provider_name.lower()
28
+ lowered_base_url = base_url.lower()
29
+ if "deepseek" in lowered_provider or "deepseek" in lowered_base_url:
30
+ return "DEEPSEEK_API_KEY"
31
+ return "OPENAI_API_KEY"
32
+
33
+
34
+ @dataclass(slots=True)
35
+ class AgenCLIConfig:
36
+ default_model: str = "openai:gpt-4.1"
37
+ log_level: str = "INFO"
38
+ workspace_dir: str = ""
39
+ sessions_dir: str = ""
40
+ agents_dir: str = ""
41
+ skills_dir: str = ""
42
+ mcp_config_path: str = ""
43
+ shell_personality: str = "concise"
44
+ approval_mode: str = "confirm"
45
+ plan_mode: bool = False
46
+ skills_last_query: str = ""
47
+ skills_install_scope: str = "global"
48
+ openai_compatible: OpenAICompatibleConfig = field(default_factory=OpenAICompatibleConfig)
49
+
50
+ @classmethod
51
+ def from_paths(cls, paths: AgenCLIPaths) -> "AgenCLIConfig":
52
+ return cls(
53
+ workspace_dir=str(paths.workspace_dir),
54
+ sessions_dir=str(paths.sessions_dir),
55
+ agents_dir=str(paths.agents_dir),
56
+ skills_dir=str(paths.skills_dir),
57
+ mcp_config_path=str(paths.mcp_file),
58
+ )
59
+
60
+
61
+ def _from_legacy_config(raw: dict, paths: AgenCLIPaths) -> AgenCLIConfig:
62
+ core = raw.get("core", {})
63
+ providers = raw.get("providers", {})
64
+ default_provider = core.get("default_provider", "openai")
65
+ provider_model = providers.get(default_provider, {}).get("model", "")
66
+ selected_provider = providers.get(default_provider, {})
67
+ return AgenCLIConfig(
68
+ default_model=core.get("default_model", provider_model or "openai:gpt-4.1"),
69
+ log_level=core.get("log_level", "INFO"),
70
+ workspace_dir=core.get("workspace_dir", str(paths.workspace_dir)),
71
+ sessions_dir=core.get("session_dir", str(paths.sessions_dir)),
72
+ agents_dir=raw.get("agents_dir", str(paths.agents_dir)),
73
+ skills_dir=raw.get("skills_dir", str(paths.skills_dir)),
74
+ mcp_config_path=raw.get("mcp_config_path", str(paths.mcp_file)),
75
+ skills_last_query=raw.get("skills_last_query", ""),
76
+ skills_install_scope=raw.get("skills_install_scope", "global"),
77
+ openai_compatible=OpenAICompatibleConfig(
78
+ provider_name=default_provider,
79
+ base_url=selected_provider.get("base_url", ""),
80
+ model=provider_model or core.get("default_model", ""),
81
+ model_kind=selected_provider.get("model_kind", "chat"),
82
+ api_key_env=_infer_api_key_env(
83
+ default_provider,
84
+ selected_provider.get("base_url", ""),
85
+ selected_provider.get("api_key_env"),
86
+ ),
87
+ ),
88
+ )
89
+
90
+
91
+ def _from_modern_config(raw: dict) -> AgenCLIConfig:
92
+ openai_compatible_raw = raw.get("openai_compatible", {})
93
+ return AgenCLIConfig(
94
+ default_model=raw.get("default_model", "openai:gpt-4.1"),
95
+ log_level=raw.get("log_level", "INFO"),
96
+ workspace_dir=raw.get("workspace_dir", ""),
97
+ sessions_dir=raw.get("sessions_dir", ""),
98
+ agents_dir=raw.get("agents_dir", ""),
99
+ skills_dir=raw.get("skills_dir", ""),
100
+ mcp_config_path=raw.get("mcp_config_path", ""),
101
+ skills_last_query=raw.get("skills_last_query", ""),
102
+ skills_install_scope=raw.get("skills_install_scope", "global"),
103
+ openai_compatible=OpenAICompatibleConfig(**openai_compatible_raw),
104
+ )
105
+
106
+
107
+ def ensure_config(root: Path | None = None, overwrite: bool = False) -> Path:
108
+ paths = ensure_dirs(default_paths(root))
109
+ if paths.config_file.exists() and not overwrite:
110
+ return paths.config_file
111
+
112
+ config = AgenCLIConfig.from_paths(paths)
113
+ paths.config_file.write_text(tomli_w.dumps(asdict(config)), encoding="utf-8")
114
+ return paths.config_file
115
+
116
+
117
+ def load_config(config_path: Path | None = None) -> AgenCLIConfig:
118
+ if config_path is None:
119
+ config_path = ensure_config()
120
+
121
+ with config_path.open("rb") as handle:
122
+ raw = tomllib.load(handle)
123
+
124
+ root = config_path.parent
125
+ paths = ensure_dirs(default_paths(root))
126
+
127
+ if "core" in raw or "providers" in raw:
128
+ config = _from_legacy_config(raw, paths)
129
+ else:
130
+ config = _from_modern_config(raw)
131
+
132
+ defaults = AgenCLIConfig.from_paths(paths)
133
+ for field in ("workspace_dir", "sessions_dir", "agents_dir", "skills_dir", "mcp_config_path"):
134
+ if not getattr(config, field):
135
+ setattr(config, field, getattr(defaults, field))
136
+
137
+ return config
138
+
139
+
140
+ def save_config(config: AgenCLIConfig, config_path: Path | None = None) -> Path:
141
+ if config_path is None:
142
+ config_path = ensure_config()
143
+ config_path.write_text(tomli_w.dumps(asdict(config)), encoding="utf-8")
144
+ return config_path
145
+
146
+
147
+ def set_openai_compatible_provider(
148
+ *,
149
+ config_path: Path | None = None,
150
+ provider_name: str | None = None,
151
+ base_url: str | None = None,
152
+ model: str | None = None,
153
+ model_kind: str | None = None,
154
+ api_key: str | None = None,
155
+ api_key_env: str | None = None,
156
+ set_as_default: bool = True,
157
+ ) -> AgenCLIConfig:
158
+ config = load_config(config_path)
159
+ provider = config.openai_compatible
160
+ if provider_name is not None:
161
+ provider.provider_name = provider_name
162
+ if base_url is not None:
163
+ provider.base_url = base_url
164
+ if model is not None:
165
+ provider.model = model
166
+ if model_kind is not None:
167
+ provider.model_kind = model_kind
168
+ if api_key_env is not None:
169
+ provider.api_key_env = api_key_env
170
+ if set_as_default and provider.model:
171
+ config.default_model = provider.model
172
+ save_config(config, config_path)
173
+ if api_key is not None:
174
+ set_secret(provider.api_key_name, api_key)
175
+ return config
176
+
177
+
178
+ def get_openai_compatible_api_key(config: AgenCLIConfig) -> str | None:
179
+ return get_secret(config.openai_compatible.api_key_name)
@@ -0,0 +1,14 @@
1
+ from __future__ import annotations
2
+
3
+ import keyring
4
+
5
+ SERVICE_NAME = "agencli"
6
+ OPENAI_COMPATIBLE_API_KEY = "openai_compatible_api_key"
7
+
8
+
9
+ def set_secret(name: str, value: str) -> None:
10
+ keyring.set_password(SERVICE_NAME, name, value)
11
+
12
+
13
+ def get_secret(name: str) -> str | None:
14
+ return keyring.get_password(SERVICE_NAME, name)
agencli/core/logger.py ADDED
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+
5
+
6
+ def configure_logging(level: str = "INFO") -> None:
7
+ logging.basicConfig(
8
+ level=getattr(logging, level.upper(), logging.INFO),
9
+ format="%(asctime)s %(levelname)s %(name)s: %(message)s",
10
+ )
11
+ for noisy_logger in (
12
+ "httpx",
13
+ "httpcore",
14
+ "openai",
15
+ "langchain_openai",
16
+ ):
17
+ logging.getLogger(noisy_logger).setLevel(logging.WARNING)
agencli/core/paths.py ADDED
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+
6
+
7
+ @dataclass(frozen=True)
8
+ class AgenCLIPaths:
9
+ root: Path
10
+ config_file: Path
11
+ mcp_file: Path
12
+ agents_dir: Path
13
+ skills_dir: Path
14
+ sessions_dir: Path
15
+ workspace_dir: Path
16
+
17
+
18
+ def default_paths(root: Path | None = None) -> AgenCLIPaths:
19
+ base = (root or (Path.home() / ".agencli")).expanduser()
20
+ return AgenCLIPaths(
21
+ root=base,
22
+ config_file=base / "config.toml",
23
+ mcp_file=base / "mcp.json",
24
+ agents_dir=base / "agents",
25
+ skills_dir=base / "skills",
26
+ sessions_dir=base / "sessions",
27
+ workspace_dir=base / "workspace",
28
+ )
29
+
30
+
31
+ def ensure_dirs(paths: AgenCLIPaths) -> AgenCLIPaths:
32
+ paths.root.mkdir(parents=True, exist_ok=True)
33
+ paths.agents_dir.mkdir(parents=True, exist_ok=True)
34
+ paths.skills_dir.mkdir(parents=True, exist_ok=True)
35
+ paths.sessions_dir.mkdir(parents=True, exist_ok=True)
36
+ paths.workspace_dir.mkdir(parents=True, exist_ok=True)
37
+ return paths