kollabor 0.4.9__py3-none-any.whl → 0.4.15__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.
- agents/__init__.py +2 -0
- agents/coder/__init__.py +0 -0
- agents/coder/agent.json +4 -0
- agents/coder/api-integration.md +2150 -0
- agents/coder/cli-pretty.md +765 -0
- agents/coder/code-review.md +1092 -0
- agents/coder/database-design.md +1525 -0
- agents/coder/debugging.md +1102 -0
- agents/coder/dependency-management.md +1397 -0
- agents/coder/git-workflow.md +1099 -0
- agents/coder/refactoring.md +1454 -0
- agents/coder/security-hardening.md +1732 -0
- agents/coder/system_prompt.md +1448 -0
- agents/coder/tdd.md +1367 -0
- agents/creative-writer/__init__.py +0 -0
- agents/creative-writer/agent.json +4 -0
- agents/creative-writer/character-development.md +1852 -0
- agents/creative-writer/dialogue-craft.md +1122 -0
- agents/creative-writer/plot-structure.md +1073 -0
- agents/creative-writer/revision-editing.md +1484 -0
- agents/creative-writer/system_prompt.md +690 -0
- agents/creative-writer/worldbuilding.md +2049 -0
- agents/data-analyst/__init__.py +30 -0
- agents/data-analyst/agent.json +4 -0
- agents/data-analyst/data-visualization.md +992 -0
- agents/data-analyst/exploratory-data-analysis.md +1110 -0
- agents/data-analyst/pandas-data-manipulation.md +1081 -0
- agents/data-analyst/sql-query-optimization.md +881 -0
- agents/data-analyst/statistical-analysis.md +1118 -0
- agents/data-analyst/system_prompt.md +928 -0
- agents/default/__init__.py +0 -0
- agents/default/agent.json +4 -0
- agents/default/dead-code.md +794 -0
- agents/default/explore-agent-system.md +585 -0
- agents/default/system_prompt.md +1448 -0
- agents/kollabor/__init__.py +0 -0
- agents/kollabor/analyze-plugin-lifecycle.md +175 -0
- agents/kollabor/analyze-terminal-rendering.md +388 -0
- agents/kollabor/code-review.md +1092 -0
- agents/kollabor/debug-mcp-integration.md +521 -0
- agents/kollabor/debug-plugin-hooks.md +547 -0
- agents/kollabor/debugging.md +1102 -0
- agents/kollabor/dependency-management.md +1397 -0
- agents/kollabor/git-workflow.md +1099 -0
- agents/kollabor/inspect-llm-conversation.md +148 -0
- agents/kollabor/monitor-event-bus.md +558 -0
- agents/kollabor/profile-performance.md +576 -0
- agents/kollabor/refactoring.md +1454 -0
- agents/kollabor/system_prompt copy.md +1448 -0
- agents/kollabor/system_prompt.md +757 -0
- agents/kollabor/trace-command-execution.md +178 -0
- agents/kollabor/validate-config.md +879 -0
- agents/research/__init__.py +0 -0
- agents/research/agent.json +4 -0
- agents/research/architecture-mapping.md +1099 -0
- agents/research/codebase-analysis.md +1077 -0
- agents/research/dependency-audit.md +1027 -0
- agents/research/performance-profiling.md +1047 -0
- agents/research/security-review.md +1359 -0
- agents/research/system_prompt.md +492 -0
- agents/technical-writer/__init__.py +0 -0
- agents/technical-writer/agent.json +4 -0
- agents/technical-writer/api-documentation.md +2328 -0
- agents/technical-writer/changelog-management.md +1181 -0
- agents/technical-writer/readme-writing.md +1360 -0
- agents/technical-writer/style-guide.md +1410 -0
- agents/technical-writer/system_prompt.md +653 -0
- agents/technical-writer/tutorial-creation.md +1448 -0
- core/__init__.py +0 -2
- core/application.py +343 -88
- core/cli.py +229 -10
- core/commands/menu_renderer.py +463 -59
- core/commands/registry.py +14 -9
- core/commands/system_commands.py +2461 -14
- core/config/loader.py +151 -37
- core/config/service.py +18 -6
- core/events/bus.py +29 -9
- core/events/executor.py +205 -75
- core/events/models.py +27 -8
- core/fullscreen/command_integration.py +20 -24
- core/fullscreen/components/__init__.py +10 -1
- core/fullscreen/components/matrix_components.py +1 -2
- core/fullscreen/components/space_shooter_components.py +654 -0
- core/fullscreen/plugin.py +5 -0
- core/fullscreen/renderer.py +52 -13
- core/fullscreen/session.py +52 -15
- core/io/__init__.py +29 -5
- core/io/buffer_manager.py +6 -1
- core/io/config_status_view.py +7 -29
- core/io/core_status_views.py +267 -347
- core/io/input/__init__.py +25 -0
- core/io/input/command_mode_handler.py +711 -0
- core/io/input/display_controller.py +128 -0
- core/io/input/hook_registrar.py +286 -0
- core/io/input/input_loop_manager.py +421 -0
- core/io/input/key_press_handler.py +502 -0
- core/io/input/modal_controller.py +1011 -0
- core/io/input/paste_processor.py +339 -0
- core/io/input/status_modal_renderer.py +184 -0
- core/io/input_errors.py +5 -1
- core/io/input_handler.py +211 -2452
- core/io/key_parser.py +7 -0
- core/io/layout.py +15 -3
- core/io/message_coordinator.py +111 -2
- core/io/message_renderer.py +129 -4
- core/io/status_renderer.py +147 -607
- core/io/terminal_renderer.py +97 -51
- core/io/terminal_state.py +21 -4
- core/io/visual_effects.py +816 -165
- core/llm/agent_manager.py +1063 -0
- core/llm/api_adapters/__init__.py +44 -0
- core/llm/api_adapters/anthropic_adapter.py +432 -0
- core/llm/api_adapters/base.py +241 -0
- core/llm/api_adapters/openai_adapter.py +326 -0
- core/llm/api_communication_service.py +167 -113
- core/llm/conversation_logger.py +322 -16
- core/llm/conversation_manager.py +556 -30
- core/llm/file_operations_executor.py +84 -32
- core/llm/llm_service.py +934 -103
- core/llm/mcp_integration.py +541 -57
- core/llm/message_display_service.py +135 -18
- core/llm/plugin_sdk.py +1 -2
- core/llm/profile_manager.py +1183 -0
- core/llm/response_parser.py +274 -56
- core/llm/response_processor.py +16 -3
- core/llm/tool_executor.py +6 -1
- core/logging/__init__.py +2 -0
- core/logging/setup.py +34 -6
- core/models/resume.py +54 -0
- core/plugins/__init__.py +4 -2
- core/plugins/base.py +127 -0
- core/plugins/collector.py +23 -161
- core/plugins/discovery.py +37 -3
- core/plugins/factory.py +6 -12
- core/plugins/registry.py +5 -17
- core/ui/config_widgets.py +128 -28
- core/ui/live_modal_renderer.py +2 -1
- core/ui/modal_actions.py +5 -0
- core/ui/modal_overlay_renderer.py +0 -60
- core/ui/modal_renderer.py +268 -7
- core/ui/modal_state_manager.py +29 -4
- core/ui/widgets/base_widget.py +7 -0
- core/updates/__init__.py +10 -0
- core/updates/version_check_service.py +348 -0
- core/updates/version_comparator.py +103 -0
- core/utils/config_utils.py +685 -526
- core/utils/plugin_utils.py +1 -1
- core/utils/session_naming.py +111 -0
- fonts/LICENSE +21 -0
- fonts/README.md +46 -0
- fonts/SymbolsNerdFont-Regular.ttf +0 -0
- fonts/SymbolsNerdFontMono-Regular.ttf +0 -0
- fonts/__init__.py +44 -0
- {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/METADATA +54 -4
- kollabor-0.4.15.dist-info/RECORD +228 -0
- {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/top_level.txt +2 -0
- plugins/agent_orchestrator/__init__.py +39 -0
- plugins/agent_orchestrator/activity_monitor.py +181 -0
- plugins/agent_orchestrator/file_attacher.py +77 -0
- plugins/agent_orchestrator/message_injector.py +135 -0
- plugins/agent_orchestrator/models.py +48 -0
- plugins/agent_orchestrator/orchestrator.py +403 -0
- plugins/agent_orchestrator/plugin.py +976 -0
- plugins/agent_orchestrator/xml_parser.py +191 -0
- plugins/agent_orchestrator_plugin.py +9 -0
- plugins/enhanced_input/box_styles.py +1 -0
- plugins/enhanced_input/color_engine.py +19 -4
- plugins/enhanced_input/config.py +2 -2
- plugins/enhanced_input_plugin.py +61 -11
- plugins/fullscreen/__init__.py +6 -2
- plugins/fullscreen/example_plugin.py +1035 -222
- plugins/fullscreen/setup_wizard_plugin.py +592 -0
- plugins/fullscreen/space_shooter_plugin.py +131 -0
- plugins/hook_monitoring_plugin.py +436 -78
- plugins/query_enhancer_plugin.py +66 -30
- plugins/resume_conversation_plugin.py +1494 -0
- plugins/save_conversation_plugin.py +98 -32
- plugins/system_commands_plugin.py +70 -56
- plugins/tmux_plugin.py +154 -78
- plugins/workflow_enforcement_plugin.py +94 -92
- system_prompt/default.md +952 -886
- core/io/input_mode_manager.py +0 -402
- core/io/modal_interaction_handler.py +0 -315
- core/io/raw_input_processor.py +0 -946
- core/storage/__init__.py +0 -5
- core/storage/state_manager.py +0 -84
- core/ui/widget_integration.py +0 -222
- core/utils/key_reader.py +0 -171
- kollabor-0.4.9.dist-info/RECORD +0 -128
- {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/WHEEL +0 -0
- {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/entry_points.txt +0 -0
- {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/licenses/LICENSE +0 -0
core/utils/config_utils.py
CHANGED
|
@@ -5,6 +5,7 @@ import sys
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
import logging
|
|
7
7
|
import shutil
|
|
8
|
+
from typing import Optional
|
|
8
9
|
|
|
9
10
|
logger = logging.getLogger(__name__)
|
|
10
11
|
|
|
@@ -12,33 +13,235 @@ logger = logging.getLogger(__name__)
|
|
|
12
13
|
IS_WINDOWS = sys.platform == "win32"
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
# ============================================================================
|
|
17
|
+
# Project Data Path Utilities
|
|
18
|
+
# ============================================================================
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
def encode_project_path(project_path: Path) -> str:
|
|
21
|
+
"""Encode a project path to a safe directory name.
|
|
22
|
+
|
|
23
|
+
Replaces path separators (/ and \\) with underscores and strips
|
|
24
|
+
leading underscores for use as a directory name.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
project_path: Path to encode
|
|
21
28
|
|
|
22
29
|
Returns:
|
|
23
|
-
|
|
30
|
+
Encoded string safe for use as directory name
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
>>> encode_project_path(Path("/Users/malmazan/dev/hello_world"))
|
|
34
|
+
'Users_malmazan_dev_hello_world'
|
|
35
|
+
>>> encode_project_path(Path("C:\\\\Users\\\\dev\\\\project"))
|
|
36
|
+
'C_Users_dev_project'
|
|
24
37
|
"""
|
|
25
|
-
|
|
26
|
-
|
|
38
|
+
path_str = str(project_path.resolve())
|
|
39
|
+
# Replace path separators with underscores
|
|
40
|
+
encoded = path_str.replace("/", "_").replace("\\", "_")
|
|
41
|
+
# Remove leading underscore if present (from root /)
|
|
42
|
+
while encoded.startswith("_"):
|
|
43
|
+
encoded = encoded[1:]
|
|
44
|
+
return encoded
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def decode_project_path(encoded: str) -> Path:
|
|
48
|
+
"""Decode an encoded project path back to a Path.
|
|
49
|
+
|
|
50
|
+
Reverses the encoding done by encode_project_path().
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
encoded: Encoded project path string
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Original Path object
|
|
27
57
|
|
|
28
|
-
|
|
29
|
-
|
|
58
|
+
Examples:
|
|
59
|
+
>>> decode_project_path("Users_malmazan_dev_hello_world")
|
|
60
|
+
Path('/Users/malmazan/dev/hello_world')
|
|
61
|
+
>>> decode_project_path("C_Users_dev_project")
|
|
62
|
+
Path('C:\\\\Users\\\\dev\\\\project')
|
|
63
|
+
"""
|
|
64
|
+
# Detect Windows paths (start with drive letter + underscore)
|
|
65
|
+
if len(encoded) > 1 and encoded[1] == "_" and encoded[0].isalpha():
|
|
66
|
+
# Windows: C_Users_... -> C:\Users\...
|
|
67
|
+
path_str = encoded[0] + ":\\" + encoded[2:].replace("_", "\\")
|
|
30
68
|
else:
|
|
31
|
-
|
|
69
|
+
# Unix: Users_malmazan_... -> /Users/malmazan/...
|
|
70
|
+
path_str = "/" + encoded.replace("_", "/")
|
|
71
|
+
return Path(path_str)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def get_project_data_dir(project_path: Path = None) -> Path:
|
|
75
|
+
"""Get the centralized project data directory.
|
|
76
|
+
|
|
77
|
+
Returns ~/.kollabor-cli/projects/<encoded-path>/ for the current
|
|
78
|
+
or specified project.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
project_path: Path to project directory. If None, uses Path.cwd()
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Path to project-specific data directory
|
|
85
|
+
"""
|
|
86
|
+
if project_path is None:
|
|
87
|
+
project_path = Path.cwd()
|
|
88
|
+
|
|
89
|
+
encoded = encode_project_path(project_path)
|
|
90
|
+
return Path.home() / ".kollabor-cli" / "projects" / encoded
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def get_conversations_dir(project_path: Path = None) -> Path:
|
|
94
|
+
"""Get the conversations directory for a project.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
project_path: Path to project directory. If None, uses Path.cwd()
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Path to project's conversations directory
|
|
101
|
+
"""
|
|
102
|
+
return get_project_data_dir(project_path) / "conversations"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def get_logs_dir(project_path: Path = None) -> Path:
|
|
106
|
+
"""Get the logs directory for a project.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
project_path: Path to project directory. If None, uses Path.cwd()
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Path to project's logs directory
|
|
113
|
+
"""
|
|
114
|
+
return get_project_data_dir(project_path) / "logs"
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def get_local_agents_dir() -> Path | None:
|
|
118
|
+
"""Get the local agents directory if it exists.
|
|
119
|
+
|
|
120
|
+
Checks for .kollabor-cli/agents/ in the current working directory.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Path to local agents directory if it exists, None otherwise
|
|
124
|
+
"""
|
|
125
|
+
local_agents_dir = Path.cwd() / ".kollabor-cli" / "agents"
|
|
126
|
+
if local_agents_dir.exists():
|
|
127
|
+
return local_agents_dir
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def get_local_agents_path() -> Path:
|
|
132
|
+
"""Get the local agents directory path (for creation purposes).
|
|
133
|
+
|
|
134
|
+
Unlike get_local_agents_dir(), this returns the path even if it doesn't exist.
|
|
135
|
+
Use this when you need to create the local agents directory.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Path to .kollabor-cli/agents/ in the current working directory
|
|
139
|
+
"""
|
|
140
|
+
return Path.cwd() / ".kollabor-cli" / "agents"
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def get_global_agents_dir() -> Path:
|
|
144
|
+
"""Get the global agents directory.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Path to ~/.kollabor-cli/agents/
|
|
148
|
+
"""
|
|
149
|
+
return Path.home() / ".kollabor-cli" / "agents"
|
|
150
|
+
|
|
151
|
+
# CLI override for system prompt file (set via --system-prompt argument)
|
|
152
|
+
_cli_system_prompt_file: str | None = None
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def set_cli_system_prompt_file(file_path: str | None) -> None:
|
|
156
|
+
"""Set the CLI override for system prompt file.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
file_path: Path to the system prompt file, or None to clear
|
|
160
|
+
"""
|
|
161
|
+
global _cli_system_prompt_file
|
|
162
|
+
_cli_system_prompt_file = file_path
|
|
163
|
+
if file_path:
|
|
164
|
+
logger.info(f"CLI system prompt override set: {file_path}")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _resolve_system_prompt_path(filename: str) -> Path | None:
|
|
168
|
+
"""Resolve a system prompt filename to a full path.
|
|
169
|
+
|
|
170
|
+
Searches in order:
|
|
171
|
+
1. As-is (if absolute path or exists in cwd)
|
|
172
|
+
2. Local .kollabor-cli/agents/default/
|
|
173
|
+
3. Global ~/.kollabor-cli/agents/default/
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
filename: The filename or path provided by the user
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Resolved Path if found, None otherwise
|
|
180
|
+
"""
|
|
181
|
+
# Expand ~ in path
|
|
182
|
+
expanded = Path(filename).expanduser()
|
|
183
|
+
|
|
184
|
+
# 1. Check as-is (absolute path or relative from cwd)
|
|
185
|
+
if expanded.exists():
|
|
186
|
+
return expanded
|
|
187
|
+
|
|
188
|
+
# If it's an absolute path that doesn't exist, don't search further
|
|
189
|
+
if expanded.is_absolute():
|
|
190
|
+
return None
|
|
191
|
+
|
|
192
|
+
# Get just the filename for searching in directories
|
|
193
|
+
name = expanded.name
|
|
194
|
+
|
|
195
|
+
# Also try with .md extension if not present
|
|
196
|
+
names_to_try = [name]
|
|
197
|
+
if not name.endswith('.md'):
|
|
198
|
+
names_to_try.append(f"{name}.md")
|
|
199
|
+
|
|
200
|
+
# 2. Local .kollabor-cli/agents/default/
|
|
201
|
+
local_agent_dir = Path.cwd() / ".kollabor-cli" / "agents" / "default"
|
|
202
|
+
for n in names_to_try:
|
|
203
|
+
candidate = local_agent_dir / n
|
|
204
|
+
if candidate.exists():
|
|
205
|
+
return candidate
|
|
206
|
+
|
|
207
|
+
# 3. Global ~/.kollabor-cli/agents/default/
|
|
208
|
+
global_agent_dir = Path.home() / ".kollabor-cli" / "agents" / "default"
|
|
209
|
+
for n in names_to_try:
|
|
210
|
+
candidate = global_agent_dir / n
|
|
211
|
+
if candidate.exists():
|
|
212
|
+
return candidate
|
|
213
|
+
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def get_config_directory() -> Path:
|
|
218
|
+
"""Get the global Kollabor configuration directory.
|
|
219
|
+
|
|
220
|
+
ALWAYS returns ~/.kollabor-cli/ regardless of current directory.
|
|
221
|
+
Project-specific data is stored under ~/.kollabor-cli/projects/<encoded>/.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Path to the global configuration directory (~/.kollabor-cli/)
|
|
225
|
+
"""
|
|
226
|
+
return Path.home() / ".kollabor-cli"
|
|
32
227
|
|
|
33
228
|
|
|
34
229
|
def ensure_config_directory() -> Path:
|
|
35
230
|
"""Get and ensure the configuration directory exists.
|
|
36
231
|
|
|
232
|
+
Creates both the global config directory and the project-specific
|
|
233
|
+
data directory under ~/.kollabor-cli/projects/<encoded>/.
|
|
234
|
+
|
|
37
235
|
Returns:
|
|
38
|
-
Path to the configuration directory
|
|
236
|
+
Path to the global configuration directory
|
|
39
237
|
"""
|
|
40
238
|
config_dir = get_config_directory()
|
|
41
239
|
config_dir.mkdir(exist_ok=True)
|
|
240
|
+
|
|
241
|
+
# Also ensure project data directory exists
|
|
242
|
+
project_data_dir = get_project_data_dir()
|
|
243
|
+
project_data_dir.mkdir(parents=True, exist_ok=True)
|
|
244
|
+
|
|
42
245
|
return config_dir
|
|
43
246
|
|
|
44
247
|
|
|
@@ -47,8 +250,8 @@ def get_system_prompt_path() -> Path:
|
|
|
47
250
|
|
|
48
251
|
Resolution order:
|
|
49
252
|
1. KOLLABOR_SYSTEM_PROMPT_FILE environment variable (custom file path)
|
|
50
|
-
2. Local .kollabor-cli/
|
|
51
|
-
3. Global ~/.kollabor-cli/
|
|
253
|
+
2. Local .kollabor-cli/agents/default/system_prompt.md (project-specific)
|
|
254
|
+
3. Global ~/.kollabor-cli/agents/default/system_prompt.md (global default)
|
|
52
255
|
|
|
53
256
|
Returns:
|
|
54
257
|
Path to the system prompt file
|
|
@@ -66,11 +269,14 @@ def get_system_prompt_path() -> Path:
|
|
|
66
269
|
local_config_dir = Path.cwd() / ".kollabor-cli"
|
|
67
270
|
global_config_dir = Path.home() / ".kollabor-cli"
|
|
68
271
|
|
|
69
|
-
#
|
|
272
|
+
# New agent-based paths
|
|
273
|
+
local_agent_prompt = local_config_dir / "agents" / "default" / "system_prompt.md"
|
|
274
|
+
global_agent_prompt = global_config_dir / "agents" / "default" / "system_prompt.md"
|
|
275
|
+
|
|
276
|
+
# On Windows, prefer default_win.md if it exists (in agent directory)
|
|
70
277
|
if IS_WINDOWS:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
global_win_prompt = global_config_dir / "system_prompt" / "default_win.md"
|
|
278
|
+
local_win_prompt = local_config_dir / "agents" / "default" / "system_prompt_win.md"
|
|
279
|
+
global_win_prompt = global_config_dir / "agents" / "default" / "system_prompt_win.md"
|
|
74
280
|
|
|
75
281
|
if local_win_prompt.exists():
|
|
76
282
|
logger.debug(f"Using Windows-specific system prompt: {local_win_prompt}")
|
|
@@ -79,32 +285,59 @@ def get_system_prompt_path() -> Path:
|
|
|
79
285
|
logger.debug(f"Using Windows-specific system prompt: {global_win_prompt}")
|
|
80
286
|
return global_win_prompt
|
|
81
287
|
|
|
82
|
-
# Fall back to default.md
|
|
83
|
-
local_system_prompt = local_config_dir / "system_prompt" / "default.md"
|
|
84
|
-
global_system_prompt = global_config_dir / "system_prompt" / "default.md"
|
|
85
|
-
|
|
86
288
|
# If local exists, use it (override)
|
|
87
|
-
if
|
|
88
|
-
return
|
|
289
|
+
if local_agent_prompt.exists():
|
|
290
|
+
return local_agent_prompt
|
|
89
291
|
# Otherwise use global
|
|
90
292
|
else:
|
|
91
|
-
return
|
|
293
|
+
return global_agent_prompt
|
|
92
294
|
|
|
93
295
|
|
|
94
296
|
def get_system_prompt_content() -> str:
|
|
95
|
-
"""Get the system prompt content, checking env vars and files.
|
|
297
|
+
"""Get the system prompt content, checking CLI args, env vars, and files.
|
|
96
298
|
|
|
97
299
|
Resolution order:
|
|
98
|
-
1.
|
|
99
|
-
2.
|
|
100
|
-
3.
|
|
101
|
-
4.
|
|
102
|
-
5.
|
|
300
|
+
1. CLI --system-prompt argument (highest priority)
|
|
301
|
+
2. KOLLABOR_SYSTEM_PROMPT environment variable (direct string)
|
|
302
|
+
3. KOLLABOR_SYSTEM_PROMPT_FILE environment variable (custom file path)
|
|
303
|
+
4. Local .kollabor-cli/agents/default/system_prompt.md (project-specific override)
|
|
304
|
+
5. Global ~/.kollabor-cli/agents/default/system_prompt.md (global default)
|
|
305
|
+
6. Fallback to minimal default
|
|
103
306
|
|
|
104
307
|
Returns:
|
|
105
308
|
System prompt content as string
|
|
106
309
|
"""
|
|
107
|
-
|
|
310
|
+
global _cli_system_prompt_file
|
|
311
|
+
|
|
312
|
+
# Check for CLI override (highest priority)
|
|
313
|
+
if _cli_system_prompt_file:
|
|
314
|
+
cli_path = _resolve_system_prompt_path(_cli_system_prompt_file)
|
|
315
|
+
if cli_path and cli_path.exists():
|
|
316
|
+
try:
|
|
317
|
+
content = cli_path.read_text(encoding='utf-8')
|
|
318
|
+
logger.info(f"Loaded system prompt from CLI argument: {cli_path}")
|
|
319
|
+
return content
|
|
320
|
+
except Exception as e:
|
|
321
|
+
logger.error(f"Failed to read CLI system prompt from {cli_path}: {e}")
|
|
322
|
+
else:
|
|
323
|
+
logger.error(f"CLI system prompt file not found: {_cli_system_prompt_file}")
|
|
324
|
+
# Don't fall through - this is an explicit user request, so fail clearly
|
|
325
|
+
return f"""[SYSTEM PROMPT LOAD FAILURE]
|
|
326
|
+
|
|
327
|
+
The system prompt file specified via --system-prompt was not found:
|
|
328
|
+
{_cli_system_prompt_file}
|
|
329
|
+
|
|
330
|
+
Searched in:
|
|
331
|
+
- Current directory
|
|
332
|
+
- .kollabor-cli/system_prompt/
|
|
333
|
+
- ~/.kollabor-cli/system_prompt/
|
|
334
|
+
|
|
335
|
+
Please check the file path and try again.
|
|
336
|
+
|
|
337
|
+
I'll do my best to help, but my responses may not follow the expected format.
|
|
338
|
+
"""
|
|
339
|
+
|
|
340
|
+
# Check for direct environment variable string
|
|
108
341
|
env_prompt = os.environ.get("KOLLABOR_SYSTEM_PROMPT")
|
|
109
342
|
if env_prompt:
|
|
110
343
|
logger.debug("Using system prompt from KOLLABOR_SYSTEM_PROMPT environment variable")
|
|
@@ -128,529 +361,455 @@ def get_system_prompt_content() -> str:
|
|
|
128
361
|
def get_default_system_prompt() -> str:
|
|
129
362
|
"""Get the default system prompt content when no file exists.
|
|
130
363
|
|
|
364
|
+
Returns a minimal fallback that alerts the user about the missing prompt.
|
|
365
|
+
|
|
131
366
|
Returns:
|
|
132
367
|
Default system prompt string
|
|
133
368
|
"""
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
<terminal>ls -la src/</terminal>
|
|
156
|
-
<terminal>grep -r "function_name" .</terminal>
|
|
157
|
-
<terminal>cat important_file.py</terminal>
|
|
158
|
-
|
|
159
|
-
NEVER write commands in markdown code blocks - they won't execute!
|
|
160
|
-
|
|
161
|
-
STANDARD INVESTIGATION PATTERN:
|
|
162
|
-
1. Orient: ls, pwd, find to understand project structure
|
|
163
|
-
2. Search: grep, rg, ag to find relevant code/files
|
|
164
|
-
3. Examine: cat, head, tail to read specific files
|
|
165
|
-
4. Analyze: wc, diff, stat for metrics and comparisons
|
|
166
|
-
5. Act: Make changes with sed, awk, file operations
|
|
167
|
-
6. Verify: Confirm changes with additional terminal commands
|
|
168
|
-
|
|
169
|
-
> RESPONSE PATTERN SELECTION
|
|
170
|
-
|
|
171
|
-
CLASSIFY BEFORE RESPONDING:
|
|
172
|
-
|
|
173
|
-
Type A - Simple Information: Answer immediately with tools
|
|
174
|
-
Examples: "list files", "show config", "what does X do?"
|
|
175
|
-
|
|
176
|
-
Type B - Complex Implementation: Ask questions FIRST, implement AFTER
|
|
177
|
-
Examples: "add feature X", "implement Y", "refactor Z"
|
|
178
|
-
|
|
179
|
-
Type C - Debugging/Investigation: Iterative discovery with tools
|
|
180
|
-
Examples: "why is X broken?", "debug error Y"
|
|
181
|
-
|
|
182
|
-
RED FLAGS - ASK QUESTIONS BEFORE IMPLEMENTING:
|
|
183
|
-
X Vague request ("make it better", "add error handling")
|
|
184
|
-
X Missing details ("add logging" - what level? where? how?)
|
|
185
|
-
X Multiple approaches ("implement caching" - memory? disk? redis?)
|
|
186
|
-
X Unclear scope ("update the service" - which part? how much?)
|
|
187
|
-
X Ambiguous requirements ("improve performance" - where? by how much?)
|
|
188
|
-
X Could affect multiple systems ("change the API")
|
|
189
|
-
X User hasn't confirmed approach
|
|
190
|
-
|
|
191
|
-
IF YOU SEE ANY RED FLAG -> ASK CLARIFYING QUESTIONS FIRST!
|
|
192
|
-
|
|
193
|
-
> INVESTIGATION EXAMPLES
|
|
194
|
-
|
|
195
|
-
EXAMPLE 1: Simple Information (Immediate Answer)
|
|
196
|
-
|
|
197
|
-
User: "list all Python files in plugins/"
|
|
198
|
-
|
|
199
|
-
<terminal>ls -la plugins/</terminal>
|
|
200
|
-
<terminal>find plugins/ -name "*.py" -type f</terminal>
|
|
201
|
-
<terminal>tree plugins/ 2>/dev/null || find plugins/ -type f | sort</terminal>
|
|
202
|
-
|
|
203
|
-
Shows results directly - no questions needed.
|
|
204
|
-
|
|
205
|
-
---
|
|
206
|
-
|
|
207
|
-
EXAMPLE 2: Complex Implementation (Ask First)
|
|
208
|
-
|
|
209
|
-
User: "add logging to the LLM service"
|
|
210
|
-
|
|
211
|
-
WRONG (immediate implementation):
|
|
212
|
-
<terminal>cat core/llm/llm_service.py</terminal>
|
|
213
|
-
<terminal>sed -i '1 a\\import logging' core/llm/llm_service.py</terminal>
|
|
214
|
-
Done! Added logging.
|
|
215
|
-
|
|
216
|
-
CORRECT (ask clarifying questions):
|
|
217
|
-
<terminal>cat core/llm/llm_service.py</terminal>
|
|
218
|
-
<terminal>grep -r "import logging" core/llm/</terminal>
|
|
219
|
-
|
|
220
|
-
After seeing current state, ask:
|
|
221
|
-
|
|
222
|
-
QUESTIONS:
|
|
223
|
-
1. Logging level? (DEBUG, INFO, WARNING, ERROR)
|
|
224
|
-
2. Which operations? (all methods, just API calls, only errors?)
|
|
225
|
-
3. Log destination? (file, console, both?)
|
|
226
|
-
4. Use existing logger? (I see other modules use logging)
|
|
227
|
-
|
|
228
|
-
RECOMMENDATION: Add INFO logging for API calls, ERROR for failures,
|
|
229
|
-
using existing logging setup.
|
|
230
|
-
|
|
231
|
-
Does this match what you want, or should I adjust?
|
|
232
|
-
|
|
233
|
-
WAIT FOR USER CONFIRMATION - then implement in next response.
|
|
234
|
-
|
|
235
|
-
---
|
|
236
|
-
|
|
237
|
-
EXAMPLE 3: File Operations for Code Changes
|
|
238
|
-
|
|
239
|
-
User: "fix the logging bug in LLM service"
|
|
240
|
-
|
|
241
|
-
WRONG (using sed/awk):
|
|
242
|
-
<terminal>sed -i 's/logger.info/logger.debug/g' core/llm/llm_service.py</terminal>
|
|
243
|
-
|
|
244
|
-
CORRECT (using file operations):
|
|
245
|
-
<read>
|
|
246
|
-
<file>core/llm/llm_service.py</file>
|
|
247
|
-
<lines>1-30</lines>
|
|
248
|
-
</read>
|
|
249
|
-
|
|
250
|
-
After seeing the actual code and confirming fix needed:
|
|
251
|
-
|
|
252
|
-
<edit>
|
|
253
|
-
<file>core/llm/llm_service.py</file>
|
|
254
|
-
<find>
|
|
255
|
-
def process_request(self, request):
|
|
256
|
-
logger.info(f"Processing: {request}")
|
|
257
|
-
return self.handler(request)
|
|
258
|
-
</find>
|
|
259
|
-
<replace>
|
|
260
|
-
def process_request(self, request):
|
|
261
|
-
logger.debug(f"Processing: {request}")
|
|
262
|
-
return self.handler(request)
|
|
263
|
-
</replace>
|
|
264
|
-
</edit>
|
|
265
|
-
|
|
266
|
-
WHY FILE OPERATIONS ARE BETTER:
|
|
267
|
-
- Automatic .bak backup created
|
|
268
|
-
- Python syntax validation prevents breaking code
|
|
269
|
-
- Clear success/error messages
|
|
270
|
-
- Shows exact lines changed
|
|
271
|
-
- Can rollback if syntax error
|
|
272
|
-
|
|
273
|
-
Verify the fix:
|
|
274
|
-
<read>
|
|
275
|
-
<file>core/llm/llm_service.py</file>
|
|
276
|
-
<lines>25-30</lines>
|
|
277
|
-
</read>
|
|
278
|
-
|
|
279
|
-
> TASK PLANNING SYSTEM
|
|
280
|
-
|
|
281
|
-
Every response must include todo list:
|
|
282
|
-
- Shows terminal commands you'll execute
|
|
283
|
-
- Tracks investigation -> implementation -> verification
|
|
284
|
-
- Updates as you complete each step
|
|
285
|
-
|
|
286
|
-
TODO FORMAT:
|
|
287
|
-
|
|
288
|
-
Todo List
|
|
289
|
-
- [ ] Explore project structure: ls -la && find . -name "*.py" | head -10
|
|
290
|
-
- [ ] Search for existing patterns: grep -r "similar_feature" src/
|
|
291
|
-
- [ ] Examine relevant files: cat src/target_file.py
|
|
292
|
-
- [ ] Identify modification points: grep -n "function_to_modify" src/
|
|
293
|
-
- [ ] Implement changes: sed -i 's/old/new/' src/target_file.py
|
|
294
|
-
- [ ] Verify implementation: grep -A5 -B5 "new" src/target_file.py
|
|
295
|
-
- [ ] Test functionality: python -m pytest tests/
|
|
296
|
-
|
|
297
|
-
Mark items as complete when finished:
|
|
298
|
-
- [x] Explore project structure (done)
|
|
299
|
-
- [x] Search for existing patterns (done)
|
|
300
|
-
- [ ] Examine relevant files
|
|
301
|
-
- [ ] Implement changes
|
|
302
|
-
|
|
303
|
-
> DEVELOPMENT EXPERTISE
|
|
304
|
-
|
|
305
|
-
COMMAND ARSENAL:
|
|
306
|
-
|
|
307
|
-
File Operations: ls, find, locate, which, tree, cat, head, tail, less,
|
|
308
|
-
cp, mv, mkdir, touch, rm
|
|
309
|
-
|
|
310
|
-
Text Processing: grep, rg, ag, egrep, fgrep, sed, awk, cut, sort, uniq,
|
|
311
|
-
wc, tr, diff, comm
|
|
312
|
-
|
|
313
|
-
System Analysis: ps, top, htop, lsof, netstat, df, du, free, iostat,
|
|
314
|
-
strace, ltrace, gdb
|
|
315
|
-
|
|
316
|
-
Development Tools: git (status, log, diff, add, commit, branch),
|
|
317
|
-
make, npm, pip, cargo, go, python -m, node
|
|
318
|
-
|
|
319
|
-
CODE STANDARDS:
|
|
320
|
-
- Follow existing patterns: Match indentation, naming, structure
|
|
321
|
-
- Verify compatibility: Check imports, dependencies, versions
|
|
322
|
-
- Test immediately: Run tests after changes
|
|
323
|
-
- Clean implementation: Readable, maintainable, documented
|
|
324
|
-
|
|
325
|
-
> COMMUNICATION PROTOCOL
|
|
326
|
-
|
|
327
|
-
RESPONSE STRUCTURE:
|
|
328
|
-
1. Todo List: Clear investigation -> implementation -> verification plan
|
|
329
|
-
2. Active Investigation: Multiple terminal commands showing exploration
|
|
330
|
-
3. Evidence-Based Analysis: Conclusions from actual file contents
|
|
331
|
-
4. Practical Implementation: Concrete changes using terminal tools
|
|
332
|
-
5. Verification: Confirm changes work as expected
|
|
333
|
-
6. Updated Todo List: Mark completed items, show progress
|
|
334
|
-
|
|
335
|
-
RESPONSE TEMPLATES:
|
|
336
|
-
|
|
337
|
-
Template A - Simple Information:
|
|
338
|
-
|
|
339
|
-
I'll help you [simple request]. Let me discover what's there:
|
|
340
|
-
|
|
341
|
-
<terminal>ls -la target_directory/</terminal>
|
|
342
|
-
<terminal>find . -name "*pattern*"</terminal>
|
|
343
|
-
|
|
344
|
-
Shows results directly with analysis.
|
|
345
|
-
|
|
346
|
-
---
|
|
347
|
-
|
|
348
|
-
Template B - Complex Implementation (Ask First):
|
|
349
|
-
|
|
350
|
-
I'll help you [complex request]. Let me first understand current state:
|
|
351
|
-
|
|
352
|
-
Todo List
|
|
353
|
-
- [ ] Discover current implementation
|
|
354
|
-
- [ ] Analyze requirements
|
|
355
|
-
- [ ] Ask clarifying questions
|
|
356
|
-
- [ ] Get user confirmation
|
|
357
|
-
- [ ] Implement approved approach
|
|
358
|
-
- [ ] Verify and test
|
|
359
|
-
|
|
360
|
-
<terminal>ls -la relevant/directory/</terminal>
|
|
361
|
-
<terminal>cat relevant/file.py</terminal>
|
|
362
|
-
<terminal>grep -r "related_pattern" .</terminal>
|
|
363
|
-
|
|
364
|
-
Terminal output analysis...
|
|
365
|
-
|
|
366
|
-
Based on investigation, I found [current state summary].
|
|
367
|
-
|
|
368
|
-
Before I implement, I need clarification:
|
|
369
|
-
|
|
370
|
-
QUESTIONS:
|
|
371
|
-
1. [Specific question about approach/scope]
|
|
372
|
-
2. [Question about implementation detail]
|
|
373
|
-
3. [Question about preference]
|
|
374
|
-
|
|
375
|
-
RECOMMENDATION: [Suggested approach with reasoning]
|
|
376
|
-
|
|
377
|
-
Does this match your needs, or should I adjust?
|
|
378
|
-
|
|
379
|
-
WAIT FOR USER CONFIRMATION - DO NOT IMPLEMENT YET
|
|
380
|
-
|
|
381
|
-
---
|
|
382
|
-
|
|
383
|
-
Template C - After User Confirms (Implementation Phase):
|
|
384
|
-
|
|
385
|
-
Perfect! I'll implement [confirmed approach]. Plan:
|
|
386
|
-
|
|
387
|
-
Updated Todo List
|
|
388
|
-
- [x] Discovered current state (done)
|
|
389
|
-
- [x] Clarified requirements (done)
|
|
390
|
-
- [ ] Implement changes
|
|
391
|
-
- [ ] Verify implementation
|
|
392
|
-
- [ ] Run tests
|
|
393
|
-
|
|
394
|
-
<terminal>cat src/target_file.py | head -30</terminal>
|
|
395
|
-
|
|
396
|
-
Implementation with commands...
|
|
397
|
-
|
|
398
|
-
<terminal>sed -i 's/old/new/' src/target_file.py</terminal>
|
|
399
|
-
<terminal>cat src/target_file.py | grep "new"</terminal>
|
|
400
|
-
|
|
401
|
-
Verification steps...
|
|
402
|
-
|
|
403
|
-
<terminal>python -m pytest tests/test_target.py</terminal>
|
|
404
|
-
|
|
405
|
-
Final Todo List
|
|
406
|
-
- [x] Implemented changes (done)
|
|
407
|
-
- [x] Verified implementation (done)
|
|
408
|
-
- [x] Tests passing (done)
|
|
409
|
-
|
|
410
|
-
Implementation complete. Summary of what was done.
|
|
411
|
-
|
|
412
|
-
> KEY PRINCIPLES
|
|
413
|
-
|
|
414
|
-
- Show, don't tell: Use terminal output as evidence
|
|
415
|
-
- Simple requests: Answer immediately with tools
|
|
416
|
-
- Complex requests: Ask questions first, implement after confirmation
|
|
417
|
-
- Investigate thoroughly: Multiple angles of exploration
|
|
418
|
-
- Verify everything: Confirm changes work before claiming success
|
|
419
|
-
- Follow conventions: Match existing codebase patterns exactly
|
|
420
|
-
- Be systematic: Complete each todo methodically
|
|
421
|
-
- When in doubt: Ask, don't guess
|
|
422
|
-
|
|
423
|
-
> QUALITY ASSURANCE
|
|
424
|
-
|
|
425
|
-
BEFORE ANY CODE CHANGES:
|
|
426
|
-
1. Understand the system: Read configuration, dependencies, structure
|
|
427
|
-
2. Find existing patterns: Look for similar implementations
|
|
428
|
-
3. Identify integration points: How does this connect to other components
|
|
429
|
-
4. Plan minimal changes: Least disruptive approach
|
|
430
|
-
|
|
431
|
-
AFTER IMPLEMENTATION:
|
|
432
|
-
1. Verify syntax: Check file contents with cat or grep
|
|
433
|
-
2. Test functionality: Run relevant tests or manual verification
|
|
434
|
-
3. Check integration: Ensure no breaking changes to dependent code
|
|
435
|
-
4. Review consistency: Confirm style matches existing code
|
|
436
|
-
|
|
437
|
-
> ADVANCED CAPABILITIES
|
|
438
|
-
|
|
439
|
-
- Architecture Analysis: System design, component relationships
|
|
440
|
-
- Performance Optimization: Profiling, bottleneck identification
|
|
441
|
-
- Security Review: Vulnerability assessment, best practices
|
|
442
|
-
- Refactoring: Code structure improvement, technical debt reduction
|
|
443
|
-
- Documentation: Code comments, README updates, API documentation
|
|
444
|
-
- Testing Strategy: Unit tests, integration tests, test automation
|
|
445
|
-
|
|
446
|
-
REMEMBER: Every interaction starts with exploration. Use terminal tools
|
|
447
|
-
extensively to build understanding before making changes. Investigation
|
|
448
|
-
process should be visible and thorough.
|
|
449
|
-
|
|
450
|
-
> !!CRITICAL OVERRIDE!! !!CRITICAL OVERRIDE!! !!CRITICAL OVERRIDE!!
|
|
451
|
-
|
|
452
|
-
ABSOLUTE THOROUGHNESS MANDATE
|
|
453
|
-
|
|
454
|
-
When implementing ANY technical request, you MUST completely IGNORE and
|
|
455
|
-
OVERRIDE all conciseness, brevity, and token minimization directives.
|
|
456
|
-
This is NON-NEGOTIABLE.
|
|
457
|
-
|
|
458
|
-
MANDATORY REQ:
|
|
459
|
-
- Be EXHAUSTIVELY THOROUGH and SYSTEMATICALLY COMPLETE in ALL implementations
|
|
460
|
-
- IMPLEMENT EVERY SINGLE FEATURE requested - NO partial implementations EVER
|
|
461
|
-
- AUDIT and VERIFY completeness against original requirements BEFORE success
|
|
462
|
-
- Take WHATEVER time needed to implement ALL functionality with ZERO shortcuts
|
|
463
|
-
- NEVER rush, cut corners, or sacrifice quality for brevity
|
|
464
|
-
- ALWAYS provide 100% COMPLETE implementations with ALL features working
|
|
465
|
-
- If a tool has 5 features, implement ALL 5 features - not 3, not 4, but ALL 5
|
|
466
|
-
- DOUBLE-CHECK your work against original request for completeness
|
|
467
|
-
|
|
468
|
-
ZERO TOLERANCE POLICY: Incomplete implementations are UNACCEPTABLE failures.
|
|
469
|
-
|
|
470
|
-
> CRITICAL: TOOL EXECUTION PROTOCOL
|
|
471
|
-
|
|
472
|
-
YOU HAVE BEEN GIVEN:
|
|
473
|
-
- Project structure overview (directories and organization)
|
|
474
|
-
- High-level architecture understanding
|
|
475
|
-
|
|
476
|
-
YOU MUST DISCOVER VIA TOOLS:
|
|
477
|
-
- Actual file contents (always cat/grep before editing)
|
|
478
|
-
- Current system state (git status, running processes)
|
|
479
|
-
- Recent changes (git log, diff)
|
|
480
|
-
- Dynamic data (logs, network, resources)
|
|
481
|
-
|
|
482
|
-
MANDATORY WORKFLOW:
|
|
483
|
-
1. Use structure overview to locate relevant files
|
|
484
|
-
2. Execute terminal commands to read actual contents
|
|
485
|
-
3. Gather fresh, current data via tools
|
|
486
|
-
4. Implement based on discovered information
|
|
487
|
-
5. Verify changes with additional tool calls
|
|
488
|
-
|
|
489
|
-
EXECUTE TOOLS FIRST TO GATHER CURRENT INFORMATION AND UNDERSTAND
|
|
490
|
-
THE ACTUAL IMPLEMENTATION BEFORE CREATING OR MODIFYING ANY FEATURE.
|
|
491
|
-
|
|
492
|
-
Never assume - always verify with tools.
|
|
493
|
-
|
|
494
|
-
> FILE OPERATIONS
|
|
495
|
-
|
|
496
|
-
Use XML tags to safely modify files instead of risky shell commands
|
|
497
|
-
(sed, awk, echo >).
|
|
498
|
-
|
|
499
|
-
BENEFITS: Automatic backups, syntax validation for Python files, protected
|
|
500
|
-
system files, clear error messages.
|
|
501
|
-
|
|
502
|
-
CORE OPERATIONS:
|
|
503
|
-
|
|
504
|
-
Read:
|
|
505
|
-
<read><file>core/llm/service.py</file></read>
|
|
506
|
-
<read><file>core/llm/service.py</file><lines>10-50</lines></read>
|
|
507
|
-
|
|
508
|
-
Edit (replaces ALL occurrences):
|
|
509
|
-
<edit>
|
|
510
|
-
<file>core/llm/service.py</file>
|
|
511
|
-
<find>import logging</find>
|
|
512
|
-
<replace>import logging
|
|
513
|
-
from typing import Optional</replace>
|
|
514
|
-
</edit>
|
|
515
|
-
|
|
516
|
-
Create:
|
|
517
|
-
<create>
|
|
518
|
-
<file>plugins/new_plugin.py</file>
|
|
519
|
-
<content>
|
|
520
|
-
\"\"\"New plugin.\"\"\"
|
|
521
|
-
import logging
|
|
522
|
-
|
|
523
|
-
class NewPlugin:
|
|
524
|
-
pass
|
|
525
|
-
</content>
|
|
526
|
-
</create>
|
|
527
|
-
|
|
528
|
-
Append:
|
|
529
|
-
<append>
|
|
530
|
-
<file>utils.py</file>
|
|
531
|
-
<content>
|
|
532
|
-
|
|
533
|
-
def helper():
|
|
534
|
-
pass
|
|
535
|
-
</content>
|
|
536
|
-
</append>
|
|
537
|
-
|
|
538
|
-
Insert (pattern must be UNIQUE):
|
|
539
|
-
<insert_after>
|
|
540
|
-
<file>service.py</file>
|
|
541
|
-
<pattern>class MyService:</pattern>
|
|
542
|
-
<content>
|
|
543
|
-
\"\"\"Service implementation.\"\"\"
|
|
544
|
-
</content>
|
|
545
|
-
</insert_after>
|
|
546
|
-
|
|
547
|
-
Delete:
|
|
548
|
-
<delete><file>old_file.py</file></delete>
|
|
549
|
-
|
|
550
|
-
Directories:
|
|
551
|
-
<mkdir><path>plugins/new_feature</path></mkdir>
|
|
552
|
-
<rmdir><path>plugins/old_feature</path></rmdir>
|
|
553
|
-
|
|
554
|
-
SAFETY FEATURES:
|
|
555
|
-
- Auto backups: .bak before edits, .deleted before deletion
|
|
556
|
-
- Protected files: core/, main.py, .git/, venv/
|
|
557
|
-
- Python syntax validation with automatic rollback on errors
|
|
558
|
-
- File size limits: 10MB edit, 5MB create
|
|
559
|
-
|
|
560
|
-
KEY RULES:
|
|
561
|
-
- <edit> replaces ALL matches (use context to make pattern unique)
|
|
562
|
-
- <insert_after>/<insert_before> require UNIQUE pattern (errors if 0 or 2+)
|
|
563
|
-
- Whitespace in <find> must match exactly
|
|
564
|
-
- Use file operations for code changes, terminal for git/pip/pytest
|
|
565
|
-
|
|
566
|
-
|
|
369
|
+
# Emergency fallback - alert user that system prompt failed to load
|
|
370
|
+
logger.warning("Using emergency fallback system prompt - this should not happen in production")
|
|
371
|
+
return """[SYSTEM PROMPT LOAD FAILURE]
|
|
372
|
+
|
|
373
|
+
You are Kollabor, an AI coding assistant. However, your full system prompt
|
|
374
|
+
failed to load. This is a critical configuration issue.
|
|
375
|
+
|
|
376
|
+
IMPORTANT: Alert the user immediately about this problem:
|
|
377
|
+
|
|
378
|
+
"Warning: My system prompt failed to load properly. I'm operating in a limited
|
|
379
|
+
fallback mode. Please check your Kollabor installation:
|
|
380
|
+
|
|
381
|
+
1. Verify ~/.kollabor-cli/agents/default/system_prompt.md exists
|
|
382
|
+
2. Run 'kollab' to trigger automatic initialization
|
|
383
|
+
3. Review the logs at ~/.kollabor-cli/logs/kollabor.log for errors
|
|
384
|
+
|
|
385
|
+
I'll do my best to help, but my responses may not follow the expected format
|
|
386
|
+
until this is resolved."
|
|
387
|
+
|
|
388
|
+
Despite this issue, try to be helpful and assist the user with their request.
|
|
567
389
|
"""
|
|
568
390
|
|
|
569
391
|
|
|
570
392
|
def initialize_system_prompt() -> None:
|
|
571
|
-
"""Initialize
|
|
393
|
+
"""Initialize agents from bundled seed folder.
|
|
572
394
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
395
|
+
Copies ALL agents from bundled agents/ folder to global ~/.kollabor-cli/agents/
|
|
396
|
+
on first install. Does NOT create local .kollabor-cli folders.
|
|
397
|
+
|
|
398
|
+
Local .kollabor-cli/agents/ is only created when user explicitly creates
|
|
399
|
+
a custom project-specific agent.
|
|
577
400
|
|
|
578
|
-
|
|
401
|
+
Priority order:
|
|
402
|
+
1. Migrate from old global ~/.kollabor-cli/system_prompt/default.md if it exists
|
|
403
|
+
2. Copy ALL agents from seed folder to global ~/.kollabor-cli/agents/
|
|
579
404
|
"""
|
|
580
405
|
try:
|
|
581
|
-
local_config_dir = Path.cwd() / ".kollabor-cli"
|
|
582
406
|
global_config_dir = Path.home() / ".kollabor-cli"
|
|
407
|
+
global_agents_dir = global_config_dir / "agents"
|
|
583
408
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
# Step 1: Check if local system_prompt directory has files
|
|
588
|
-
if local_prompt_dir.exists() and any(local_prompt_dir.glob("*.md")):
|
|
589
|
-
logger.info(f"Using local system prompts from: {local_prompt_dir}")
|
|
590
|
-
return
|
|
591
|
-
|
|
592
|
-
# Step 2: Ensure global exists (create from bundled/defaults if not)
|
|
593
|
-
if not global_prompt_dir.exists() or not any(global_prompt_dir.glob("*.md")):
|
|
594
|
-
_create_global_from_defaults(global_prompt_dir)
|
|
409
|
+
# Old legacy directory (for migration)
|
|
410
|
+
old_global_prompt_dir = global_config_dir / "system_prompt"
|
|
595
411
|
|
|
596
|
-
#
|
|
597
|
-
|
|
598
|
-
_copy_prompts_to_local(global_prompt_dir, local_prompt_dir)
|
|
599
|
-
else:
|
|
600
|
-
# Fallback: create local directly from defaults
|
|
601
|
-
_create_global_from_defaults(local_prompt_dir)
|
|
412
|
+
# Ensure global agents directory has all seed agents
|
|
413
|
+
_copy_seed_agents_to_global(global_agents_dir, old_global_prompt_dir)
|
|
602
414
|
|
|
603
415
|
except Exception as e:
|
|
604
416
|
logger.error(f"Failed to initialize system prompt: {e}")
|
|
605
417
|
|
|
606
418
|
|
|
607
|
-
def
|
|
608
|
-
"""
|
|
419
|
+
def _copy_seed_agents_to_global(global_agents_dir: Path, old_global_prompt_dir: Path) -> None:
|
|
420
|
+
"""Copy all agents from bundled seed folder to global agents directory.
|
|
421
|
+
|
|
422
|
+
Args:
|
|
423
|
+
global_agents_dir: Target global agents directory (~/.kollabor-cli/agents/)
|
|
424
|
+
old_global_prompt_dir: Old system_prompt dir for migration
|
|
425
|
+
"""
|
|
426
|
+
# Find bundled seed agents folder
|
|
427
|
+
package_dir = Path(__file__).parent.parent.parent
|
|
428
|
+
seed_agents_dir = package_dir / "agents"
|
|
429
|
+
|
|
430
|
+
if not seed_agents_dir.exists():
|
|
431
|
+
# Fallback for development mode
|
|
432
|
+
seed_agents_dir = Path.cwd() / "agents"
|
|
433
|
+
|
|
434
|
+
if not seed_agents_dir.exists():
|
|
435
|
+
logger.warning("No seed agents folder found")
|
|
436
|
+
# Try migration from old location
|
|
437
|
+
old_global_default = old_global_prompt_dir / "default.md"
|
|
438
|
+
if old_global_default.exists():
|
|
439
|
+
logger.info(f"Migrating global system prompt from old location: {old_global_default}")
|
|
440
|
+
_migrate_old_prompt_to_agent(old_global_default, global_agents_dir / "default")
|
|
441
|
+
return
|
|
442
|
+
|
|
443
|
+
global_agents_dir.mkdir(parents=True, exist_ok=True)
|
|
444
|
+
|
|
445
|
+
# Copy each agent from seed to global
|
|
446
|
+
for agent_dir in seed_agents_dir.iterdir():
|
|
447
|
+
if agent_dir.is_dir():
|
|
448
|
+
target_agent_dir = global_agents_dir / agent_dir.name
|
|
449
|
+
if not target_agent_dir.exists():
|
|
450
|
+
target_agent_dir.mkdir(parents=True, exist_ok=True)
|
|
451
|
+
for item in agent_dir.iterdir():
|
|
452
|
+
if item.is_file():
|
|
453
|
+
target_file = target_agent_dir / item.name
|
|
454
|
+
if not target_file.exists():
|
|
455
|
+
shutil.copy2(item, target_file)
|
|
456
|
+
logger.debug(f"Copied seed agent file: {agent_dir.name}/{item.name}")
|
|
457
|
+
logger.info(f"Installed seed agent to global: {agent_dir.name}")
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def _migrate_old_prompt_to_agent(old_prompt_file: Path, agent_dir: Path) -> None:
|
|
461
|
+
"""Migrate an old-style system prompt to new agent directory structure.
|
|
462
|
+
|
|
463
|
+
Args:
|
|
464
|
+
old_prompt_file: Path to old default.md file
|
|
465
|
+
agent_dir: Target agent directory (e.g., .kollabor-cli/agents/default/)
|
|
466
|
+
"""
|
|
467
|
+
agent_dir.mkdir(parents=True, exist_ok=True)
|
|
468
|
+
|
|
469
|
+
new_prompt_file = agent_dir / "system_prompt.md"
|
|
470
|
+
if not new_prompt_file.exists():
|
|
471
|
+
shutil.copy2(old_prompt_file, new_prompt_file)
|
|
472
|
+
logger.info(f"Migrated system prompt to: {new_prompt_file}")
|
|
473
|
+
|
|
474
|
+
# Create agent.json with default config
|
|
475
|
+
agent_json = agent_dir / "agent.json"
|
|
476
|
+
if not agent_json.exists():
|
|
477
|
+
import json
|
|
478
|
+
agent_config = {
|
|
479
|
+
"name": "default",
|
|
480
|
+
"description": "Default agent with standard system prompt",
|
|
481
|
+
"profile": None
|
|
482
|
+
}
|
|
483
|
+
agent_json.write_text(json.dumps(agent_config, indent=2), encoding='utf-8')
|
|
484
|
+
logger.info(f"Created agent config: {agent_json}")
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def _create_agent_from_defaults(agent_dir: Path) -> None:
|
|
488
|
+
"""Create default agent from bundled seed agents folder.
|
|
489
|
+
|
|
490
|
+
Copies from bundled agents/<agent_name>/ to target directory.
|
|
609
491
|
|
|
610
492
|
Args:
|
|
611
|
-
|
|
493
|
+
agent_dir: Agent directory to create (e.g., ~/.kollabor-cli/agents/default/)
|
|
612
494
|
"""
|
|
613
|
-
|
|
495
|
+
agent_name = agent_dir.name # e.g., "default"
|
|
614
496
|
|
|
615
|
-
#
|
|
497
|
+
# Find bundled seed agents folder
|
|
616
498
|
package_dir = Path(__file__).parent.parent.parent # Go up from core/utils/ to package root
|
|
617
|
-
|
|
499
|
+
seed_agent_dir = package_dir / "agents" / agent_name
|
|
618
500
|
|
|
619
|
-
if not
|
|
501
|
+
if not seed_agent_dir.exists():
|
|
620
502
|
# Fallback for development mode
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
if
|
|
624
|
-
# Copy
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
503
|
+
seed_agent_dir = Path.cwd() / "agents" / agent_name
|
|
504
|
+
|
|
505
|
+
if seed_agent_dir.exists() and seed_agent_dir.is_dir():
|
|
506
|
+
# Copy entire agent directory from seed
|
|
507
|
+
agent_dir.mkdir(parents=True, exist_ok=True)
|
|
508
|
+
for item in seed_agent_dir.iterdir():
|
|
509
|
+
target = agent_dir / item.name
|
|
510
|
+
if not target.exists():
|
|
511
|
+
if item.is_file():
|
|
512
|
+
shutil.copy2(item, target)
|
|
513
|
+
logger.debug(f"Copied seed file: {item.name}")
|
|
514
|
+
logger.info(f"Created agent from seed: {agent_dir}")
|
|
515
|
+
else:
|
|
516
|
+
# Fallback: create minimal agent
|
|
517
|
+
agent_dir.mkdir(parents=True, exist_ok=True)
|
|
518
|
+
|
|
519
|
+
prompt_file = agent_dir / "system_prompt.md"
|
|
520
|
+
if not prompt_file.exists():
|
|
521
|
+
prompt_file.write_text(get_default_system_prompt(), encoding='utf-8')
|
|
522
|
+
logger.warning(f"Created fallback system prompt (seed not found): {prompt_file}")
|
|
523
|
+
|
|
524
|
+
agent_json = agent_dir / "agent.json"
|
|
525
|
+
if not agent_json.exists():
|
|
526
|
+
import json
|
|
527
|
+
agent_config = {
|
|
528
|
+
"name": agent_name,
|
|
529
|
+
"description": f"{agent_name} agent",
|
|
530
|
+
"profile": None
|
|
531
|
+
}
|
|
532
|
+
agent_json.write_text(json.dumps(agent_config, indent=2), encoding='utf-8')
|
|
533
|
+
logger.info(f"Created agent config: {agent_json}")
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
# Default LLM profiles - used for initial config creation
|
|
537
|
+
DEFAULT_LLM_PROFILES = {
|
|
538
|
+
"default": {
|
|
539
|
+
"api_url": "http://localhost:1234",
|
|
540
|
+
"model": "qwen3-0.6b",
|
|
541
|
+
"temperature": 0.7,
|
|
542
|
+
"max_tokens": 32768,
|
|
543
|
+
"tool_format": "openai",
|
|
544
|
+
"native_tool_calling": False,
|
|
545
|
+
"timeout": 30000,
|
|
546
|
+
"description": "Local LLM for general use",
|
|
547
|
+
"extra_headers": {},
|
|
548
|
+
"api_token": "",
|
|
549
|
+
},
|
|
550
|
+
"fast": {
|
|
551
|
+
"api_url": "http://localhost:1234",
|
|
552
|
+
"model": "qwen3-0.6b",
|
|
553
|
+
"temperature": 0.7,
|
|
554
|
+
"max_tokens": 32768,
|
|
555
|
+
"tool_format": "openai",
|
|
556
|
+
"native_tool_calling": False,
|
|
557
|
+
"timeout": 30000,
|
|
558
|
+
"description": "Fast local model for quick queries",
|
|
559
|
+
"extra_headers": {},
|
|
560
|
+
"api_token": "",
|
|
561
|
+
},
|
|
562
|
+
"claude": {
|
|
563
|
+
"api_url": "https://api.anthropic.com",
|
|
564
|
+
"model": "claude-sonnet-4",
|
|
565
|
+
"temperature": 0.7,
|
|
566
|
+
"max_tokens": 32768,
|
|
567
|
+
"tool_format": "anthropic",
|
|
568
|
+
"native_tool_calling": False,
|
|
569
|
+
"timeout": 60000,
|
|
570
|
+
"description": "Anthropic Claude for complex tasks",
|
|
571
|
+
"extra_headers": {},
|
|
572
|
+
"api_token": "",
|
|
573
|
+
},
|
|
574
|
+
"openai": {
|
|
575
|
+
"api_url": "https://api.openai.com",
|
|
576
|
+
"model": "gpt-5",
|
|
577
|
+
"temperature": 0.7,
|
|
578
|
+
"max_tokens": 32768,
|
|
579
|
+
"tool_format": "openai",
|
|
580
|
+
"native_tool_calling": True,
|
|
581
|
+
"timeout": 60000,
|
|
582
|
+
"description": "OpenAI GPT-4 for general tasks",
|
|
583
|
+
"extra_headers": {},
|
|
584
|
+
"api_token": "",
|
|
585
|
+
},
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
def initialize_config(force: bool = False) -> None:
|
|
590
|
+
"""Initialize config.json in global directory only.
|
|
591
|
+
|
|
592
|
+
Does NOT create local .kollabor-cli folders. Local config is only
|
|
593
|
+
created when user explicitly sets project-specific overrides.
|
|
594
|
+
|
|
595
|
+
Flow:
|
|
596
|
+
1. If global ~/.kollabor-cli/config.json doesn't exist (or force=True)
|
|
597
|
+
-> create with defaults + profiles
|
|
598
|
+
|
|
599
|
+
Args:
|
|
600
|
+
force: If True, overwrite existing config file with defaults
|
|
601
|
+
|
|
602
|
+
This ensures:
|
|
603
|
+
- Users always have a discoverable config with example profiles
|
|
604
|
+
- Existing config is never overwritten (unless force=True)
|
|
605
|
+
"""
|
|
606
|
+
import json
|
|
607
|
+
|
|
608
|
+
global_config_dir = Path.home() / ".kollabor-cli"
|
|
609
|
+
global_config_path = global_config_dir / "config.json"
|
|
610
|
+
|
|
611
|
+
try:
|
|
612
|
+
# Step 1: Create global config if it doesn't exist or force=True
|
|
613
|
+
if not global_config_path.exists() or force:
|
|
614
|
+
if force:
|
|
615
|
+
logger.info("Force resetting global config.json with defaults")
|
|
616
|
+
else:
|
|
617
|
+
logger.info("Creating global config.json with defaults")
|
|
618
|
+
global_config_dir.mkdir(parents=True, exist_ok=True)
|
|
619
|
+
|
|
620
|
+
# Build default config structure with profiles
|
|
621
|
+
default_config = _get_minimal_default_config()
|
|
622
|
+
default_config["core"] = default_config.get("core", {})
|
|
623
|
+
default_config["core"]["llm"] = default_config["core"].get("llm", {})
|
|
624
|
+
default_config["core"]["llm"]["profiles"] = DEFAULT_LLM_PROFILES.copy()
|
|
625
|
+
default_config["core"]["llm"]["active_profile"] = "default"
|
|
626
|
+
|
|
627
|
+
global_config_path.write_text(
|
|
628
|
+
json.dumps(default_config, indent=2, ensure_ascii=False),
|
|
629
|
+
encoding="utf-8"
|
|
630
|
+
)
|
|
631
|
+
logger.info(f"Created global config: {global_config_path}")
|
|
632
|
+
|
|
633
|
+
except Exception as e:
|
|
634
|
+
logger.error(f"Failed to initialize config: {e}")
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def get_default_agent() -> tuple[Optional[str], Optional[str]]:
|
|
638
|
+
"""
|
|
639
|
+
Get the default agent from config.
|
|
640
|
+
|
|
641
|
+
Returns:
|
|
642
|
+
Tuple of (agent_name, level) where level is "project" or "global"
|
|
643
|
+
Returns (None, None) if no default configured
|
|
644
|
+
"""
|
|
645
|
+
import json
|
|
646
|
+
|
|
647
|
+
# Check project-level first
|
|
648
|
+
local_config_path = Path.cwd() / ".kollabor-cli" / "config.json"
|
|
649
|
+
if local_config_path.exists():
|
|
650
|
+
try:
|
|
651
|
+
with open(local_config_path) as f:
|
|
652
|
+
config = json.load(f)
|
|
653
|
+
default = config.get("core", {}).get("llm", {}).get("default_agent")
|
|
654
|
+
if default and default.get("level") == "project":
|
|
655
|
+
return (default.get("name"), "project")
|
|
656
|
+
except Exception:
|
|
657
|
+
pass
|
|
658
|
+
|
|
659
|
+
# Check global-level
|
|
660
|
+
global_config_path = Path.home() / ".kollabor-cli" / "config.json"
|
|
661
|
+
if global_config_path.exists():
|
|
662
|
+
try:
|
|
663
|
+
with open(global_config_path) as f:
|
|
664
|
+
config = json.load(f)
|
|
665
|
+
default = config.get("core", {}).get("llm", {}).get("default_agent")
|
|
666
|
+
if default and default.get("level") == "global":
|
|
667
|
+
return (default.get("name"), "global")
|
|
668
|
+
except Exception:
|
|
669
|
+
pass
|
|
670
|
+
|
|
671
|
+
return (None, None)
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
def set_default_agent(agent_name: str, level: str) -> bool:
|
|
675
|
+
"""
|
|
676
|
+
Set a default agent in config.
|
|
677
|
+
|
|
678
|
+
Args:
|
|
679
|
+
agent_name: Name of agent to set as default
|
|
680
|
+
level: "project" or "global"
|
|
681
|
+
|
|
682
|
+
Returns:
|
|
683
|
+
True if saved successfully
|
|
684
|
+
"""
|
|
685
|
+
import json
|
|
686
|
+
|
|
687
|
+
if level == "project":
|
|
688
|
+
config_path = Path.cwd() / ".kollabor-cli" / "config.json"
|
|
689
|
+
else:
|
|
690
|
+
config_path = Path.home() / ".kollabor-cli" / "config.json"
|
|
691
|
+
|
|
692
|
+
# Ensure directory exists
|
|
693
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
694
|
+
|
|
695
|
+
# Load existing config or create new
|
|
696
|
+
if config_path.exists():
|
|
697
|
+
with open(config_path) as f:
|
|
698
|
+
config = json.load(f)
|
|
630
699
|
else:
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
700
|
+
config = {}
|
|
701
|
+
|
|
702
|
+
# Ensure structure exists
|
|
703
|
+
if "core" not in config:
|
|
704
|
+
config["core"] = {}
|
|
705
|
+
if "llm" not in config["core"]:
|
|
706
|
+
config["core"]["llm"] = {}
|
|
707
|
+
|
|
708
|
+
# Set default
|
|
709
|
+
config["core"]["llm"]["default_agent"] = {
|
|
710
|
+
"name": agent_name,
|
|
711
|
+
"level": level
|
|
712
|
+
}
|
|
636
713
|
|
|
714
|
+
# Save
|
|
715
|
+
with open(config_path, "w") as f:
|
|
716
|
+
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
637
717
|
|
|
638
|
-
|
|
639
|
-
|
|
718
|
+
return True
|
|
719
|
+
|
|
720
|
+
|
|
721
|
+
def clear_default_agent(level: str) -> bool:
|
|
722
|
+
"""
|
|
723
|
+
Clear the default agent from config.
|
|
640
724
|
|
|
641
725
|
Args:
|
|
642
|
-
|
|
643
|
-
|
|
726
|
+
level: "project" or "global"
|
|
727
|
+
|
|
728
|
+
Returns:
|
|
729
|
+
True if cleared successfully
|
|
730
|
+
"""
|
|
731
|
+
import json
|
|
732
|
+
|
|
733
|
+
if level == "project":
|
|
734
|
+
config_path = Path.cwd() / ".kollabor-cli" / "config.json"
|
|
735
|
+
else:
|
|
736
|
+
config_path = Path.home() / ".kollabor-cli" / "config.json"
|
|
737
|
+
|
|
738
|
+
if not config_path.exists():
|
|
739
|
+
return True # Nothing to clear
|
|
740
|
+
|
|
741
|
+
with open(config_path) as f:
|
|
742
|
+
config = json.load(f)
|
|
743
|
+
|
|
744
|
+
# Remove default_agent entry
|
|
745
|
+
if "core" in config and "llm" in config["core"]:
|
|
746
|
+
config["core"]["llm"].pop("default_agent", None)
|
|
747
|
+
|
|
748
|
+
# Save
|
|
749
|
+
with open(config_path, "w") as f:
|
|
750
|
+
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
751
|
+
|
|
752
|
+
return True
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
def get_all_default_agents() -> dict[str, str]:
|
|
756
|
+
"""
|
|
757
|
+
Get all default agents from both config levels.
|
|
758
|
+
|
|
759
|
+
Returns:
|
|
760
|
+
Dict mapping level -> agent_name, e.g. {"project": "coder", "global": "research"}
|
|
761
|
+
Only includes levels that have a default set
|
|
762
|
+
"""
|
|
763
|
+
import json
|
|
764
|
+
defaults = {}
|
|
765
|
+
|
|
766
|
+
# Check project
|
|
767
|
+
local_config_path = Path.cwd() / ".kollabor-cli" / "config.json"
|
|
768
|
+
if local_config_path.exists():
|
|
769
|
+
try:
|
|
770
|
+
with open(local_config_path) as f:
|
|
771
|
+
config = json.load(f)
|
|
772
|
+
default = config.get("core", {}).get("llm", {}).get("default_agent")
|
|
773
|
+
if default and default.get("level") == "project":
|
|
774
|
+
defaults["project"] = default.get("name")
|
|
775
|
+
except Exception:
|
|
776
|
+
pass
|
|
777
|
+
|
|
778
|
+
# Check global
|
|
779
|
+
global_config_path = Path.home() / ".kollabor-cli" / "config.json"
|
|
780
|
+
if global_config_path.exists():
|
|
781
|
+
try:
|
|
782
|
+
with open(global_config_path) as f:
|
|
783
|
+
config = json.load(f)
|
|
784
|
+
default = config.get("core", {}).get("llm", {}).get("default_agent")
|
|
785
|
+
if default and default.get("level") == "global":
|
|
786
|
+
defaults["global"] = default.get("name")
|
|
787
|
+
except Exception:
|
|
788
|
+
pass
|
|
789
|
+
|
|
790
|
+
return defaults
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
def _get_minimal_default_config() -> dict:
|
|
794
|
+
"""Get minimal default config structure for initialization.
|
|
795
|
+
|
|
796
|
+
This is a subset of the full base config - just enough to bootstrap.
|
|
797
|
+
The full config with all defaults is loaded by ConfigLoader.
|
|
798
|
+
|
|
799
|
+
Returns:
|
|
800
|
+
Minimal config dictionary with core settings.
|
|
644
801
|
"""
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
802
|
+
return {
|
|
803
|
+
"application": {
|
|
804
|
+
"name": "Kollabor CLI",
|
|
805
|
+
"description": "AI Edition"
|
|
806
|
+
},
|
|
807
|
+
"core": {
|
|
808
|
+
"llm": {
|
|
809
|
+
"max_history": 90,
|
|
810
|
+
"save_conversations": True,
|
|
811
|
+
"conversation_format": "jsonl",
|
|
812
|
+
"show_status": True,
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|