deepy-cli 0.2.11__tar.gz → 0.2.12__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.
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/PKG-INFO +1 -1
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/pyproject.toml +1 -1
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/__init__.py +1 -1
- deepy_cli-0.2.12/src/deepy/data/tools/apply_patch.md +68 -0
- deepy_cli-0.2.12/src/deepy/data/tools/edit_text.md +21 -0
- deepy_cli-0.2.12/src/deepy/data/tools/read_file.md +16 -0
- deepy_cli-0.2.12/src/deepy/data/tools/write_file.md +13 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/prompts/system.py +3 -3
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/prompts/tool_docs.py +4 -2
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/tools/agents.py +193 -79
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/tools/builtin.py +1465 -76
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/tools/file_state.py +61 -6
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/tui/diff.py +66 -14
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/message_view.py +92 -10
- deepy_cli-0.2.11/src/deepy/data/tools/edit.md +0 -17
- deepy_cli-0.2.11/src/deepy/data/tools/modify.md +0 -26
- deepy_cli-0.2.11/src/deepy/data/tools/read.md +0 -8
- deepy_cli-0.2.11/src/deepy/data/tools/write.md +0 -16
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/README.md +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/__main__.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/cli.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/config/__init__.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/config/settings.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/data/__init__.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/data/skills/skill-creator/SKILL.md +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/data/skills/skill-installer/SKILL.md +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/data/tools/AskUserQuestion.md +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/data/tools/WebFetch.md +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/data/tools/WebSearch.md +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/data/tools/__init__.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/data/tools/shell.md +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/data/tools/todo_write.md +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/errors.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/input_suggestions.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/llm/__init__.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/llm/agent.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/llm/compaction.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/llm/context.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/llm/events.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/llm/model_capabilities.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/llm/provider.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/llm/replay.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/llm/runner.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/llm/thinking.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/mcp.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/prompts/__init__.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/prompts/compact.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/prompts/init_agents.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/prompts/rules.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/prompts/runtime_context.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/session_cost.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/sessions/__init__.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/sessions/jsonl.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/sessions/manager.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/skill_market.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/skills.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/status.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/todos.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/tools/__init__.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/tools/result.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/tools/shell_output.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/tools/shell_utils.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/tui/__init__.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/tui/app.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/tui/commands.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/tui/compat.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/tui/runner.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/tui/screens.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/tui/state.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/tui/widgets.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/types/__init__.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/types/sdk.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/types/tool_payloads.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/__init__.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/app.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/ask_user_question.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/exit_summary.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/file_mentions.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/loading_text.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/local_command.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/markdown.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/model_picker.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/prompt_buffer.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/prompt_input.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/session_list.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/session_picker.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/skill_picker.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/slash_commands.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/status_footer.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/styles.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/terminal.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/theme_picker.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/thinking_state.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/ui/welcome.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/update_check.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/usage.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/utils/__init__.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/utils/debug_logger.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/utils/error_logger.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/utils/json.py +0 -0
- {deepy_cli-0.2.11 → deepy_cli-0.2.12}/src/deepy/utils/notify.py +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
## apply_patch
|
|
2
|
+
|
|
3
|
+
Batch structured file operations in one call.
|
|
4
|
+
|
|
5
|
+
Args: `operations`.
|
|
6
|
+
|
|
7
|
+
Use this when a change has multiple edits in one file, touches multiple files,
|
|
8
|
+
creates/deletes/moves files, replaces a larger block, or needs one all-or-nothing
|
|
9
|
+
preflight before writing. Use `edit_text` only for one small single-file exact
|
|
10
|
+
edit where `old_string` and `new_string` are straightforward.
|
|
11
|
+
|
|
12
|
+
Each operation is an object with a `type` and the fields relevant to that type.
|
|
13
|
+
Set unrelated nullable fields to `null` when required by the schema.
|
|
14
|
+
|
|
15
|
+
Supported operation types:
|
|
16
|
+
|
|
17
|
+
- `create_file`: create a new text file with `file_path` and `content`.
|
|
18
|
+
- `replace_file`: explicitly replace a whole existing file with `file_path`,
|
|
19
|
+
`content`, `overwrite=true`, and either `snapshot_id` or `expected_hash`.
|
|
20
|
+
- `delete_file`: delete `file_path`.
|
|
21
|
+
- `move_file`: move `file_path` to `destination_path`.
|
|
22
|
+
- `replace_block`: replace exact `old_text` with `new_text`.
|
|
23
|
+
- `insert_before`: insert `content` before exact `anchor`.
|
|
24
|
+
- `insert_after`: insert `content` after exact `anchor`.
|
|
25
|
+
- `replace_all`: replace every exact `old_text` match with `new_text`.
|
|
26
|
+
|
|
27
|
+
Example:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"operations": [
|
|
32
|
+
{
|
|
33
|
+
"type": "replace_block",
|
|
34
|
+
"file_path": "portfolio/index.html",
|
|
35
|
+
"old_text": "<p>Old bio</p>",
|
|
36
|
+
"new_text": "<p>New bio</p>",
|
|
37
|
+
"expected_occurrences": 1,
|
|
38
|
+
"destination_path": null,
|
|
39
|
+
"content": null,
|
|
40
|
+
"anchor": null,
|
|
41
|
+
"replace_all": null,
|
|
42
|
+
"overwrite": null,
|
|
43
|
+
"snapshot_id": null,
|
|
44
|
+
"expected_hash": null
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"type": "insert_after",
|
|
48
|
+
"file_path": "portfolio/styles.css",
|
|
49
|
+
"anchor": ".about {\\n display: grid;\\n}\\n",
|
|
50
|
+
"content": "\\n.about-tags {\\n display: flex;\\n}\\n",
|
|
51
|
+
"expected_occurrences": 1,
|
|
52
|
+
"destination_path": null,
|
|
53
|
+
"old_text": null,
|
|
54
|
+
"new_text": null,
|
|
55
|
+
"replace_all": null,
|
|
56
|
+
"overwrite": null,
|
|
57
|
+
"snapshot_id": null,
|
|
58
|
+
"expected_hash": null
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Deepy preflights all operations before committing file side effects, preserves
|
|
65
|
+
existing encodings and line endings on updates, writes new text files as UTF-8
|
|
66
|
+
without BOM, and returns per-operation, changed-file, diff, and diff-preview
|
|
67
|
+
metadata. Exact text and anchor operations reject absent, ambiguous, no-op, and
|
|
68
|
+
unexpected-count matches with structured diagnostics.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
## edit_text
|
|
2
|
+
|
|
3
|
+
Make a small exact/string edit to an existing text file.
|
|
4
|
+
|
|
5
|
+
Args: `file_path`, `old_string`, `new_string`, `replace_all`,
|
|
6
|
+
`expected_occurrences`, optional `snippet_id`.
|
|
7
|
+
|
|
8
|
+
Use this for focused edits where the current text is known. Prefer
|
|
9
|
+
`expected_occurrences` as a safety check, especially with `replace_all`. If a
|
|
10
|
+
partial `read_file` returned a `snippet_id`, pass it only when you intentionally
|
|
11
|
+
want to restrict the replacement to that snippet. For ordinary single-file exact
|
|
12
|
+
edits, pass `file_path` and omit `snippet_id`; Deepy can internally promote a
|
|
13
|
+
fresh partial read to a full-file exact edit when needed. Do not pass
|
|
14
|
+
`snapshot_id` as `snippet_id`; snapshots are for stale protection, while
|
|
15
|
+
snippets are only returned by partial reads.
|
|
16
|
+
|
|
17
|
+
Deepy preserves the existing file encoding and line endings, rejects stale files,
|
|
18
|
+
rejects no-op edits, and returns structured error metadata for missing,
|
|
19
|
+
ambiguous, or count-mismatched replacements. Use `apply_patch` when there are
|
|
20
|
+
multiple edits in one file, multiple files, create/delete/move operations, or a
|
|
21
|
+
larger block replacement.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
## read_file
|
|
2
|
+
|
|
3
|
+
Read files or list directories before changes.
|
|
4
|
+
|
|
5
|
+
Args: `file_path`, optional `offset`, optional `limit`, optional `pages`.
|
|
6
|
+
|
|
7
|
+
Text output includes line numbers. Full text reads record a managed snapshot with
|
|
8
|
+
encoding, line-ending, snapshot id, and content hash metadata for later
|
|
9
|
+
`edit_text`, `write_file`, or `apply_patch` calls. Partial reads return snippet
|
|
10
|
+
metadata that can scope later `edit_text` calls but do not authorize unrestricted
|
|
11
|
+
whole-file replacement. For normal single-file exact edits after a partial read,
|
|
12
|
+
prefer `edit_text` with `file_path` and no `snippet_id`; use the snippet only
|
|
13
|
+
when you need to constrain the replacement to that line range.
|
|
14
|
+
|
|
15
|
+
Non-text files such as images, notebooks, and PDFs may return descriptive
|
|
16
|
+
metadata, but they are not tracked for text mutation.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
## write_file
|
|
2
|
+
|
|
3
|
+
Create a new text file or explicitly replace a whole file.
|
|
4
|
+
|
|
5
|
+
Args: `file_path`, `content`, `overwrite`, optional `snapshot_id`, optional
|
|
6
|
+
`expected_hash`.
|
|
7
|
+
|
|
8
|
+
For new files, Deepy writes UTF-8 without BOM by default. For existing files,
|
|
9
|
+
whole-file replacement requires `overwrite=true` and a fresh `snapshot_id` or
|
|
10
|
+
`expected_hash` from `read_file`; this prevents accidental stale rewrites.
|
|
11
|
+
|
|
12
|
+
Prefer `edit_text` for small targeted edits and `apply_patch` for structured or
|
|
13
|
+
multi-file edits.
|
|
@@ -43,10 +43,10 @@ def build_system_prompt(
|
|
|
43
43
|
return f"""You are Deepy, a terminal coding agent in the user's project.
|
|
44
44
|
|
|
45
45
|
Core rules:
|
|
46
|
-
- Work in the repo with tools: inspect,
|
|
46
|
+
- Work in the repo with tools: inspect, edit, test, verify.
|
|
47
47
|
- Preserve user changes. Prefer small, verifiable edits.
|
|
48
|
-
- Read existing files when you need context; exact `
|
|
49
|
-
- Use `
|
|
48
|
+
- Read existing files when you need context; exact `edit_text` edits can establish the managed snapshot internally.
|
|
49
|
+
- Use `edit_text` for one small single-file exact edit. Use structured `apply_patch.operations` when a change has multiple edits in one file, touches multiple files, creates/deletes/moves files, or replaces a larger block. Use `write_file` for new files or explicit whole-file replacement.
|
|
50
50
|
- After project generators create scaffold files, read and edit the generated block instead of replacing the file.
|
|
51
51
|
- Run shell commands using the Runtime context's command dialect and path style: `powershell` -> PowerShell with Windows paths; `cmd` -> cmd; `posix` -> POSIX shell.
|
|
52
52
|
- Match visible thinking/reasoning language to the user's latest natural language. If the user asks in Chinese, you MUST write visible thinking/reasoning in Chinese unless they explicitly request another language. Do not switch visible thinking/reasoning to English for Chinese requests.
|
|
@@ -26,26 +26,40 @@ def build_function_tools(
|
|
|
26
26
|
questions = args.get("questions")
|
|
27
27
|
return runtime.ask_user_question(questions if isinstance(questions, list) else [])
|
|
28
28
|
|
|
29
|
-
async def
|
|
29
|
+
async def invoke_read_file(_context: object, raw_input: str) -> str:
|
|
30
30
|
args = _tool_args(raw_input)
|
|
31
|
-
return runtime.
|
|
31
|
+
return runtime.read_file(
|
|
32
32
|
_string_arg(args, "file_path"),
|
|
33
33
|
start_line=_int_arg(args, "offset", 1),
|
|
34
34
|
limit=_optional_int_arg(args, "limit"),
|
|
35
35
|
pages=_optional_string_arg(args, "pages"),
|
|
36
36
|
)
|
|
37
37
|
|
|
38
|
-
async def
|
|
38
|
+
async def invoke_edit_text(_context: object, raw_input: str) -> str:
|
|
39
39
|
args = _tool_args(raw_input)
|
|
40
|
-
return runtime.
|
|
40
|
+
return runtime.edit_text(
|
|
41
41
|
_optional_string_arg(args, "file_path"),
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
new=_nullable_string_arg(args, "new_string"),
|
|
42
|
+
_string_arg(args, "old_string"),
|
|
43
|
+
_string_arg(args, "new_string"),
|
|
45
44
|
replace_all=_bool_arg(args, "replace_all", False),
|
|
46
45
|
snippet_id=_optional_string_arg(args, "snippet_id"),
|
|
46
|
+
expected_occurrences=_optional_int_arg(args, "expected_occurrences"),
|
|
47
47
|
)
|
|
48
48
|
|
|
49
|
+
async def invoke_write_file(_context: object, raw_input: str) -> str:
|
|
50
|
+
args = _tool_args(raw_input)
|
|
51
|
+
return runtime.write_file(
|
|
52
|
+
_string_arg(args, "file_path"),
|
|
53
|
+
args.get("content"),
|
|
54
|
+
overwrite=_bool_arg(args, "overwrite", False),
|
|
55
|
+
snapshot_id=_optional_string_arg(args, "snapshot_id"),
|
|
56
|
+
expected_hash=_optional_string_arg(args, "expected_hash"),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
async def invoke_apply_patch(_context: object, raw_input: str) -> str:
|
|
60
|
+
args = _tool_args(raw_input)
|
|
61
|
+
return runtime.apply_patch(args.get("operations"))
|
|
62
|
+
|
|
49
63
|
async def invoke_web_search(_context: object, raw_input: str) -> str:
|
|
50
64
|
args = _tool_args(raw_input)
|
|
51
65
|
return runtime.web_search(_string_arg(args, "query"))
|
|
@@ -101,22 +115,47 @@ def build_function_tools(
|
|
|
101
115
|
strict_json_schema=False,
|
|
102
116
|
),
|
|
103
117
|
FunctionTool(
|
|
104
|
-
name="
|
|
105
|
-
description=
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
118
|
+
name="read_file",
|
|
119
|
+
description=(
|
|
120
|
+
"Read a file or directory and record managed text snapshots for later edits. "
|
|
121
|
+
"Use this before whole-file replacement or when you need context."
|
|
122
|
+
),
|
|
123
|
+
params_json_schema=READ_FILE_SCHEMA,
|
|
124
|
+
on_invoke_tool=invoke_read_file,
|
|
125
|
+
strict_json_schema=True,
|
|
109
126
|
),
|
|
110
127
|
FunctionTool(
|
|
111
|
-
name="
|
|
128
|
+
name="edit_text",
|
|
112
129
|
description=(
|
|
113
|
-
"
|
|
114
|
-
"
|
|
115
|
-
"to
|
|
130
|
+
"Preferred tool for small single-file exact/string edits. Use file_path "
|
|
131
|
+
"with old_string/new_string and expected_occurrences when possible; use "
|
|
132
|
+
"snippet_id only to intentionally scope a partial-read range."
|
|
116
133
|
),
|
|
117
|
-
params_json_schema=
|
|
118
|
-
on_invoke_tool=
|
|
119
|
-
strict_json_schema=
|
|
134
|
+
params_json_schema=EDIT_TEXT_SCHEMA,
|
|
135
|
+
on_invoke_tool=invoke_edit_text,
|
|
136
|
+
strict_json_schema=True,
|
|
137
|
+
),
|
|
138
|
+
FunctionTool(
|
|
139
|
+
name="write_file",
|
|
140
|
+
description=(
|
|
141
|
+
"Create a new text file or explicitly replace a whole file. Existing-file "
|
|
142
|
+
"replacement requires overwrite intent plus snapshot_id or expected_hash."
|
|
143
|
+
),
|
|
144
|
+
params_json_schema=WRITE_FILE_SCHEMA,
|
|
145
|
+
on_invoke_tool=invoke_write_file,
|
|
146
|
+
strict_json_schema=True,
|
|
147
|
+
),
|
|
148
|
+
FunctionTool(
|
|
149
|
+
name="apply_patch",
|
|
150
|
+
description=(
|
|
151
|
+
"Batch structured file operations. Best for multiple edits in one file, "
|
|
152
|
+
"multi-file edits, create/delete/move, or larger block replacements. "
|
|
153
|
+
"Provide an operations array using create_file, replace_file, delete_file, "
|
|
154
|
+
"move_file, replace_block, insert_before, insert_after, or replace_all."
|
|
155
|
+
),
|
|
156
|
+
params_json_schema=APPLY_PATCH_SCHEMA,
|
|
157
|
+
on_invoke_tool=invoke_apply_patch,
|
|
158
|
+
strict_json_schema=True,
|
|
120
159
|
),
|
|
121
160
|
FunctionTool(
|
|
122
161
|
name="WebSearch",
|
|
@@ -172,12 +211,12 @@ def _string_arg(args: dict[str, Any], name: str) -> str:
|
|
|
172
211
|
|
|
173
212
|
def _optional_string_arg(args: dict[str, Any], name: str) -> str | None:
|
|
174
213
|
value = args.get(name)
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
return value
|
|
214
|
+
if not isinstance(value, str):
|
|
215
|
+
return None
|
|
216
|
+
stripped = value.strip()
|
|
217
|
+
if not stripped or stripped.casefold() in {"null", "none", "undefined"}:
|
|
218
|
+
return None
|
|
219
|
+
return value
|
|
181
220
|
|
|
182
221
|
|
|
183
222
|
def _int_arg(args: dict[str, Any], name: str, default: int) -> int:
|
|
@@ -264,111 +303,186 @@ ASK_USER_QUESTION_SCHEMA: dict[str, Any] = {
|
|
|
264
303
|
"additionalProperties": False,
|
|
265
304
|
}
|
|
266
305
|
|
|
267
|
-
|
|
306
|
+
READ_FILE_SCHEMA: dict[str, Any] = {
|
|
268
307
|
"type": "object",
|
|
269
308
|
"properties": {
|
|
270
309
|
"file_path": {
|
|
271
310
|
"type": "string",
|
|
272
|
-
"description": "
|
|
311
|
+
"description": "Path to file or directory under the current project",
|
|
273
312
|
},
|
|
274
313
|
"offset": {
|
|
275
|
-
"type": "number",
|
|
276
|
-
"description": "Line number to start reading from",
|
|
314
|
+
"type": ["number", "null"],
|
|
315
|
+
"description": "Line number to start reading from; null means start at line 1",
|
|
277
316
|
},
|
|
278
317
|
"limit": {
|
|
279
|
-
"type": "number",
|
|
280
|
-
"description": "Number of lines to read",
|
|
318
|
+
"type": ["number", "null"],
|
|
319
|
+
"description": "Number of lines to read; null means the default limit",
|
|
281
320
|
},
|
|
282
321
|
"pages": {
|
|
283
|
-
"type": "string",
|
|
284
|
-
"description":
|
|
285
|
-
'Page range for PDF files (e.g., "1-5", "3", "10-20"). Only applicable to PDF files.'
|
|
286
|
-
),
|
|
322
|
+
"type": ["string", "null"],
|
|
323
|
+
"description": "Page range for PDF files; null for non-PDF reads",
|
|
287
324
|
},
|
|
288
325
|
},
|
|
289
|
-
"required": ["file_path"],
|
|
326
|
+
"required": ["file_path", "offset", "limit", "pages"],
|
|
290
327
|
"additionalProperties": False,
|
|
291
328
|
}
|
|
292
329
|
|
|
293
|
-
|
|
330
|
+
EDIT_TEXT_SCHEMA: dict[str, Any] = {
|
|
294
331
|
"type": "object",
|
|
295
332
|
"properties": {
|
|
296
333
|
"file_path": {
|
|
297
|
-
"type": "string",
|
|
298
|
-
"description": "
|
|
334
|
+
"type": ["string", "null"],
|
|
335
|
+
"description": "Path to file. Use null when snippet_id scopes the edit.",
|
|
299
336
|
},
|
|
300
337
|
"snippet_id": {
|
|
301
|
-
"type": "string",
|
|
302
|
-
"description":
|
|
303
|
-
"Snippet id returned by the Read or Modify tool to scope the search range after "
|
|
304
|
-
"a partial read."
|
|
305
|
-
),
|
|
306
|
-
},
|
|
307
|
-
"content": {
|
|
308
|
-
"type": "string",
|
|
309
|
-
"description": (
|
|
310
|
-
"Complete content for a new file only. Do not use for existing files; read the file "
|
|
311
|
-
"and use old_string/new_string instead."
|
|
312
|
-
),
|
|
338
|
+
"type": ["string", "null"],
|
|
339
|
+
"description": "Snippet id returned by read_file to scope the edit.",
|
|
313
340
|
},
|
|
314
341
|
"old_string": {
|
|
315
342
|
"type": "string",
|
|
316
|
-
"description": "Exact existing text to replace
|
|
343
|
+
"description": "Exact existing text to replace.",
|
|
317
344
|
},
|
|
318
345
|
"new_string": {
|
|
319
346
|
"type": "string",
|
|
320
|
-
"description": "Replacement text
|
|
347
|
+
"description": "Replacement text. Must change file content.",
|
|
321
348
|
},
|
|
322
349
|
"replace_all": {
|
|
323
350
|
"type": "boolean",
|
|
324
|
-
"description": "Replace all occurrences of old_string
|
|
325
|
-
"default": False,
|
|
351
|
+
"description": "Replace all occurrences of old_string.",
|
|
326
352
|
},
|
|
327
353
|
"expected_occurrences": {
|
|
328
|
-
"type": "number",
|
|
329
|
-
"description":
|
|
330
|
-
"Expected number of matches, especially useful as a safety check with replace_all"
|
|
331
|
-
),
|
|
354
|
+
"type": ["number", "null"],
|
|
355
|
+
"description": "Expected number of matches; null skips this safety check.",
|
|
332
356
|
},
|
|
333
357
|
},
|
|
334
|
-
"required": [
|
|
358
|
+
"required": [
|
|
359
|
+
"file_path",
|
|
360
|
+
"snippet_id",
|
|
361
|
+
"old_string",
|
|
362
|
+
"new_string",
|
|
363
|
+
"replace_all",
|
|
364
|
+
"expected_occurrences",
|
|
365
|
+
],
|
|
335
366
|
"additionalProperties": False,
|
|
336
367
|
}
|
|
337
368
|
|
|
338
|
-
|
|
369
|
+
WRITE_FILE_SCHEMA: dict[str, Any] = {
|
|
339
370
|
"type": "object",
|
|
340
371
|
"properties": {
|
|
341
372
|
"file_path": {
|
|
342
373
|
"type": "string",
|
|
343
|
-
"description": "
|
|
374
|
+
"description": "Path to the file under the current project.",
|
|
344
375
|
},
|
|
345
|
-
"
|
|
376
|
+
"content": {
|
|
346
377
|
"type": "string",
|
|
347
|
-
"description":
|
|
348
|
-
"Snippet id returned by the Read or Edit tool to scope the search range after a partial read."
|
|
349
|
-
),
|
|
378
|
+
"description": "Complete file content.",
|
|
350
379
|
},
|
|
351
|
-
"
|
|
380
|
+
"overwrite": {
|
|
381
|
+
"type": "boolean",
|
|
382
|
+
"description": "Explicit intent to replace an existing file.",
|
|
383
|
+
},
|
|
384
|
+
"snapshot_id": {
|
|
385
|
+
"type": ["string", "null"],
|
|
386
|
+
"description": "Snapshot id returned by read_file for existing-file replacement.",
|
|
387
|
+
},
|
|
388
|
+
"expected_hash": {
|
|
389
|
+
"type": ["string", "null"],
|
|
390
|
+
"description": "Content hash returned by read_file for existing-file replacement.",
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
"required": ["file_path", "content", "overwrite", "snapshot_id", "expected_hash"],
|
|
394
|
+
"additionalProperties": False,
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
APPLY_PATCH_OPERATION_SCHEMA: dict[str, Any] = {
|
|
398
|
+
"type": "object",
|
|
399
|
+
"properties": {
|
|
400
|
+
"type": {
|
|
352
401
|
"type": "string",
|
|
353
|
-
"
|
|
402
|
+
"enum": [
|
|
403
|
+
"create_file",
|
|
404
|
+
"replace_file",
|
|
405
|
+
"delete_file",
|
|
406
|
+
"move_file",
|
|
407
|
+
"replace_block",
|
|
408
|
+
"insert_before",
|
|
409
|
+
"insert_after",
|
|
410
|
+
"replace_all",
|
|
411
|
+
],
|
|
412
|
+
"description": "Structured file operation type.",
|
|
354
413
|
},
|
|
355
|
-
"
|
|
414
|
+
"file_path": {
|
|
356
415
|
"type": "string",
|
|
357
|
-
"description": "
|
|
416
|
+
"description": "Path to the source or target file under the current project.",
|
|
358
417
|
},
|
|
359
|
-
"
|
|
360
|
-
"type": "
|
|
361
|
-
"description": "
|
|
362
|
-
|
|
418
|
+
"destination_path": {
|
|
419
|
+
"type": ["string", "null"],
|
|
420
|
+
"description": "Destination path for move_file; null for other operations.",
|
|
421
|
+
},
|
|
422
|
+
"content": {
|
|
423
|
+
"type": ["string", "null"],
|
|
424
|
+
"description": "File content for create_file/replace_file or inserted content for insert operations.",
|
|
425
|
+
},
|
|
426
|
+
"old_text": {
|
|
427
|
+
"type": ["string", "null"],
|
|
428
|
+
"description": "Exact text to replace for replace_block or replace_all.",
|
|
429
|
+
},
|
|
430
|
+
"new_text": {
|
|
431
|
+
"type": ["string", "null"],
|
|
432
|
+
"description": "Replacement text for replace_block or replace_all.",
|
|
433
|
+
},
|
|
434
|
+
"anchor": {
|
|
435
|
+
"type": ["string", "null"],
|
|
436
|
+
"description": "Exact anchor text for insert_before or insert_after.",
|
|
363
437
|
},
|
|
364
438
|
"expected_occurrences": {
|
|
365
|
-
"type": "
|
|
366
|
-
"description":
|
|
367
|
-
|
|
368
|
-
|
|
439
|
+
"type": ["integer", "null"],
|
|
440
|
+
"description": "Expected match count for exact text or anchor operations.",
|
|
441
|
+
},
|
|
442
|
+
"replace_all": {
|
|
443
|
+
"type": ["boolean", "null"],
|
|
444
|
+
"description": "Whether to apply a block or insertion operation to every matching occurrence.",
|
|
445
|
+
},
|
|
446
|
+
"overwrite": {
|
|
447
|
+
"type": ["boolean", "null"],
|
|
448
|
+
"description": "Explicit overwrite intent for replace_file.",
|
|
449
|
+
},
|
|
450
|
+
"snapshot_id": {
|
|
451
|
+
"type": ["string", "null"],
|
|
452
|
+
"description": "Snapshot id returned by read_file for replace_file.",
|
|
453
|
+
},
|
|
454
|
+
"expected_hash": {
|
|
455
|
+
"type": ["string", "null"],
|
|
456
|
+
"description": "Content hash returned by read_file for replace_file.",
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
"required": [
|
|
460
|
+
"type",
|
|
461
|
+
"file_path",
|
|
462
|
+
"destination_path",
|
|
463
|
+
"content",
|
|
464
|
+
"old_text",
|
|
465
|
+
"new_text",
|
|
466
|
+
"anchor",
|
|
467
|
+
"expected_occurrences",
|
|
468
|
+
"replace_all",
|
|
469
|
+
"overwrite",
|
|
470
|
+
"snapshot_id",
|
|
471
|
+
"expected_hash",
|
|
472
|
+
],
|
|
473
|
+
"additionalProperties": False,
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
APPLY_PATCH_SCHEMA: dict[str, Any] = {
|
|
477
|
+
"type": "object",
|
|
478
|
+
"properties": {
|
|
479
|
+
"operations": {
|
|
480
|
+
"type": "array",
|
|
481
|
+
"description": "Structured file operations to preflight and commit as one logical patch.",
|
|
482
|
+
"items": APPLY_PATCH_OPERATION_SCHEMA,
|
|
369
483
|
},
|
|
370
484
|
},
|
|
371
|
-
"required": ["
|
|
485
|
+
"required": ["operations"],
|
|
372
486
|
"additionalProperties": False,
|
|
373
487
|
}
|
|
374
488
|
|