xdog-coding 2.1.1__tar.gz → 2.2.0__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.
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/.gitignore +1 -1
- xdog_coding-2.2.0/PKG-INFO +95 -0
- xdog_coding-2.2.0/README.md +67 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/pyproject.toml +6 -6
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/cli/args.py +10 -1
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/cli/config_selector.py +10 -1
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/config.py +16 -7
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/agent_session.py +36 -3
- xdog_coding-2.2.0/src/xdog/coding/core/permissions.py +275 -0
- xdog_coding-2.2.0/src/xdog/coding/core/remote_session.py +39 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/sdk.py +41 -11
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/slash_commands.py +6 -3
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/main.py +8 -2
- xdog_coding-2.2.0/src/xdog/coding/modes/interactive/components/assistant_message.py +63 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/modes/interactive/components/chat_log.py +64 -27
- xdog_coding-2.2.0/src/xdog/coding/modes/interactive/components/custom_editor.py +19 -0
- xdog_coding-2.2.0/src/xdog/coding/modes/interactive/components/editor_layout.py +5 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/modes/interactive/components/footer.py +29 -2
- xdog_coding-2.2.0/src/xdog/coding/modes/interactive/components/permission_prompt.py +91 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/modes/interactive/components/tool_execution.py +119 -64
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/modes/interactive/components/user_message.py +2 -1
- xdog_coding-2.2.0/src/xdog/coding/modes/interactive/interactive_mode.py +967 -0
- xdog_coding-2.2.0/src/xdog/coding/modes/interactive/tool_renderers.py +24 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/modes/print_mode.py +49 -0
- xdog_coding-2.2.0/src/xdog/coding/modes/rpc/rpc_mode.py +231 -0
- xdog_coding-2.2.0/tests/fixtures/terminal_client.py +83 -0
- xdog_coding-2.2.0/tests/test_cli_args.py +44 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/tests/test_cli_config_selector.py +3 -2
- xdog_coding-2.2.0/tests/test_compact_layout.py +471 -0
- xdog_coding-2.2.0/tests/test_footer_context.py +40 -0
- xdog_coding-2.2.0/tests/test_interactive_mode.py +875 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/tests/test_messages.py +27 -5
- xdog_coding-2.2.0/tests/test_permissions.py +121 -0
- xdog_coding-2.2.0/tests/test_rpc_mode.py +93 -0
- xdog_coding-2.2.0/tests/test_sdk_session_state.py +63 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/tests/test_session_smoke.py +23 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/tests/test_slash_commands.py +23 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/tests/test_slash_skills.py +1 -1
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/tests/test_system_prompt.py +2 -0
- xdog_coding-2.2.0/tests/test_terminal_acceptance.py +261 -0
- xdog_coding-2.1.1/PKG-INFO +0 -55
- xdog_coding-2.1.1/README.md +0 -27
- xdog_coding-2.1.1/src/xdog/coding/modes/interactive/components/assistant_message.py +0 -22
- xdog_coding-2.1.1/src/xdog/coding/modes/interactive/components/custom_editor.py +0 -260
- xdog_coding-2.1.1/src/xdog/coding/modes/interactive/interactive_mode.py +0 -543
- xdog_coding-2.1.1/src/xdog/coding/modes/rpc/rpc_mode.py +0 -187
- xdog_coding-2.1.1/tests/test_cli_args.py +0 -23
- xdog_coding-2.1.1/tests/test_footer_context.py +0 -18
- xdog_coding-2.1.1/tests/test_interactive_mode.py +0 -145
- xdog_coding-2.1.1/tests/test_rpc_mode.py +0 -10
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/LICENSE +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/LICENSE-EXCEPTION.md +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/__init__.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/cli/__init__.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/cli/file_processor.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/cli/initial_message.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/cli/list_models.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/cli/session_picker.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/__init__.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/bash_executor.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/compaction/__init__.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/compaction/compaction.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/compaction/utils.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/defaults.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/event_bus.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/exec_utils.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/extensions/__init__.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/extensions/loader.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/extensions/runner.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/extensions/types.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/keybindings.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/messages.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/prompt_templates.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/resource_loader.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/session_manager.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/settings_manager.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/system_prompt.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/timings.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/core/tools/__init__.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/modes/__init__.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/modes/interactive/__init__.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/modes/interactive/components/__init__.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/modes/interactive/theme.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/modes/rpc/__init__.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/py.typed +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/utils/__init__.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/utils/frontmatter.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/utils/git.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/utils/mime.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/utils/shell.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/src/xdog/coding/utils/sleep.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/tests/test_agent_session.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/tests/test_cli_file_processor.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/tests/test_coding_config.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/tests/test_event_bus.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/tests/test_prompt_templates.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/tests/test_resource_loader.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/tests/test_session_manager.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/tests/test_settings_manager.py +0 -0
- {xdog_coding-2.1.1 → xdog_coding-2.2.0}/tests/test_timings.py +0 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: xdog-coding
|
|
3
|
+
Version: 2.2.0
|
|
4
|
+
Summary: Interactive coding agent CLI
|
|
5
|
+
Project-URL: Homepage, https://xdog.942295.xyz
|
|
6
|
+
Project-URL: Repository, https://github.com/litterzhang/xdog
|
|
7
|
+
Project-URL: Documentation, https://xdog.942295.xyz
|
|
8
|
+
Project-URL: Issues, https://github.com/litterzhang/xdog/issues
|
|
9
|
+
Author: HugeMan
|
|
10
|
+
License-Expression: AGPL-3.0-or-later
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
License-File: LICENSE-EXCEPTION.md
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Requires-Dist: click>=8.1.0
|
|
22
|
+
Requires-Dist: pydantic>=2.0
|
|
23
|
+
Requires-Dist: pyyaml>=6.0
|
|
24
|
+
Requires-Dist: xdog-agent>=2.2.0
|
|
25
|
+
Requires-Dist: xdog-ai[openai]>=2.2.0
|
|
26
|
+
Requires-Dist: xdog-tui>=2.2.0
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# xdog-coding
|
|
30
|
+
|
|
31
|
+
**Interactive coding-agent CLI.**
|
|
32
|
+
|
|
33
|
+
A terminal coding agent with session management, built on
|
|
34
|
+
[`xdog-agent`](https://pypi.org/project/xdog-agent/) and the
|
|
35
|
+
[`xdog-tui`](https://pypi.org/project/xdog-tui/) rendering layer.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
uv run xdog-coding
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Interactive terminal
|
|
42
|
+
|
|
43
|
+
The interface uses native scrollback, one status row, and a shared multiline
|
|
44
|
+
editor. Enter submits or queues; Shift/Alt+Enter inserts a newline. Ctrl+O opens
|
|
45
|
+
recent tool/reasoning details: Left/Right chooses an entry and Page Up/Page Down
|
|
46
|
+
scrolls. Escape closes autocomplete/details first, then cancels active work and
|
|
47
|
+
restores queued text ahead of the current draft. Ctrl+Z suspends on POSIX.
|
|
48
|
+
|
|
49
|
+
Permission prompts take priority. Up/Down chooses an action, Enter confirms,
|
|
50
|
+
Escape denies that call, and Page Up/Page Down scrolls the command summary.
|
|
51
|
+
The editor draft stays intact when panels open or the terminal resizes.
|
|
52
|
+
See [terminal verification](../../docs/tui-pi-parity.md); remote coding and
|
|
53
|
+
fullscreen application modes are excluded.
|
|
54
|
+
|
|
55
|
+
## Tool permissions
|
|
56
|
+
|
|
57
|
+
Potentially mutating tool calls are gated immediately before execution. By
|
|
58
|
+
default, read-only filesystem operations and `current_time` run automatically;
|
|
59
|
+
`bash`, filesystem writes/edits/deletes, and unknown extension tools ask first.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
xdog-coding --permission-mode ask # default: ask for dangerous calls
|
|
63
|
+
xdog-coding --permission-mode ask-all # ask before every tool
|
|
64
|
+
xdog-coding --permission-mode allow-all # trusted unattended automation
|
|
65
|
+
xdog-coding --permission-mode deny # allow read-only calls only
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Interactive approvals offer **Allow once**, **Allow for this session** (the exact
|
|
69
|
+
same call only), and **Deny**. Non-interactive runs fail closed when no approver
|
|
70
|
+
is available unless `allow-all` was explicitly selected.
|
|
71
|
+
|
|
72
|
+
The same setting can be stored in `~/.config/xdog/coding/settings.json`:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{"permission_mode": "ask"}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Permission prompts are an execution gate, not an OS sandbox: an approved shell
|
|
79
|
+
command still has the permissions of the `xdog-coding` process.
|
|
80
|
+
|
|
81
|
+
## Part of xdog
|
|
82
|
+
|
|
83
|
+
This package is one piece of [xdog](https://github.com/litterzhang/xdog), a
|
|
84
|
+
local-first toolkit for building, running and scheduling LLM workflows. The
|
|
85
|
+
centrepiece is [`xdog-flow`](https://pypi.org/project/xdog-flow/) — a typed
|
|
86
|
+
workflow format and compiler.
|
|
87
|
+
|
|
88
|
+
Documentation: **https://xdog.942295.xyz**
|
|
89
|
+
|
|
90
|
+
## Licence
|
|
91
|
+
|
|
92
|
+
Copyright (c) 2026 HugeMan <942295.xyz>
|
|
93
|
+
|
|
94
|
+
GNU Affero General Public License v3.0 or later — see
|
|
95
|
+
[LICENSE](https://github.com/litterzhang/xdog/blob/main/LICENSE).
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# xdog-coding
|
|
2
|
+
|
|
3
|
+
**Interactive coding-agent CLI.**
|
|
4
|
+
|
|
5
|
+
A terminal coding agent with session management, built on
|
|
6
|
+
[`xdog-agent`](https://pypi.org/project/xdog-agent/) and the
|
|
7
|
+
[`xdog-tui`](https://pypi.org/project/xdog-tui/) rendering layer.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
uv run xdog-coding
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Interactive terminal
|
|
14
|
+
|
|
15
|
+
The interface uses native scrollback, one status row, and a shared multiline
|
|
16
|
+
editor. Enter submits or queues; Shift/Alt+Enter inserts a newline. Ctrl+O opens
|
|
17
|
+
recent tool/reasoning details: Left/Right chooses an entry and Page Up/Page Down
|
|
18
|
+
scrolls. Escape closes autocomplete/details first, then cancels active work and
|
|
19
|
+
restores queued text ahead of the current draft. Ctrl+Z suspends on POSIX.
|
|
20
|
+
|
|
21
|
+
Permission prompts take priority. Up/Down chooses an action, Enter confirms,
|
|
22
|
+
Escape denies that call, and Page Up/Page Down scrolls the command summary.
|
|
23
|
+
The editor draft stays intact when panels open or the terminal resizes.
|
|
24
|
+
See [terminal verification](../../docs/tui-pi-parity.md); remote coding and
|
|
25
|
+
fullscreen application modes are excluded.
|
|
26
|
+
|
|
27
|
+
## Tool permissions
|
|
28
|
+
|
|
29
|
+
Potentially mutating tool calls are gated immediately before execution. By
|
|
30
|
+
default, read-only filesystem operations and `current_time` run automatically;
|
|
31
|
+
`bash`, filesystem writes/edits/deletes, and unknown extension tools ask first.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
xdog-coding --permission-mode ask # default: ask for dangerous calls
|
|
35
|
+
xdog-coding --permission-mode ask-all # ask before every tool
|
|
36
|
+
xdog-coding --permission-mode allow-all # trusted unattended automation
|
|
37
|
+
xdog-coding --permission-mode deny # allow read-only calls only
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Interactive approvals offer **Allow once**, **Allow for this session** (the exact
|
|
41
|
+
same call only), and **Deny**. Non-interactive runs fail closed when no approver
|
|
42
|
+
is available unless `allow-all` was explicitly selected.
|
|
43
|
+
|
|
44
|
+
The same setting can be stored in `~/.config/xdog/coding/settings.json`:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{"permission_mode": "ask"}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Permission prompts are an execution gate, not an OS sandbox: an approved shell
|
|
51
|
+
command still has the permissions of the `xdog-coding` process.
|
|
52
|
+
|
|
53
|
+
## Part of xdog
|
|
54
|
+
|
|
55
|
+
This package is one piece of [xdog](https://github.com/litterzhang/xdog), a
|
|
56
|
+
local-first toolkit for building, running and scheduling LLM workflows. The
|
|
57
|
+
centrepiece is [`xdog-flow`](https://pypi.org/project/xdog-flow/) — a typed
|
|
58
|
+
workflow format and compiler.
|
|
59
|
+
|
|
60
|
+
Documentation: **https://xdog.942295.xyz**
|
|
61
|
+
|
|
62
|
+
## Licence
|
|
63
|
+
|
|
64
|
+
Copyright (c) 2026 HugeMan <942295.xyz>
|
|
65
|
+
|
|
66
|
+
GNU Affero General Public License v3.0 or later — see
|
|
67
|
+
[LICENSE](https://github.com/litterzhang/xdog/blob/main/LICENSE).
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "xdog-coding"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.2.0"
|
|
8
8
|
description = "Interactive coding agent CLI"
|
|
9
9
|
requires-python = ">=3.12"
|
|
10
10
|
classifiers = [
|
|
@@ -21,9 +21,9 @@ readme = "README.md"
|
|
|
21
21
|
license = "AGPL-3.0-or-later"
|
|
22
22
|
license-files = ["LICENSE", "LICENSE-EXCEPTION.md"]
|
|
23
23
|
dependencies = [
|
|
24
|
-
"xdog-ai>=2.
|
|
25
|
-
"xdog-agent>=2.
|
|
26
|
-
"xdog-tui>=2.
|
|
24
|
+
"xdog-ai[openai]>=2.2.0",
|
|
25
|
+
"xdog-agent>=2.2.0",
|
|
26
|
+
"xdog-tui>=2.2.0",
|
|
27
27
|
"pyyaml>=6.0",
|
|
28
28
|
"click>=8.1.0",
|
|
29
29
|
"pydantic>=2.0",
|
|
@@ -31,9 +31,9 @@ dependencies = [
|
|
|
31
31
|
|
|
32
32
|
[project.urls]
|
|
33
33
|
Homepage = "https://xdog.942295.xyz"
|
|
34
|
-
Repository = "https://github.com/litterzhang/
|
|
34
|
+
Repository = "https://github.com/litterzhang/xdog"
|
|
35
35
|
Documentation = "https://xdog.942295.xyz"
|
|
36
|
-
Issues = "https://github.com/litterzhang/
|
|
36
|
+
Issues = "https://github.com/litterzhang/xdog/issues"
|
|
37
37
|
|
|
38
38
|
[project.scripts]
|
|
39
39
|
xdog-coding = "xdog.coding.main:main"
|
|
@@ -60,10 +60,16 @@ from xdog.coding.cli.session_picker import pick_session_command
|
|
|
60
60
|
)
|
|
61
61
|
@click.option(
|
|
62
62
|
"--thinking-level",
|
|
63
|
-
type=click.Choice(["
|
|
63
|
+
type=click.Choice(["off", "minimal", "low", "medium", "high", "xhigh"]),
|
|
64
64
|
default=None,
|
|
65
65
|
help="Thinking / reasoning level.",
|
|
66
66
|
)
|
|
67
|
+
@click.option(
|
|
68
|
+
"--permission-mode",
|
|
69
|
+
type=click.Choice(["ask", "ask-all", "allow-all", "deny"]),
|
|
70
|
+
default=None,
|
|
71
|
+
help="Tool permission policy.",
|
|
72
|
+
)
|
|
67
73
|
@click.option(
|
|
68
74
|
"--list-models",
|
|
69
75
|
is_flag=True,
|
|
@@ -99,6 +105,7 @@ def cli(
|
|
|
99
105
|
working_dir: Path | None,
|
|
100
106
|
config_path: Path | None,
|
|
101
107
|
thinking_level: str | None,
|
|
108
|
+
permission_mode: str | None,
|
|
102
109
|
list_models: bool,
|
|
103
110
|
pick_session: bool,
|
|
104
111
|
rpc: bool,
|
|
@@ -124,6 +131,8 @@ def cli(
|
|
|
124
131
|
overrides["model"] = model
|
|
125
132
|
if thinking_level is not None:
|
|
126
133
|
overrides["thinking_level"] = thinking_level
|
|
134
|
+
if permission_mode is not None:
|
|
135
|
+
overrides["permission_mode"] = permission_mode
|
|
127
136
|
|
|
128
137
|
# Defer to the main entry-point for actual execution
|
|
129
138
|
from xdog.coding.main import run_agent
|
|
@@ -57,10 +57,19 @@ def config_selector(config_path: Path | None = None) -> GlobalConfig:
|
|
|
57
57
|
cfg = cfg.model_copy(update={"default_model": new_model})
|
|
58
58
|
|
|
59
59
|
# Thinking level
|
|
60
|
-
new_thinking = input(
|
|
60
|
+
new_thinking = input(
|
|
61
|
+
f"Thinking level [{cfg.thinking_level}] (off/minimal/low/medium/high/xhigh): "
|
|
62
|
+
).strip()
|
|
61
63
|
if new_thinking:
|
|
62
64
|
cfg = cfg.model_copy(update={"thinking_level": new_thinking})
|
|
63
65
|
|
|
66
|
+
# Tool permissions
|
|
67
|
+
new_permissions = input(
|
|
68
|
+
f"Permission mode [{cfg.permission_mode}] (ask/ask-all/allow-all/deny): "
|
|
69
|
+
).strip()
|
|
70
|
+
if new_permissions:
|
|
71
|
+
cfg = cfg.model_copy(update={"permission_mode": new_permissions})
|
|
72
|
+
|
|
64
73
|
# Custom instructions
|
|
65
74
|
if cfg.custom_instructions:
|
|
66
75
|
print(f"\nCurrent custom instructions: {cfg.custom_instructions[:80]}...")
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""Global configuration and paths for the coding agent.
|
|
2
2
|
|
|
3
3
|
Follows XDG Base Directory Specification:
|
|
4
|
-
- Config: $XDG_CONFIG_HOME/
|
|
5
|
-
- Data: $XDG_DATA_HOME/
|
|
6
|
-
- State: $XDG_STATE_HOME/
|
|
4
|
+
- Config: $XDG_CONFIG_HOME/xdog/coding/ (settings, models, keybindings)
|
|
5
|
+
- Data: $XDG_DATA_HOME/xdog/coding/ (sessions, auth, extensions)
|
|
6
|
+
- State: $XDG_STATE_HOME/xdog/coding/ (logs, cache)
|
|
7
7
|
- Project: <cwd>/.coding/ (project-local config)
|
|
8
8
|
|
|
9
9
|
Override all XDG paths with a single directory via CODING_DIR env var.
|
|
@@ -24,7 +24,7 @@ from pydantic import BaseModel, Field
|
|
|
24
24
|
# Constants
|
|
25
25
|
# ---------------------------------------------------------------------------
|
|
26
26
|
|
|
27
|
-
MONOREPO_NAME = "
|
|
27
|
+
MONOREPO_NAME = "xdog"
|
|
28
28
|
APP_NAME = "coding"
|
|
29
29
|
VERSION = "0.57.1"
|
|
30
30
|
PROJECT_DIR_NAME = ".coding"
|
|
@@ -45,7 +45,7 @@ def _env_override_dir() -> Path | None:
|
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
def get_config_dir() -> Path:
|
|
48
|
-
"""$XDG_CONFIG_HOME/
|
|
48
|
+
"""$XDG_CONFIG_HOME/xdog/coding/"""
|
|
49
49
|
override = _env_override_dir()
|
|
50
50
|
if override is not None:
|
|
51
51
|
return override
|
|
@@ -54,7 +54,7 @@ def get_config_dir() -> Path:
|
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
def get_data_dir() -> Path:
|
|
57
|
-
"""$XDG_DATA_HOME/
|
|
57
|
+
"""$XDG_DATA_HOME/xdog/coding/"""
|
|
58
58
|
override = _env_override_dir()
|
|
59
59
|
if override is not None:
|
|
60
60
|
return override
|
|
@@ -63,7 +63,7 @@ def get_data_dir() -> Path:
|
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
def get_state_dir() -> Path:
|
|
66
|
-
"""$XDG_STATE_HOME/
|
|
66
|
+
"""$XDG_STATE_HOME/xdog/coding/"""
|
|
67
67
|
override = _env_override_dir()
|
|
68
68
|
if override is not None:
|
|
69
69
|
return override
|
|
@@ -172,6 +172,7 @@ class GlobalConfig(BaseModel):
|
|
|
172
172
|
|
|
173
173
|
default_model: str = ""
|
|
174
174
|
thinking_level: str = "normal"
|
|
175
|
+
permission_mode: str = "ask"
|
|
175
176
|
extensions: list[str] = Field(default_factory=list)
|
|
176
177
|
allowed_tools: list[str] = Field(default_factory=list)
|
|
177
178
|
custom_instructions: str = ""
|
|
@@ -207,6 +208,7 @@ class ProjectConfig(BaseModel):
|
|
|
207
208
|
|
|
208
209
|
model: str | None = None
|
|
209
210
|
thinking_level: str | None = None
|
|
211
|
+
permission_mode: str | None = None
|
|
210
212
|
allowed_tools: list[str] = Field(default_factory=list)
|
|
211
213
|
custom_instructions: str = ""
|
|
212
214
|
extensions: list[str] = Field(default_factory=list)
|
|
@@ -233,6 +235,7 @@ class RuntimeConfig:
|
|
|
233
235
|
|
|
234
236
|
model: str
|
|
235
237
|
thinking_level: str
|
|
238
|
+
permission_mode: str
|
|
236
239
|
allowed_tools: tuple[str, ...]
|
|
237
240
|
custom_instructions: str
|
|
238
241
|
extensions: tuple[str, ...]
|
|
@@ -253,6 +256,11 @@ class RuntimeConfig:
|
|
|
253
256
|
pcfg = project_cfg or ProjectConfig()
|
|
254
257
|
model = ov.get("model") or pcfg.model or global_cfg.default_model
|
|
255
258
|
thinking = ov.get("thinking_level") or pcfg.thinking_level or global_cfg.thinking_level
|
|
259
|
+
permission_mode = (
|
|
260
|
+
ov.get("permission_mode")
|
|
261
|
+
or pcfg.permission_mode
|
|
262
|
+
or global_cfg.permission_mode
|
|
263
|
+
)
|
|
256
264
|
|
|
257
265
|
merged_tools = list(global_cfg.allowed_tools) + list(pcfg.allowed_tools)
|
|
258
266
|
merged_ext = list(global_cfg.extensions) + list(pcfg.extensions)
|
|
@@ -266,6 +274,7 @@ class RuntimeConfig:
|
|
|
266
274
|
return cls(
|
|
267
275
|
model=model,
|
|
268
276
|
thinking_level=thinking,
|
|
277
|
+
permission_mode=permission_mode,
|
|
269
278
|
allowed_tools=tuple(merged_tools),
|
|
270
279
|
custom_instructions="\n\n".join(instructions_parts),
|
|
271
280
|
extensions=tuple(merged_ext),
|
|
@@ -24,6 +24,7 @@ from xdog.ai.types import (
|
|
|
24
24
|
from xdog.coding.core.defaults import COMPACTION_THRESHOLD_RATIO, MAX_CONTEXT_TOKENS
|
|
25
25
|
from xdog.coding.core.event_bus import EventBus, get_event_bus
|
|
26
26
|
from xdog.coding.core.messages import dicts_to_messages, messages_to_dicts
|
|
27
|
+
from xdog.coding.core.permissions import PermissionManager
|
|
27
28
|
from xdog.coding.core.session_manager import SessionData, SessionManager
|
|
28
29
|
from xdog.coding.core.settings_manager import SettingsManager
|
|
29
30
|
from xdog.coding.core.system_prompt import build_system_prompt
|
|
@@ -47,6 +48,9 @@ class AgentSession:
|
|
|
47
48
|
tool_registry: Any # kept for backward compat, may be None
|
|
48
49
|
bash: Any # kept for backward compat, may be None
|
|
49
50
|
working_dir: Path
|
|
51
|
+
context_window: int = MAX_CONTEXT_TOKENS
|
|
52
|
+
max_prompt_tokens: int = 0
|
|
53
|
+
permissions: PermissionManager = field(default_factory=PermissionManager)
|
|
50
54
|
event_bus: EventBus = field(default_factory=get_event_bus)
|
|
51
55
|
_unsubscribe: Any = field(default=None, init=False, repr=False)
|
|
52
56
|
#: Slugs of skills whose instructions are currently in the system prompt.
|
|
@@ -59,6 +63,9 @@ class AgentSession:
|
|
|
59
63
|
if self.session_data.settings:
|
|
60
64
|
self.settings.load_session_settings(self.session_data.settings)
|
|
61
65
|
|
|
66
|
+
# Sessions constructed outside the SDK still receive enforcement.
|
|
67
|
+
self.agent.set_before_tool_call(self.permissions.before_tool_call)
|
|
68
|
+
|
|
62
69
|
# Subscribe to agent events for persistence and event bus forwarding
|
|
63
70
|
self._unsubscribe = self.agent.subscribe(self._on_agent_event)
|
|
64
71
|
|
|
@@ -80,6 +87,11 @@ class AgentSession:
|
|
|
80
87
|
def is_streaming(self) -> bool:
|
|
81
88
|
return self.agent.state.is_streaming
|
|
82
89
|
|
|
90
|
+
@property
|
|
91
|
+
def context_limit(self) -> int:
|
|
92
|
+
"""Prompt limit used for compaction; falls back to the model window."""
|
|
93
|
+
return self.max_prompt_tokens or self.context_window or MAX_CONTEXT_TOKENS
|
|
94
|
+
|
|
83
95
|
# -- Public API --
|
|
84
96
|
|
|
85
97
|
@property
|
|
@@ -176,17 +188,35 @@ class AgentSession:
|
|
|
176
188
|
"""Cancel the current agent turn."""
|
|
177
189
|
self.agent.abort()
|
|
178
190
|
|
|
191
|
+
def cancel(self) -> None:
|
|
192
|
+
"""Cancel active work and discard agent-level queued messages."""
|
|
193
|
+
self.agent.clear_all_queues()
|
|
194
|
+
self.permissions.deny_all()
|
|
195
|
+
self.agent.abort()
|
|
196
|
+
|
|
179
197
|
# -- Model / thinking --
|
|
180
198
|
|
|
181
199
|
def set_model(self, model: str) -> None:
|
|
182
|
-
"""Switch the active model."""
|
|
200
|
+
"""Switch the active model and refresh its provider-reported limits."""
|
|
183
201
|
self.agent.set_model(model)
|
|
202
|
+
try:
|
|
203
|
+
import xdog.ai as ai
|
|
204
|
+
|
|
205
|
+
model_info = ai.provider("copilot").model(model)
|
|
206
|
+
if model_info is not None:
|
|
207
|
+
self.context_window = model_info.context_window or MAX_CONTEXT_TOKENS
|
|
208
|
+
self.max_prompt_tokens = model_info.max_prompt_tokens
|
|
209
|
+
except Exception:
|
|
210
|
+
logger.debug("could not refresh model limits for %s", model, exc_info=True)
|
|
211
|
+
self.settings.set_session_model(model)
|
|
184
212
|
self._persist()
|
|
185
213
|
|
|
186
214
|
def set_thinking_level(self, level: ThinkingLevel | None) -> None:
|
|
187
|
-
"""Switch the thinking/reasoning level."""
|
|
215
|
+
"""Switch and persist the thinking/reasoning level."""
|
|
188
216
|
from dataclasses import replace
|
|
217
|
+
|
|
189
218
|
self.agent.set_options(replace(self.agent.options, thinking=level))
|
|
219
|
+
self.settings.set_session_thinking(level or "off")
|
|
190
220
|
self._persist()
|
|
191
221
|
|
|
192
222
|
# -- Compaction --
|
|
@@ -222,7 +252,7 @@ class AgentSession:
|
|
|
222
252
|
char_count += 200
|
|
223
253
|
|
|
224
254
|
estimated_tokens = char_count / 4
|
|
225
|
-
threshold =
|
|
255
|
+
threshold = self.context_limit * COMPACTION_THRESHOLD_RATIO
|
|
226
256
|
|
|
227
257
|
if estimated_tokens > threshold:
|
|
228
258
|
await self._run_compaction()
|
|
@@ -257,6 +287,7 @@ class AgentSession:
|
|
|
257
287
|
return False
|
|
258
288
|
|
|
259
289
|
self.session_data = loaded
|
|
290
|
+
self.permissions.clear_session_rules()
|
|
260
291
|
restored_messages = list(self.session_data.messages)
|
|
261
292
|
self.agent.replace_messages(restored_messages)
|
|
262
293
|
|
|
@@ -335,6 +366,7 @@ class AgentSession:
|
|
|
335
366
|
RuntimeConfig(
|
|
336
367
|
model=self.agent.state.model or "unknown",
|
|
337
368
|
thinking_level=str(self.agent.options.thinking or "normal"),
|
|
369
|
+
permission_mode=self.permissions.mode,
|
|
338
370
|
allowed_tools=tuple(t.name for t in self.agent.state.tools),
|
|
339
371
|
custom_instructions=self.settings.custom_instructions,
|
|
340
372
|
extensions=(),
|
|
@@ -372,6 +404,7 @@ class AgentSession:
|
|
|
372
404
|
|
|
373
405
|
def dispose(self) -> None:
|
|
374
406
|
"""Clean up resources."""
|
|
407
|
+
self.permissions.close()
|
|
375
408
|
if self._unsubscribe:
|
|
376
409
|
self._unsubscribe()
|
|
377
410
|
self._unsubscribe = None
|