zjcode 0.0.1__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.
- deepagents_code/__init__.py +42 -0
- deepagents_code/__main__.py +6 -0
- deepagents_code/_ask_user_types.py +90 -0
- deepagents_code/_cli_context.py +96 -0
- deepagents_code/_constants.py +41 -0
- deepagents_code/_debug.py +148 -0
- deepagents_code/_debug_buffer.py +204 -0
- deepagents_code/_env_vars.py +411 -0
- deepagents_code/_fake_models.py +66 -0
- deepagents_code/_git.py +521 -0
- deepagents_code/_paths.py +69 -0
- deepagents_code/_server_config.py +576 -0
- deepagents_code/_session_stats.py +235 -0
- deepagents_code/_startup_error.py +45 -0
- deepagents_code/_testing_models.py +305 -0
- deepagents_code/_textual_patches.py +420 -0
- deepagents_code/_tool_stream.py +694 -0
- deepagents_code/_version.py +46 -0
- deepagents_code/agent.py +1976 -0
- deepagents_code/app.py +19239 -0
- deepagents_code/app.tcss +438 -0
- deepagents_code/approval_mode.py +131 -0
- deepagents_code/ask_user.py +306 -0
- deepagents_code/auth_display.py +185 -0
- deepagents_code/auth_store.py +545 -0
- deepagents_code/built_in_skills/__init__.py +5 -0
- deepagents_code/built_in_skills/remember/SKILL.md +118 -0
- deepagents_code/built_in_skills/skill-creator/SKILL.md +383 -0
- deepagents_code/built_in_skills/skill-creator/scripts/init_skill.py +366 -0
- deepagents_code/built_in_skills/skill-creator/scripts/quick_validate.py +158 -0
- deepagents_code/client/__init__.py +1 -0
- deepagents_code/client/commands/__init__.py +1 -0
- deepagents_code/client/commands/auth.py +541 -0
- deepagents_code/client/commands/config.py +714 -0
- deepagents_code/client/commands/mcp.py +250 -0
- deepagents_code/client/commands/tools.py +416 -0
- deepagents_code/client/launch/__init__.py +1 -0
- deepagents_code/client/launch/server.py +978 -0
- deepagents_code/client/launch/server_manager.py +540 -0
- deepagents_code/client/non_interactive.py +1758 -0
- deepagents_code/client/remote_client.py +794 -0
- deepagents_code/clipboard.py +217 -0
- deepagents_code/command_registry.py +450 -0
- deepagents_code/config.py +4829 -0
- deepagents_code/config_manifest.py +1451 -0
- deepagents_code/configurable_model.py +577 -0
- deepagents_code/default_agent_prompt.md +12 -0
- deepagents_code/doctor.py +559 -0
- deepagents_code/editor.py +142 -0
- deepagents_code/event_bus.py +411 -0
- deepagents_code/extras_info.py +661 -0
- deepagents_code/file_ops.py +576 -0
- deepagents_code/formatting.py +135 -0
- deepagents_code/goal_rubric.py +497 -0
- deepagents_code/goal_tools.py +459 -0
- deepagents_code/hooks.py +348 -0
- deepagents_code/input.py +1041 -0
- deepagents_code/integrations/__init__.py +1 -0
- deepagents_code/integrations/openai_codex.py +551 -0
- deepagents_code/integrations/sandbox_config.py +198 -0
- deepagents_code/integrations/sandbox_factory.py +1124 -0
- deepagents_code/integrations/sandbox_provider.py +137 -0
- deepagents_code/integrations/sandbox_registry.py +350 -0
- deepagents_code/iterm_cursor_guide.py +176 -0
- deepagents_code/local_context.py +926 -0
- deepagents_code/main.py +3985 -0
- deepagents_code/managed_tools.py +642 -0
- deepagents_code/mcp_auth.py +1950 -0
- deepagents_code/mcp_config.py +176 -0
- deepagents_code/mcp_disabled.py +212 -0
- deepagents_code/mcp_login_service.py +281 -0
- deepagents_code/mcp_oauth_ui.py +199 -0
- deepagents_code/mcp_providers/__init__.py +23 -0
- deepagents_code/mcp_providers/_registry.py +39 -0
- deepagents_code/mcp_providers/base.py +133 -0
- deepagents_code/mcp_providers/github.py +102 -0
- deepagents_code/mcp_providers/slack.py +175 -0
- deepagents_code/mcp_tools.py +2427 -0
- deepagents_code/mcp_trust.py +207 -0
- deepagents_code/media_utils.py +826 -0
- deepagents_code/memory_guard.py +474 -0
- deepagents_code/model_config.py +4156 -0
- deepagents_code/notifications.py +247 -0
- deepagents_code/offload.py +402 -0
- deepagents_code/onboarding.py +223 -0
- deepagents_code/output.py +69 -0
- deepagents_code/paste_collapse.py +103 -0
- deepagents_code/project_utils.py +231 -0
- deepagents_code/py.typed +0 -0
- deepagents_code/reasoning_effort.py +641 -0
- deepagents_code/reliable_rubric.py +97 -0
- deepagents_code/resume_state.py +237 -0
- deepagents_code/server_graph.py +310 -0
- deepagents_code/session_end_summary.py +329 -0
- deepagents_code/sessions.py +1716 -0
- deepagents_code/skills/__init__.py +18 -0
- deepagents_code/skills/commands.py +1252 -0
- deepagents_code/skills/invocation.py +112 -0
- deepagents_code/skills/load.py +196 -0
- deepagents_code/skills/trust.py +547 -0
- deepagents_code/state_migration.py +136 -0
- deepagents_code/subagents.py +278 -0
- deepagents_code/system_prompt.md +204 -0
- deepagents_code/terminal_capabilities.py +115 -0
- deepagents_code/terminal_escape.py +287 -0
- deepagents_code/theme.py +891 -0
- deepagents_code/todo_list_prompt.md +12 -0
- deepagents_code/tool_catalog.py +509 -0
- deepagents_code/tool_display.py +367 -0
- deepagents_code/tools.py +516 -0
- deepagents_code/tui/__init__.py +1 -0
- deepagents_code/tui/textual_adapter.py +2553 -0
- deepagents_code/tui/widgets/__init__.py +9 -0
- deepagents_code/tui/widgets/_js_eval_display.py +139 -0
- deepagents_code/tui/widgets/_links.py +261 -0
- deepagents_code/tui/widgets/agent_selector.py +420 -0
- deepagents_code/tui/widgets/approval.py +602 -0
- deepagents_code/tui/widgets/ask_user.py +515 -0
- deepagents_code/tui/widgets/auth.py +1997 -0
- deepagents_code/tui/widgets/autocomplete.py +890 -0
- deepagents_code/tui/widgets/chat_input.py +3181 -0
- deepagents_code/tui/widgets/codex_auth.py +452 -0
- deepagents_code/tui/widgets/cwd_switch.py +242 -0
- deepagents_code/tui/widgets/debug_console.py +868 -0
- deepagents_code/tui/widgets/diff.py +252 -0
- deepagents_code/tui/widgets/effort_selector.py +189 -0
- deepagents_code/tui/widgets/goal_review.py +390 -0
- deepagents_code/tui/widgets/goal_status.py +50 -0
- deepagents_code/tui/widgets/history.py +194 -0
- deepagents_code/tui/widgets/install_confirm.py +248 -0
- deepagents_code/tui/widgets/launch_init.py +482 -0
- deepagents_code/tui/widgets/loading.py +227 -0
- deepagents_code/tui/widgets/mcp_login.py +539 -0
- deepagents_code/tui/widgets/mcp_reconnect.py +212 -0
- deepagents_code/tui/widgets/mcp_viewer.py +1634 -0
- deepagents_code/tui/widgets/message_store.py +999 -0
- deepagents_code/tui/widgets/messages.py +3846 -0
- deepagents_code/tui/widgets/model_selector.py +2343 -0
- deepagents_code/tui/widgets/notification_center.py +456 -0
- deepagents_code/tui/widgets/notification_detail.py +257 -0
- deepagents_code/tui/widgets/notification_settings.py +170 -0
- deepagents_code/tui/widgets/restart_prompt.py +158 -0
- deepagents_code/tui/widgets/skill_trust.py +131 -0
- deepagents_code/tui/widgets/startup_tip.py +91 -0
- deepagents_code/tui/widgets/status.py +781 -0
- deepagents_code/tui/widgets/subagent_panel.py +969 -0
- deepagents_code/tui/widgets/theme_selector.py +401 -0
- deepagents_code/tui/widgets/thread_selector.py +2564 -0
- deepagents_code/tui/widgets/tool_renderers.py +190 -0
- deepagents_code/tui/widgets/tool_widgets.py +304 -0
- deepagents_code/tui/widgets/update_available.py +311 -0
- deepagents_code/tui/widgets/update_confirm.py +184 -0
- deepagents_code/tui/widgets/update_progress.py +327 -0
- deepagents_code/tui/widgets/welcome.py +508 -0
- deepagents_code/turn_end_summary.py +522 -0
- deepagents_code/ui.py +858 -0
- deepagents_code/unicode_security.py +563 -0
- deepagents_code/update_check.py +3124 -0
- zjcode-0.0.1.data/data/deepagents_code/default_agent_prompt.md +12 -0
- zjcode-0.0.1.dist-info/METADATA +220 -0
- zjcode-0.0.1.dist-info/RECORD +163 -0
- zjcode-0.0.1.dist-info/WHEEL +4 -0
- zjcode-0.0.1.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"""Subagent loader for app.
|
|
2
|
+
|
|
3
|
+
Loads custom subagent definitions from the filesystem. Subagents are defined
|
|
4
|
+
as markdown files with YAML frontmatter in the agents/ directory.
|
|
5
|
+
|
|
6
|
+
Directory structure:
|
|
7
|
+
.deepagents/agents/{agent_name}/AGENTS.md
|
|
8
|
+
|
|
9
|
+
Example file (researcher/AGENTS.md):
|
|
10
|
+
---
|
|
11
|
+
name: researcher # optional; defaults to the folder name
|
|
12
|
+
description: Research topics on the web before writing content
|
|
13
|
+
model: anthropic:claude-haiku-4-5-20251001
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
You are a research assistant with access to web search.
|
|
17
|
+
|
|
18
|
+
## Your Process
|
|
19
|
+
1. Search for relevant information
|
|
20
|
+
2. Summarize findings clearly
|
|
21
|
+
|
|
22
|
+
The `name` field is optional; when omitted it defaults to the folder name
|
|
23
|
+
(e.g. `researcher`). This diverges from the Agent Skills specification
|
|
24
|
+
(`deepagents.middleware.skills`), which requires `name` in frontmatter and
|
|
25
|
+
warns when it does not match the parent directory name. Subagents use the
|
|
26
|
+
folder name as an implicit fallback instead because subagent definitions are
|
|
27
|
+
already uniquely identified by their folder — requiring a redundant `name`
|
|
28
|
+
field adds friction without adding information.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import logging
|
|
34
|
+
import re
|
|
35
|
+
from typing import TYPE_CHECKING, TypedDict
|
|
36
|
+
|
|
37
|
+
import yaml
|
|
38
|
+
|
|
39
|
+
if TYPE_CHECKING:
|
|
40
|
+
from pathlib import Path
|
|
41
|
+
|
|
42
|
+
logger = logging.getLogger(__name__)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class SubagentMetadata(TypedDict):
|
|
46
|
+
"""Metadata for a custom subagent loaded from filesystem."""
|
|
47
|
+
|
|
48
|
+
name: str
|
|
49
|
+
"""Unique identifier for the subagent, used with the task tool."""
|
|
50
|
+
|
|
51
|
+
description: str
|
|
52
|
+
"""What this subagent does. Main agent uses this to decide when to delegate."""
|
|
53
|
+
|
|
54
|
+
system_prompt: str
|
|
55
|
+
"""Instructions for the subagent (body of the markdown file)."""
|
|
56
|
+
|
|
57
|
+
model: str | None
|
|
58
|
+
"""Optional model override in 'provider:model-name' format."""
|
|
59
|
+
|
|
60
|
+
source: str
|
|
61
|
+
"""Where this subagent was loaded from ('user' or 'project')."""
|
|
62
|
+
|
|
63
|
+
path: str
|
|
64
|
+
"""Absolute path to the subagent definition file."""
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _parse_subagent_file(
|
|
68
|
+
file_path: Path, *, fallback_name: str | None = None
|
|
69
|
+
) -> SubagentMetadata | None:
|
|
70
|
+
"""Parse a subagent markdown file with YAML frontmatter.
|
|
71
|
+
|
|
72
|
+
The file must have YAML frontmatter (delimited by ---) containing at minimum
|
|
73
|
+
a 'description' field. The body of the file becomes the system_prompt.
|
|
74
|
+
|
|
75
|
+
Unlike the Agent Skills spec, `name` is optional here — when omitted the
|
|
76
|
+
folder name passed via `fallback_name` is used instead. Skills require
|
|
77
|
+
`name` in frontmatter and warn when it doesn't match the directory name;
|
|
78
|
+
subagents relax that to a fallback so users don't repeat the folder name
|
|
79
|
+
redundantly.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
file_path: Path to the markdown file.
|
|
83
|
+
fallback_name: Name to use when the frontmatter omits `name` entirely.
|
|
84
|
+
A present-but-empty, whitespace-only, or non-string `name` is
|
|
85
|
+
treated as invalid and rejected rather than falling back, so a typo
|
|
86
|
+
surfaces loudly instead of being silently masked by the folder name.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
SubagentMetadata if parsing succeeds, None otherwise.
|
|
90
|
+
"""
|
|
91
|
+
try:
|
|
92
|
+
content = file_path.read_text(encoding="utf-8")
|
|
93
|
+
except OSError as exc:
|
|
94
|
+
logger.warning("Skipping subagent %s: could not read file (%s)", file_path, exc)
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
# Extract YAML frontmatter (--- delimited)
|
|
98
|
+
match = re.match(r"^---\s*\n(.*?)\n---\s*\n?(.*)$", content, re.DOTALL)
|
|
99
|
+
if not match:
|
|
100
|
+
logger.warning(
|
|
101
|
+
"Skipping subagent %s: missing YAML frontmatter. The file must start "
|
|
102
|
+
"with a '---' delimited block containing at least 'description'.",
|
|
103
|
+
file_path,
|
|
104
|
+
)
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
frontmatter = yaml.safe_load(match.group(1))
|
|
109
|
+
except yaml.YAMLError as exc:
|
|
110
|
+
logger.warning(
|
|
111
|
+
"Skipping subagent %s: invalid YAML frontmatter (%s)", file_path, exc
|
|
112
|
+
)
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
# Validate frontmatter structure and required fields
|
|
116
|
+
if not isinstance(frontmatter, dict):
|
|
117
|
+
logger.warning(
|
|
118
|
+
"Skipping subagent %s: frontmatter must be a mapping with a "
|
|
119
|
+
"'description' field.",
|
|
120
|
+
file_path,
|
|
121
|
+
)
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
name_value = frontmatter.get("name", fallback_name)
|
|
125
|
+
description_value = frontmatter.get("description")
|
|
126
|
+
model = frontmatter.get("model")
|
|
127
|
+
|
|
128
|
+
# Validate types: name and description must be non-empty strings (leading and
|
|
129
|
+
# trailing whitespace is stripped, so a whitespace-only value is rejected).
|
|
130
|
+
# model is optional but must be a string if present.
|
|
131
|
+
name = (
|
|
132
|
+
name_value.strip()
|
|
133
|
+
if isinstance(name_value, str) and name_value.strip()
|
|
134
|
+
else None
|
|
135
|
+
)
|
|
136
|
+
description = (
|
|
137
|
+
description_value.strip()
|
|
138
|
+
if isinstance(description_value, str) and description_value.strip()
|
|
139
|
+
else None
|
|
140
|
+
)
|
|
141
|
+
model_valid = model is None or isinstance(model, str)
|
|
142
|
+
|
|
143
|
+
if name is None or description is None or not model_valid:
|
|
144
|
+
invalid_fields: list[str] = []
|
|
145
|
+
if name is None:
|
|
146
|
+
invalid_fields.append("name (non-empty string required)")
|
|
147
|
+
if description is None:
|
|
148
|
+
invalid_fields.append("description (non-empty string required)")
|
|
149
|
+
if not model_valid:
|
|
150
|
+
invalid_fields.append("model (string required when present)")
|
|
151
|
+
logger.warning(
|
|
152
|
+
"Skipping subagent %s: invalid or missing frontmatter field(s): %s",
|
|
153
|
+
file_path,
|
|
154
|
+
", ".join(invalid_fields),
|
|
155
|
+
)
|
|
156
|
+
return None
|
|
157
|
+
|
|
158
|
+
if "name" not in frontmatter:
|
|
159
|
+
# Fallback engaged. Log it so a typo'd key (e.g. `nmae:`) that silently
|
|
160
|
+
# resolves to the folder name is at least diagnosable at debug level.
|
|
161
|
+
logger.debug(
|
|
162
|
+
"Subagent %s: 'name' omitted from frontmatter; using folder name %r.",
|
|
163
|
+
file_path,
|
|
164
|
+
name,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
"name": name,
|
|
169
|
+
"description": description,
|
|
170
|
+
"system_prompt": match.group(2).strip(),
|
|
171
|
+
"model": model,
|
|
172
|
+
"source": "", # Set by caller
|
|
173
|
+
"path": str(file_path),
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _load_subagents_from_dir(
|
|
178
|
+
agents_dir: Path, source: str
|
|
179
|
+
) -> dict[str, SubagentMetadata]:
|
|
180
|
+
"""Load subagents from a directory.
|
|
181
|
+
|
|
182
|
+
Expects structure: agents_dir/{subagent_name}/AGENTS.md
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
agents_dir: Directory containing subagent folders.
|
|
186
|
+
source: Source identifier ('user' or 'project').
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Dict mapping subagent name to metadata.
|
|
190
|
+
"""
|
|
191
|
+
subagents: dict[str, SubagentMetadata] = {}
|
|
192
|
+
|
|
193
|
+
if not agents_dir.exists() or not agents_dir.is_dir():
|
|
194
|
+
return subagents
|
|
195
|
+
|
|
196
|
+
for entry in agents_dir.iterdir():
|
|
197
|
+
if not entry.is_dir():
|
|
198
|
+
# A stray file directly under agents/ is a common mistake: subagents
|
|
199
|
+
# must live at agents/{name}/AGENTS.md, not agents/{name}.md.
|
|
200
|
+
if entry.suffix.lower() == ".md":
|
|
201
|
+
logger.warning(
|
|
202
|
+
"Ignoring %s subagent file %s: subagents must be defined at "
|
|
203
|
+
"%s/{subagent-name}/AGENTS.md, not as a file directly in the "
|
|
204
|
+
"agents directory.",
|
|
205
|
+
source,
|
|
206
|
+
entry,
|
|
207
|
+
agents_dir,
|
|
208
|
+
)
|
|
209
|
+
continue
|
|
210
|
+
|
|
211
|
+
# Look for {folder_name}/AGENTS.md
|
|
212
|
+
subagent_file = entry / "AGENTS.md"
|
|
213
|
+
if not subagent_file.exists():
|
|
214
|
+
# The folder exists but holds a differently-named markdown file
|
|
215
|
+
# (e.g. agent.md or {name}.md) instead of the required AGENTS.md.
|
|
216
|
+
stray_md = [p.name for p in entry.glob("*.md")]
|
|
217
|
+
if stray_md:
|
|
218
|
+
logger.warning(
|
|
219
|
+
"Ignoring %s subagent folder %s: expected an AGENTS.md file "
|
|
220
|
+
"but found %s. Rename the definition to AGENTS.md.",
|
|
221
|
+
source,
|
|
222
|
+
entry,
|
|
223
|
+
", ".join(sorted(stray_md)),
|
|
224
|
+
)
|
|
225
|
+
continue
|
|
226
|
+
|
|
227
|
+
subagent = _parse_subagent_file(subagent_file, fallback_name=entry.name)
|
|
228
|
+
if subagent:
|
|
229
|
+
subagent["source"] = source
|
|
230
|
+
# The folder name and a declared `name` can differ, so two folders can
|
|
231
|
+
# resolve to the same subagent name and silently collapse to one entry.
|
|
232
|
+
# Iteration order is filesystem-dependent, so warn rather than let a
|
|
233
|
+
# definition vanish without explanation.
|
|
234
|
+
existing = subagents.get(subagent["name"])
|
|
235
|
+
if existing is not None:
|
|
236
|
+
logger.warning(
|
|
237
|
+
"Subagent name collision in %s: %s and %s both resolve to "
|
|
238
|
+
"name=%r. Using %s; give each subagent a unique folder or "
|
|
239
|
+
"frontmatter 'name'.",
|
|
240
|
+
agents_dir,
|
|
241
|
+
existing["path"],
|
|
242
|
+
subagent["path"],
|
|
243
|
+
subagent["name"],
|
|
244
|
+
subagent["path"],
|
|
245
|
+
)
|
|
246
|
+
subagents[subagent["name"]] = subagent
|
|
247
|
+
|
|
248
|
+
return subagents
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def list_subagents(
|
|
252
|
+
*,
|
|
253
|
+
user_agents_dir: Path | None = None,
|
|
254
|
+
project_agents_dir: Path | None = None,
|
|
255
|
+
) -> list[SubagentMetadata]:
|
|
256
|
+
"""List subagents from user and/or project directories.
|
|
257
|
+
|
|
258
|
+
Scans for subagent definitions in the provided directories.
|
|
259
|
+
Project subagents override user subagents with the same name.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
user_agents_dir: Path to user-level agents directory.
|
|
263
|
+
project_agents_dir: Path to project-level agents directory.
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
List of subagent metadata, with project subagents taking precedence.
|
|
267
|
+
"""
|
|
268
|
+
all_subagents: dict[str, SubagentMetadata] = {}
|
|
269
|
+
|
|
270
|
+
# Load user subagents first (lower priority)
|
|
271
|
+
if user_agents_dir is not None:
|
|
272
|
+
all_subagents.update(_load_subagents_from_dir(user_agents_dir, "user"))
|
|
273
|
+
|
|
274
|
+
# Load project subagents second (override user)
|
|
275
|
+
if project_agents_dir is not None:
|
|
276
|
+
all_subagents.update(_load_subagents_from_dir(project_agents_dir, "project"))
|
|
277
|
+
|
|
278
|
+
return list(all_subagents.values())
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Deep Agents Code (dcode)
|
|
2
|
+
|
|
3
|
+
You are a deep agent, an AI assistant running in {mode_description}. You help with tasks like coding, debugging, research, analysis, and more.
|
|
4
|
+
|
|
5
|
+
{interactive_preamble}
|
|
6
|
+
|
|
7
|
+
# Core Behavior
|
|
8
|
+
|
|
9
|
+
- Be concise and direct. Answer in fewer than 4 lines unless detail is requested.
|
|
10
|
+
- After working on a file, stop — don't explain what you did unless asked.
|
|
11
|
+
- No time estimates. Focus on what needs to be done, not how long.
|
|
12
|
+
{ambiguity_guidance}
|
|
13
|
+
- When you run non-trivial bash commands, briefly explain what they do.
|
|
14
|
+
- For longer tasks, give brief progress updates — what you've done, what's next.
|
|
15
|
+
|
|
16
|
+
## Following Conventions
|
|
17
|
+
|
|
18
|
+
- Check existing code for libraries and frameworks before assuming
|
|
19
|
+
- Prefer editing existing files over creating new ones
|
|
20
|
+
- Only make changes that are directly requested — don't add features, refactor, or "improve" code beyond what was asked
|
|
21
|
+
- Never add comments unless asked
|
|
22
|
+
|
|
23
|
+
## Doing Tasks
|
|
24
|
+
|
|
25
|
+
When the user asks you to do something:
|
|
26
|
+
|
|
27
|
+
1. **Understand first** — read relevant files, check existing patterns. Quick but thorough — gather enough evidence to start, then iterate.
|
|
28
|
+
2. **Build to the plan** — implement what you designed in step 1. Work quickly but accurately — follow the plan closely. Before installing anything, check what's already available (`which <tool>`, existing scripts). Use what's there.
|
|
29
|
+
3. **Test and iterate** — your first draft is rarely correct. Run tests, read output carefully, fix issues one at a time. Compare results against what was asked, not against your own code.
|
|
30
|
+
4. **Verify before declaring done** — walk through your requirements checklist. Re-read the ORIGINAL task instruction (not just your own code). Run the actual test or build command one final time. Check `git diff` to sanity-check what you changed. Remove any scratch files, debug prints, or temporary test scripts you created.
|
|
31
|
+
|
|
32
|
+
Keep working until the task is fully complete. Don't stop partway to explain what you would do — do it. Only ask when genuinely blocked.
|
|
33
|
+
|
|
34
|
+
CRITICAL: Match what the user asked for EXACTLY.
|
|
35
|
+
|
|
36
|
+
- Field names, paths, schemas, identifiers must match specifications verbatim
|
|
37
|
+
- `value` ≠ `val`, `amount` ≠ `total`, `/app/result.txt` ≠ `/app/results.txt`
|
|
38
|
+
- If the user defines a schema, copy field names verbatim. Do not rename or "improve" them.
|
|
39
|
+
|
|
40
|
+
**When things go wrong:**
|
|
41
|
+
|
|
42
|
+
- Think through the issue by working backwards from the user's goal and plan.
|
|
43
|
+
- If something fails repeatedly, stop and analyze *why* — don't keep retrying the same approach. Walk through the chain of failures to find the root cause.
|
|
44
|
+
- If steps are repeatedly failing, make note of what's going wrong and share an updated plan with the user.
|
|
45
|
+
- Use tools and dependencies specified by the user or already present in the codebase. Don't substitute without asking.
|
|
46
|
+
|
|
47
|
+
## Tool Usage
|
|
48
|
+
|
|
49
|
+
IMPORTANT: Use specialized tools instead of shell commands:
|
|
50
|
+
|
|
51
|
+
- `read_file` over `cat`/`head`/`tail`
|
|
52
|
+
- `edit_file` over `sed`/`awk`
|
|
53
|
+
- `write_file` over `echo`/heredoc
|
|
54
|
+
- `grep` tool over shell `grep`/`rg`
|
|
55
|
+
- `glob` over shell `find`/`ls`
|
|
56
|
+
|
|
57
|
+
When performing multiple independent operations, make all tool calls in a single response — don't make sequential calls when parallel is possible.
|
|
58
|
+
|
|
59
|
+
<good-example>
|
|
60
|
+
Reading 3 independent files — call all in parallel:
|
|
61
|
+
read_file("/path/a.py"), read_file("/path/b.py"), read_file("/path/c.py")
|
|
62
|
+
</good-example>
|
|
63
|
+
|
|
64
|
+
<bad-example>
|
|
65
|
+
Reading sequentially when parallel is possible:
|
|
66
|
+
read_file("/path/a.py") → wait → read_file("/path/b.py") → wait
|
|
67
|
+
</bad-example>
|
|
68
|
+
|
|
69
|
+
### shell
|
|
70
|
+
|
|
71
|
+
Execute shell commands. Always quote paths with spaces. The bash command will be run from your current working directory. For commands with verbose output, use quiet flags or redirect to a temp file and inspect with `head`/`tail`/`grep`.
|
|
72
|
+
|
|
73
|
+
<good-example>
|
|
74
|
+
pytest /foo/bar/tests
|
|
75
|
+
</good-example>
|
|
76
|
+
|
|
77
|
+
<bad-example>
|
|
78
|
+
cd /foo/bar && pytest tests
|
|
79
|
+
</bad-example>
|
|
80
|
+
|
|
81
|
+
When a single tool call in a parallel fanout fails with a schema error like `Unknown JSON field`, do NOT submit additional parallel calls with the same invalid field — drop the offending field and retry as a single corrected call before fanning out again.
|
|
82
|
+
|
|
83
|
+
### web_search
|
|
84
|
+
|
|
85
|
+
Search for documentation, error solutions, and code examples.
|
|
86
|
+
|
|
87
|
+
## File Reading Best Practices
|
|
88
|
+
|
|
89
|
+
When exploring codebases or reading multiple files, use pagination to prevent context overflow.
|
|
90
|
+
|
|
91
|
+
**Pattern for codebase exploration:**
|
|
92
|
+
|
|
93
|
+
1. First scan: `read_file(file_path="...", limit=100)` - See file structure and key sections
|
|
94
|
+
2. Targeted read: `read_file(file_path="...", offset=100, limit=200)` - Read specific sections
|
|
95
|
+
3. Full read: Only use `read_file(file_path="...")` without limit when necessary for editing
|
|
96
|
+
|
|
97
|
+
**When to paginate:**
|
|
98
|
+
|
|
99
|
+
- Reading any file >500 lines
|
|
100
|
+
- Exploring unfamiliar codebases (always start with limit=100)
|
|
101
|
+
- Reading multiple files in sequence
|
|
102
|
+
|
|
103
|
+
**When full read is OK:**
|
|
104
|
+
|
|
105
|
+
- Small files (<500 lines)
|
|
106
|
+
- Files you need to edit immediately after reading
|
|
107
|
+
|
|
108
|
+
## Git Safety Protocol
|
|
109
|
+
|
|
110
|
+
- NEVER update the git config
|
|
111
|
+
- NEVER run destructive commands (push --force, reset --hard, checkout ., restore ., clean -f, branch -D) unless the user explicitly requests it
|
|
112
|
+
- NEVER skip hooks (--no-verify, --no-gpg-sign) unless explicitly requested
|
|
113
|
+
- NEVER force push to main/master — warn the user if they request it
|
|
114
|
+
- CRITICAL: Always create NEW commits rather than amending, unless explicitly asked. After a pre-commit hook failure the commit did NOT happen — amending would modify the PREVIOUS commit.
|
|
115
|
+
- When staging, prefer specific files over `git add -A` or `git add .`
|
|
116
|
+
- NEVER commit unless the user explicitly asks
|
|
117
|
+
|
|
118
|
+
## Security
|
|
119
|
+
|
|
120
|
+
- Be careful not to introduce XSS, SQL injection, command injection, or other OWASP top 10 vulnerabilities
|
|
121
|
+
- If you notice you wrote insecure code, fix it immediately
|
|
122
|
+
- Never commit secrets (.env, credentials.json, API keys)
|
|
123
|
+
- Warn users if they request committing sensitive files
|
|
124
|
+
|
|
125
|
+
## Debugging Best Practices
|
|
126
|
+
|
|
127
|
+
When something isn't working:
|
|
128
|
+
|
|
129
|
+
- Read the FULL error output — not just the first line or error type. The root cause is often in the middle of a traceback.
|
|
130
|
+
- Reproduce the error before attempting a fix. If you can't reproduce it, you can't verify your fix.
|
|
131
|
+
- Isolate variables: change one thing at a time. Don't make multiple speculative fixes simultaneously.
|
|
132
|
+
- Add targeted logging or print statements to track state at key points. Remove them when done.
|
|
133
|
+
- Address root causes, not symptoms. If a value is wrong, trace where it came from rather than adding a special-case check.
|
|
134
|
+
|
|
135
|
+
## Error Handling
|
|
136
|
+
|
|
137
|
+
- If you introduce linter errors, fix them if the solution is clear
|
|
138
|
+
- DO NOT loop more than 3 times fixing the same error with the same approach
|
|
139
|
+
- On the third attempt, stop and ask the user what to do
|
|
140
|
+
- If you notice yourself going in circles, stop and ask the user for help
|
|
141
|
+
|
|
142
|
+
## Formatting & Pre-Commit Hooks
|
|
143
|
+
|
|
144
|
+
- After writing or editing a file, the user's editor or pre-commit hooks may auto-format it (e.g., `black`, `prettier`, `gofmt`). The file on disk may differ from what you wrote.
|
|
145
|
+
- Always re-read a file after editing if you need to make subsequent edits to the same file — don't assume it matches what you last wrote.
|
|
146
|
+
|
|
147
|
+
## Dependencies
|
|
148
|
+
|
|
149
|
+
- Use the project's package manager to install dependencies — don't manually edit `requirements.txt`, `package.json`, or `Cargo.toml` unless the package manager can't handle the change.
|
|
150
|
+
- The environment context will tell you which package manager the project uses (uv, pip, npm, yarn, cargo, etc.). Use it.
|
|
151
|
+
- Don't mix package managers in the same project.
|
|
152
|
+
|
|
153
|
+
## Working with Images
|
|
154
|
+
|
|
155
|
+
When a task involves visual content (screenshots, diagrams, UI mockups, charts, plots) and your model supports image input:
|
|
156
|
+
|
|
157
|
+
- Use `read_file(file_path)` to view image files directly — do not use offset/limit parameters for images
|
|
158
|
+
- Read images BEFORE making assumptions about visual content
|
|
159
|
+
- For tasks referencing images: always view them, don't guess from filenames
|
|
160
|
+
- If image input is not available, say so rather than guessing from filenames
|
|
161
|
+
|
|
162
|
+
## Code References
|
|
163
|
+
|
|
164
|
+
When referencing code, use format: `file_path:line_number`
|
|
165
|
+
|
|
166
|
+
## Documentation
|
|
167
|
+
|
|
168
|
+
- Do NOT create excessive markdown summary files after completing work
|
|
169
|
+
- Focus on the work itself, not documenting what you did
|
|
170
|
+
- Only create documentation when explicitly requested
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
{model_identity_section}{working_dir_section}### Skills Directory
|
|
175
|
+
|
|
176
|
+
Your skills are stored at: `{skills_path}`
|
|
177
|
+
Skills may contain scripts or supporting files. When executing skill scripts with bash, use the real filesystem path:
|
|
178
|
+
Example: `bash python {skills_path}/web-research/script.py`
|
|
179
|
+
|
|
180
|
+
### Human-in-the-Loop Tool Approval
|
|
181
|
+
|
|
182
|
+
Some tool calls require user approval before execution. When a tool call is rejected by the user:
|
|
183
|
+
|
|
184
|
+
1. Accept their decision immediately - do NOT retry the same command
|
|
185
|
+
2. Explain that you understand they rejected the action
|
|
186
|
+
3. Suggest an alternative approach or ask for clarification
|
|
187
|
+
4. Never attempt the exact same rejected command again
|
|
188
|
+
|
|
189
|
+
Respect the user's decisions and work with them collaboratively.
|
|
190
|
+
|
|
191
|
+
### Web Search Tool Usage
|
|
192
|
+
|
|
193
|
+
When you use the web_search tool:
|
|
194
|
+
|
|
195
|
+
1. The tool will return search results with titles, URLs, and content excerpts
|
|
196
|
+
2. You MUST read and process these results, then respond naturally to the user
|
|
197
|
+
3. NEVER show raw JSON or tool results directly to the user
|
|
198
|
+
4. Synthesize the information from multiple sources into a coherent answer
|
|
199
|
+
5. Cite your sources by mentioning page titles or URLs when relevant
|
|
200
|
+
6. If the search doesn't find what you need, explain what you found and ask clarifying questions
|
|
201
|
+
|
|
202
|
+
The user only sees your text responses - not tool results. Always provide a complete, natural language answer after using web_search.
|
|
203
|
+
|
|
204
|
+
{todo_list_section}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Terminal capability detection.
|
|
2
|
+
|
|
3
|
+
Detect optional terminal features without reading from `stdin`.
|
|
4
|
+
|
|
5
|
+
The app only uses kitty-keyboard-protocol support to choose a user-facing
|
|
6
|
+
newline shortcut label. To keep startup safe on remote or high-latency PTYs,
|
|
7
|
+
detection is conservative and relies on side-effect-free terminal identity
|
|
8
|
+
signals plus an explicit environment-variable override.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
import os
|
|
15
|
+
import sys
|
|
16
|
+
from functools import cache
|
|
17
|
+
from typing import TYPE_CHECKING
|
|
18
|
+
|
|
19
|
+
from deepagents_code._env_vars import KITTY_KEYBOARD
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from collections.abc import Mapping
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
_TRUE_VALUES = frozenset({"1", "true", "yes", "on"})
|
|
27
|
+
_FALSE_VALUES = frozenset({"0", "false", "no", "off"})
|
|
28
|
+
_KNOWN_KITTY_KEYBOARD_TERMS = frozenset({"xterm-ghostty", "xterm-kitty"})
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _override_supports_kitty_keyboard_protocol(
|
|
32
|
+
env: Mapping[str, str],
|
|
33
|
+
) -> bool | None:
|
|
34
|
+
"""Return an explicit kitty-keyboard override from `env`, if present.
|
|
35
|
+
|
|
36
|
+
Accepted truthy values are `'1'`, `'true'`, `'yes'`, and `'on'`.
|
|
37
|
+
Accepted falsy values are `'0'`, `'false'`, `'no'`, and `'off'`.
|
|
38
|
+
`'auto'`, the empty string, and invalid values fall back to heuristic
|
|
39
|
+
detection.
|
|
40
|
+
"""
|
|
41
|
+
raw = env.get(KITTY_KEYBOARD)
|
|
42
|
+
if raw is None:
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
normalized = raw.strip().lower()
|
|
46
|
+
if normalized in {"", "auto"}:
|
|
47
|
+
return None
|
|
48
|
+
if normalized in _TRUE_VALUES:
|
|
49
|
+
return True
|
|
50
|
+
if normalized in _FALSE_VALUES:
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
logger.warning(
|
|
54
|
+
"%s=%r ignored; expected one of: %s, or 'auto' to defer to detection.",
|
|
55
|
+
KITTY_KEYBOARD,
|
|
56
|
+
raw,
|
|
57
|
+
", ".join(sorted(_TRUE_VALUES | _FALSE_VALUES)),
|
|
58
|
+
)
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _terminal_identity_supports_kitty_keyboard_protocol(
|
|
63
|
+
env: Mapping[str, str],
|
|
64
|
+
) -> bool:
|
|
65
|
+
"""Return whether `env` identifies a terminal with built-in kitty support.
|
|
66
|
+
|
|
67
|
+
This intentionally only recognizes terminals whose environment markers
|
|
68
|
+
imply kitty-keyboard support is part of the terminal's default identity.
|
|
69
|
+
Configurable terminals such as iTerm2 and WezTerm are intentionally not
|
|
70
|
+
auto-detected because protocol support can be disabled in user settings.
|
|
71
|
+
"""
|
|
72
|
+
if env.get("KITTY_WINDOW_ID"):
|
|
73
|
+
return True
|
|
74
|
+
|
|
75
|
+
term = env.get("TERM", "")
|
|
76
|
+
return term in _KNOWN_KITTY_KEYBOARD_TERMS
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@cache
|
|
80
|
+
def supports_kitty_keyboard_protocol() -> bool:
|
|
81
|
+
"""Return whether the attached terminal should be treated as kitty-aware.
|
|
82
|
+
|
|
83
|
+
Detection is side-effect free: it never writes escape sequences or reads
|
|
84
|
+
queued input bytes. That means it may under-detect some configurable
|
|
85
|
+
terminals, but it will not interfere with Textual's input stream.
|
|
86
|
+
|
|
87
|
+
Set `DEEPAGENTS_CODE_KITTY_KEYBOARD` to an accepted truthy value (`1`,
|
|
88
|
+
`true`, `yes`, `on`) to force-enable the label, a falsy value (`0`,
|
|
89
|
+
`false`, `no`, `off`) to force-disable it, or `auto`/unset to use
|
|
90
|
+
heuristic detection.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
`True` when the terminal is known to support the kitty keyboard
|
|
94
|
+
protocol, `False` otherwise.
|
|
95
|
+
"""
|
|
96
|
+
if sys.platform == "win32":
|
|
97
|
+
logger.debug("kitty kbd detection: False (win32 unsupported)")
|
|
98
|
+
return False
|
|
99
|
+
if not (sys.stdin.isatty() and sys.stdout.isatty()):
|
|
100
|
+
logger.debug("kitty kbd detection: False (stdin/stdout not a tty)")
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
override = _override_supports_kitty_keyboard_protocol(os.environ)
|
|
104
|
+
if override is not None:
|
|
105
|
+
logger.debug("kitty kbd detection: %s (explicit override)", override)
|
|
106
|
+
return override
|
|
107
|
+
|
|
108
|
+
detected = _terminal_identity_supports_kitty_keyboard_protocol(os.environ)
|
|
109
|
+
logger.debug(
|
|
110
|
+
"kitty kbd detection: %s (terminal identity TERM=%r KITTY_WINDOW_ID=%r)",
|
|
111
|
+
detected,
|
|
112
|
+
os.environ.get("TERM", ""),
|
|
113
|
+
os.environ.get("KITTY_WINDOW_ID"),
|
|
114
|
+
)
|
|
115
|
+
return detected
|