deepagents 0.2.2__tar.gz → 0.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {deepagents-0.2.2/src/deepagents.egg-info → deepagents-0.2.3}/PKG-INFO +1 -1
- {deepagents-0.2.2 → deepagents-0.2.3}/pyproject.toml +2 -2
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents/backends/utils.py +51 -52
- deepagents-0.2.3/src/deepagents/default_agent_prompt.md +110 -0
- {deepagents-0.2.2 → deepagents-0.2.3/src/deepagents.egg-info}/PKG-INFO +1 -1
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents.egg-info/SOURCES.txt +1 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/LICENSE +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/README.md +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/setup.cfg +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents/__init__.py +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents/backends/__init__.py +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents/backends/composite.py +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents/backends/filesystem.py +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents/backends/protocol.py +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents/backends/state.py +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents/backends/store.py +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents/graph.py +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents/middleware/__init__.py +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents/middleware/agent_memory.py +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents/middleware/filesystem.py +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents/middleware/patch_tool_calls.py +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents/middleware/resumable_shell.py +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents/middleware/subagents.py +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents.egg-info/dependency_links.txt +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents.egg-info/requires.txt +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/src/deepagents.egg-info/top_level.txt +0 -0
- {deepagents-0.2.2 → deepagents-0.2.3}/tests/test_middleware.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "deepagents"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.3"
|
|
4
4
|
description = "General purpose 'deep agent' with sub-agent spawning, todo list capabilities, and mock file system. Built on LangGraph."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -42,7 +42,7 @@ include = ["deepagents*"]
|
|
|
42
42
|
"" = "src"
|
|
43
43
|
|
|
44
44
|
[tool.setuptools.package-data]
|
|
45
|
-
"*" = ["py.typed"]
|
|
45
|
+
"*" = ["py.typed", "*.md"]
|
|
46
46
|
|
|
47
47
|
[tool.ruff]
|
|
48
48
|
line-length = 150
|
|
@@ -6,10 +6,11 @@ enable composition without fragile string parsing.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import re
|
|
9
|
-
import wcmatch.glob as wcglob
|
|
10
9
|
from datetime import UTC, datetime
|
|
11
10
|
from pathlib import Path
|
|
12
|
-
from typing import Any, Literal, TypedDict
|
|
11
|
+
from typing import Any, Literal, TypedDict
|
|
12
|
+
|
|
13
|
+
import wcmatch.glob as wcglob
|
|
13
14
|
|
|
14
15
|
EMPTY_CONTENT_WARNING = "System reminder: File exists but has empty contents"
|
|
15
16
|
MAX_LINE_LENGTH = 10000
|
|
@@ -24,6 +25,7 @@ class FileInfo(TypedDict, total=False):
|
|
|
24
25
|
Minimal contract used across backends. Only "path" is required.
|
|
25
26
|
Other fields are best-effort and may be absent depending on backend.
|
|
26
27
|
"""
|
|
28
|
+
|
|
27
29
|
path: str
|
|
28
30
|
is_dir: bool
|
|
29
31
|
size: int # bytes (approx)
|
|
@@ -32,14 +34,15 @@ class FileInfo(TypedDict, total=False):
|
|
|
32
34
|
|
|
33
35
|
class GrepMatch(TypedDict):
|
|
34
36
|
"""Structured grep match entry."""
|
|
37
|
+
|
|
35
38
|
path: str
|
|
36
39
|
line: int
|
|
37
40
|
text: str
|
|
38
41
|
|
|
39
42
|
|
|
40
43
|
def sanitize_tool_call_id(tool_call_id: str) -> str:
|
|
41
|
-
"""Sanitize tool_call_id to prevent path traversal and separator issues.
|
|
42
|
-
|
|
44
|
+
r"""Sanitize tool_call_id to prevent path traversal and separator issues.
|
|
45
|
+
|
|
43
46
|
Replaces dangerous characters (., /, \) with underscores.
|
|
44
47
|
"""
|
|
45
48
|
sanitized = tool_call_id.replace(".", "_").replace("/", "_").replace("\\", "_")
|
|
@@ -94,10 +97,10 @@ def format_content_with_line_numbers(
|
|
|
94
97
|
|
|
95
98
|
def check_empty_content(content: str) -> str | None:
|
|
96
99
|
"""Check if content is empty and return warning message.
|
|
97
|
-
|
|
100
|
+
|
|
98
101
|
Args:
|
|
99
102
|
content: Content to check
|
|
100
|
-
|
|
103
|
+
|
|
101
104
|
Returns:
|
|
102
105
|
Warning message if empty, None otherwise
|
|
103
106
|
"""
|
|
@@ -108,10 +111,10 @@ def check_empty_content(content: str) -> str | None:
|
|
|
108
111
|
|
|
109
112
|
def file_data_to_string(file_data: dict[str, Any]) -> str:
|
|
110
113
|
"""Convert FileData to plain string content.
|
|
111
|
-
|
|
114
|
+
|
|
112
115
|
Args:
|
|
113
116
|
file_data: FileData dict with 'content' key
|
|
114
|
-
|
|
117
|
+
|
|
115
118
|
Returns:
|
|
116
119
|
Content as string with lines joined by newlines
|
|
117
120
|
"""
|
|
@@ -164,12 +167,12 @@ def format_read_response(
|
|
|
164
167
|
limit: int,
|
|
165
168
|
) -> str:
|
|
166
169
|
"""Format file data for read response with line numbers.
|
|
167
|
-
|
|
170
|
+
|
|
168
171
|
Args:
|
|
169
172
|
file_data: FileData dict
|
|
170
173
|
offset: Line offset (0-indexed)
|
|
171
174
|
limit: Maximum number of lines
|
|
172
|
-
|
|
175
|
+
|
|
173
176
|
Returns:
|
|
174
177
|
Formatted content or error message
|
|
175
178
|
"""
|
|
@@ -177,14 +180,14 @@ def format_read_response(
|
|
|
177
180
|
empty_msg = check_empty_content(content)
|
|
178
181
|
if empty_msg:
|
|
179
182
|
return empty_msg
|
|
180
|
-
|
|
183
|
+
|
|
181
184
|
lines = content.splitlines()
|
|
182
185
|
start_idx = offset
|
|
183
186
|
end_idx = min(start_idx + limit, len(lines))
|
|
184
|
-
|
|
187
|
+
|
|
185
188
|
if start_idx >= len(lines):
|
|
186
189
|
return f"Error: Line offset {offset} exceeds file length ({len(lines)} lines)"
|
|
187
|
-
|
|
190
|
+
|
|
188
191
|
selected_lines = lines[start_idx:end_idx]
|
|
189
192
|
return format_content_with_line_numbers(selected_lines, start_line=start_idx + 1)
|
|
190
193
|
|
|
@@ -196,24 +199,24 @@ def perform_string_replacement(
|
|
|
196
199
|
replace_all: bool,
|
|
197
200
|
) -> tuple[str, int] | str:
|
|
198
201
|
"""Perform string replacement with occurrence validation.
|
|
199
|
-
|
|
202
|
+
|
|
200
203
|
Args:
|
|
201
204
|
content: Original content
|
|
202
205
|
old_string: String to replace
|
|
203
206
|
new_string: Replacement string
|
|
204
207
|
replace_all: Whether to replace all occurrences
|
|
205
|
-
|
|
208
|
+
|
|
206
209
|
Returns:
|
|
207
210
|
Tuple of (new_content, occurrences) on success, or error message string
|
|
208
211
|
"""
|
|
209
212
|
occurrences = content.count(old_string)
|
|
210
|
-
|
|
213
|
+
|
|
211
214
|
if occurrences == 0:
|
|
212
215
|
return f"Error: String not found in file: '{old_string}'"
|
|
213
|
-
|
|
216
|
+
|
|
214
217
|
if occurrences > 1 and not replace_all:
|
|
215
218
|
return f"Error: String '{old_string}' appears {occurrences} times in file. Use replace_all=True to replace all instances, or provide a more specific string with surrounding context."
|
|
216
|
-
|
|
219
|
+
|
|
217
220
|
new_content = content.replace(old_string, new_string)
|
|
218
221
|
return new_content, occurrences
|
|
219
222
|
|
|
@@ -225,33 +228,33 @@ def truncate_if_too_long(result: list[str] | str) -> list[str] | str:
|
|
|
225
228
|
if total_chars > TOOL_RESULT_TOKEN_LIMIT * 4:
|
|
226
229
|
return result[: len(result) * TOOL_RESULT_TOKEN_LIMIT * 4 // total_chars] + [TRUNCATION_GUIDANCE]
|
|
227
230
|
return result
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
231
|
+
# string
|
|
232
|
+
if len(result) > TOOL_RESULT_TOKEN_LIMIT * 4:
|
|
233
|
+
return result[: TOOL_RESULT_TOKEN_LIMIT * 4] + "\n" + TRUNCATION_GUIDANCE
|
|
234
|
+
return result
|
|
232
235
|
|
|
233
236
|
|
|
234
237
|
def _validate_path(path: str | None) -> str:
|
|
235
238
|
"""Validate and normalize a path.
|
|
236
|
-
|
|
239
|
+
|
|
237
240
|
Args:
|
|
238
241
|
path: Path to validate
|
|
239
|
-
|
|
242
|
+
|
|
240
243
|
Returns:
|
|
241
244
|
Normalized path starting with /
|
|
242
|
-
|
|
245
|
+
|
|
243
246
|
Raises:
|
|
244
247
|
ValueError: If path is invalid
|
|
245
248
|
"""
|
|
246
249
|
path = path or "/"
|
|
247
250
|
if not path or path.strip() == "":
|
|
248
251
|
raise ValueError("Path cannot be empty")
|
|
249
|
-
|
|
252
|
+
|
|
250
253
|
normalized = path if path.startswith("/") else "/" + path
|
|
251
|
-
|
|
254
|
+
|
|
252
255
|
if not normalized.endswith("/"):
|
|
253
256
|
normalized += "/"
|
|
254
|
-
|
|
257
|
+
|
|
255
258
|
return normalized
|
|
256
259
|
|
|
257
260
|
|
|
@@ -261,16 +264,16 @@ def _glob_search_files(
|
|
|
261
264
|
path: str = "/",
|
|
262
265
|
) -> str:
|
|
263
266
|
"""Search files dict for paths matching glob pattern.
|
|
264
|
-
|
|
267
|
+
|
|
265
268
|
Args:
|
|
266
269
|
files: Dictionary of file paths to FileData.
|
|
267
270
|
pattern: Glob pattern (e.g., "*.py", "**/*.ts").
|
|
268
271
|
path: Base path to search from.
|
|
269
|
-
|
|
272
|
+
|
|
270
273
|
Returns:
|
|
271
274
|
Newline-separated file paths, sorted by modification time (most recent first).
|
|
272
275
|
Returns "No files found" if no matches.
|
|
273
|
-
|
|
276
|
+
|
|
274
277
|
Example:
|
|
275
278
|
```python
|
|
276
279
|
files = {"/src/main.py": FileData(...), "/test.py": FileData(...)}
|
|
@@ -313,29 +316,28 @@ def _format_grep_results(
|
|
|
313
316
|
output_mode: Literal["files_with_matches", "content", "count"],
|
|
314
317
|
) -> str:
|
|
315
318
|
"""Format grep search results based on output mode.
|
|
316
|
-
|
|
319
|
+
|
|
317
320
|
Args:
|
|
318
321
|
results: Dictionary mapping file paths to list of (line_num, line_content) tuples
|
|
319
322
|
output_mode: Output format - "files_with_matches", "content", or "count"
|
|
320
|
-
|
|
323
|
+
|
|
321
324
|
Returns:
|
|
322
325
|
Formatted string output
|
|
323
326
|
"""
|
|
324
327
|
if output_mode == "files_with_matches":
|
|
325
328
|
return "\n".join(sorted(results.keys()))
|
|
326
|
-
|
|
329
|
+
if output_mode == "count":
|
|
327
330
|
lines = []
|
|
328
331
|
for file_path in sorted(results.keys()):
|
|
329
332
|
count = len(results[file_path])
|
|
330
333
|
lines.append(f"{file_path}: {count}")
|
|
331
334
|
return "\n".join(lines)
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
return "\n".join(lines)
|
|
335
|
+
lines = []
|
|
336
|
+
for file_path in sorted(results.keys()):
|
|
337
|
+
lines.append(f"{file_path}:")
|
|
338
|
+
for line_num, line in results[file_path]:
|
|
339
|
+
lines.append(f" {line_num}: {line}")
|
|
340
|
+
return "\n".join(lines)
|
|
339
341
|
|
|
340
342
|
|
|
341
343
|
def _grep_search_files(
|
|
@@ -346,17 +348,17 @@ def _grep_search_files(
|
|
|
346
348
|
output_mode: Literal["files_with_matches", "content", "count"] = "files_with_matches",
|
|
347
349
|
) -> str:
|
|
348
350
|
"""Search file contents for regex pattern.
|
|
349
|
-
|
|
351
|
+
|
|
350
352
|
Args:
|
|
351
353
|
files: Dictionary of file paths to FileData.
|
|
352
354
|
pattern: Regex pattern to search for.
|
|
353
355
|
path: Base path to search from.
|
|
354
356
|
glob: Optional glob pattern to filter files (e.g., "*.py").
|
|
355
357
|
output_mode: Output format - "files_with_matches", "content", or "count".
|
|
356
|
-
|
|
358
|
+
|
|
357
359
|
Returns:
|
|
358
360
|
Formatted search results. Returns "No matches found" if no results.
|
|
359
|
-
|
|
361
|
+
|
|
360
362
|
Example:
|
|
361
363
|
```python
|
|
362
364
|
files = {"/file.py": FileData(content=["import os", "print('hi')"], ...)}
|
|
@@ -394,6 +396,7 @@ def _grep_search_files(
|
|
|
394
396
|
|
|
395
397
|
# -------- Structured helpers for composition --------
|
|
396
398
|
|
|
399
|
+
|
|
397
400
|
def grep_matches_from_files(
|
|
398
401
|
files: dict[str, Any],
|
|
399
402
|
pattern: str,
|
|
@@ -419,11 +422,7 @@ def grep_matches_from_files(
|
|
|
419
422
|
filtered = {fp: fd for fp, fd in files.items() if fp.startswith(normalized_path)}
|
|
420
423
|
|
|
421
424
|
if glob:
|
|
422
|
-
filtered = {
|
|
423
|
-
fp: fd
|
|
424
|
-
for fp, fd in filtered.items()
|
|
425
|
-
if wcglob.globmatch(Path(fp).name, glob, flags=wcglob.BRACE)
|
|
426
|
-
}
|
|
425
|
+
filtered = {fp: fd for fp, fd in filtered.items() if wcglob.globmatch(Path(fp).name, glob, flags=wcglob.BRACE)}
|
|
427
426
|
|
|
428
427
|
matches: list[GrepMatch] = []
|
|
429
428
|
for file_path, file_data in filtered.items():
|
|
@@ -433,16 +432,16 @@ def grep_matches_from_files(
|
|
|
433
432
|
return matches
|
|
434
433
|
|
|
435
434
|
|
|
436
|
-
def build_grep_results_dict(matches:
|
|
435
|
+
def build_grep_results_dict(matches: list[GrepMatch]) -> dict[str, list[tuple[int, str]]]:
|
|
437
436
|
"""Group structured matches into the legacy dict form used by formatters."""
|
|
438
|
-
grouped:
|
|
437
|
+
grouped: dict[str, list[tuple[int, str]]] = {}
|
|
439
438
|
for m in matches:
|
|
440
439
|
grouped.setdefault(m["path"], []).append((m["line"], m["text"]))
|
|
441
440
|
return grouped
|
|
442
441
|
|
|
443
442
|
|
|
444
443
|
def format_grep_matches(
|
|
445
|
-
matches:
|
|
444
|
+
matches: list[GrepMatch],
|
|
446
445
|
output_mode: Literal["files_with_matches", "content", "count"],
|
|
447
446
|
) -> str:
|
|
448
447
|
"""Format structured grep matches using existing formatting logic."""
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
You are an AI assistant that helps users with various tasks including coding, research, and analysis.
|
|
2
|
+
|
|
3
|
+
# Core Role
|
|
4
|
+
Your core role and behavior may be updated based on user feedback and instructions. When a user tells you how you should behave or what your role should be, update this memory file immediately to reflect that guidance.
|
|
5
|
+
|
|
6
|
+
## Memory-First Protocol
|
|
7
|
+
You have access to a persistent memory system. ALWAYS follow this protocol:
|
|
8
|
+
|
|
9
|
+
**At session start:**
|
|
10
|
+
- Check `ls /memories/` to see what knowledge you have stored
|
|
11
|
+
- If your role description references specific topics, check /memories/ for relevant guides
|
|
12
|
+
|
|
13
|
+
**Before answering questions:**
|
|
14
|
+
- If asked "what do you know about X?" or "how do I do Y?" → Check `ls /memories/` FIRST
|
|
15
|
+
- If relevant memory files exist → Read them and base your answer on saved knowledge
|
|
16
|
+
- Prefer saved knowledge over general knowledge when available
|
|
17
|
+
|
|
18
|
+
**When learning new information:**
|
|
19
|
+
- If user teaches you something or asks you to remember → Save to `/memories/[topic].md`
|
|
20
|
+
- Use descriptive filenames: `/memories/deep-agents-guide.md` not `/memories/notes.md`
|
|
21
|
+
- After saving, verify by reading back the key points
|
|
22
|
+
|
|
23
|
+
**Important:** Your memories persist across sessions. Information stored in /memories/ is more reliable than general knowledge for topics you've specifically studied.
|
|
24
|
+
|
|
25
|
+
# Tone and Style
|
|
26
|
+
Be concise and direct. Answer in fewer than 4 lines unless the user asks for detail.
|
|
27
|
+
After working on a file, just stop - don't explain what you did unless asked.
|
|
28
|
+
Avoid unnecessary introductions or conclusions.
|
|
29
|
+
|
|
30
|
+
When you run non-trivial bash commands, briefly explain what they do.
|
|
31
|
+
|
|
32
|
+
## Proactiveness
|
|
33
|
+
Take action when asked, but don't surprise users with unrequested actions.
|
|
34
|
+
If asked how to approach something, answer first before taking action.
|
|
35
|
+
|
|
36
|
+
## Following Conventions
|
|
37
|
+
- Check existing code for libraries and frameworks before assuming availability
|
|
38
|
+
- Mimic existing code style, naming conventions, and patterns
|
|
39
|
+
- Never add comments unless asked
|
|
40
|
+
|
|
41
|
+
## Task Management
|
|
42
|
+
Use write_todos for complex multi-step tasks (3+ steps). Mark tasks in_progress before starting, completed immediately after finishing.
|
|
43
|
+
For simple 1-2 step tasks, just do them without todos.
|
|
44
|
+
|
|
45
|
+
## File Reading Best Practices
|
|
46
|
+
|
|
47
|
+
**CRITICAL**: When exploring codebases or reading multiple files, ALWAYS use pagination to prevent context overflow.
|
|
48
|
+
|
|
49
|
+
**Pattern for codebase exploration:**
|
|
50
|
+
1. First scan: `read_file(path, limit=100)` - See file structure and key sections
|
|
51
|
+
2. Targeted read: `read_file(path, offset=100, limit=200)` - Read specific sections if needed
|
|
52
|
+
3. Full read: Only use `read_file(path)` without limit when necessary for editing
|
|
53
|
+
|
|
54
|
+
**When to paginate:**
|
|
55
|
+
- Reading any file >500 lines
|
|
56
|
+
- Exploring unfamiliar codebases (always start with limit=100)
|
|
57
|
+
- Reading multiple files in sequence
|
|
58
|
+
- Any research or investigation task
|
|
59
|
+
|
|
60
|
+
**When full read is OK:**
|
|
61
|
+
- Small files (<500 lines)
|
|
62
|
+
- Files you need to edit immediately after reading
|
|
63
|
+
- After confirming file size with first scan
|
|
64
|
+
|
|
65
|
+
**Example workflow:**
|
|
66
|
+
```
|
|
67
|
+
Bad: read_file(/src/large_module.py) # Floods context with 2000+ lines
|
|
68
|
+
Good: read_file(/src/large_module.py, limit=100) # Scan structure first
|
|
69
|
+
read_file(/src/large_module.py, offset=100, limit=100) # Read relevant section
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Working with Subagents (task tool)
|
|
73
|
+
When delegating to subagents:
|
|
74
|
+
- **Use filesystem for large I/O**: If input instructions are large (>500 words) OR expected output is large, communicate via files
|
|
75
|
+
- Write input context/instructions to a file, tell subagent to read it
|
|
76
|
+
- Ask subagent to write their output to a file, then read it after they return
|
|
77
|
+
- This prevents token bloat and keeps context manageable in both directions
|
|
78
|
+
- **Parallelize independent work**: When tasks are independent, spawn parallel subagents to work simultaneously
|
|
79
|
+
- **Clear specifications**: Tell subagent exactly what format/structure you need in their response or output file
|
|
80
|
+
- **Main agent synthesizes**: Subagents gather/execute, main agent integrates results into final deliverable
|
|
81
|
+
|
|
82
|
+
## Tools
|
|
83
|
+
|
|
84
|
+
### execute_bash
|
|
85
|
+
Execute shell commands. Always quote paths with spaces.
|
|
86
|
+
Examples: `pytest /foo/bar/tests` (good), `cd /foo/bar && pytest tests` (bad)
|
|
87
|
+
|
|
88
|
+
### File Tools
|
|
89
|
+
- read_file: Read file contents (use absolute paths)
|
|
90
|
+
- edit_file: Replace exact strings in files (must read first, provide unique old_string)
|
|
91
|
+
- write_file: Create or overwrite files
|
|
92
|
+
- ls: List directory contents
|
|
93
|
+
- glob: Find files by pattern (e.g., "**/*.py")
|
|
94
|
+
- grep: Search file contents
|
|
95
|
+
|
|
96
|
+
Always use absolute paths starting with /.
|
|
97
|
+
|
|
98
|
+
### web_search
|
|
99
|
+
Search for documentation, error solutions, and code examples.
|
|
100
|
+
|
|
101
|
+
### http_request
|
|
102
|
+
Make HTTP requests to APIs (GET, POST, etc.).
|
|
103
|
+
|
|
104
|
+
## Code References
|
|
105
|
+
When referencing code, use format: `file_path:line_number`
|
|
106
|
+
|
|
107
|
+
## Documentation
|
|
108
|
+
- Do NOT create excessive markdown summary/documentation files after completing work
|
|
109
|
+
- Focus on the work itself, not documenting what you did
|
|
110
|
+
- Only create documentation when explicitly requested
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|