lazyopencode 0.2.0__tar.gz → 0.2.1__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.
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/AGENTS.md +1 -1
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/PKG-INFO +22 -1
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/README.md +21 -0
- lazyopencode-0.2.1/artifacts/demo.png +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/_version.py +2 -2
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/app.py +64 -1
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/bindings.py +1 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/models/customization.py +11 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/writer.py +28 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/widgets/app_footer.py +8 -1
- lazyopencode-0.2.1/src/lazyopencode/widgets/delete_confirm.py +115 -0
- lazyopencode-0.2.0/artifacts/demo.png +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/.github/release-drafter.yml +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/.github/workflows/ci.yml +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/.github/workflows/publish.yml +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/.github/workflows/release-drafter.yml +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/.gitignore +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/.opencode/command/run-quality-gates.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/.opencode/skill/quality-gates/SKILL.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/.pre-commit-config.yaml +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/LICENSE +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/artifacts/lazyclaude-reference.png +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/opencode.json +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/pyproject.toml +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/scripts/check_quality.sh +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/__init__.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/__main__.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/mixins/filtering.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/mixins/help.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/mixins/navigation.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/models/__init__.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/__init__.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/claude_code/__init__.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/claude_code/discovery.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/claude_code/parsers/__init__.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/claude_code/parsers/agent.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/claude_code/parsers/command.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/claude_code/parsers/skill.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/claude_code/plugin_loader.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/discovery.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/gitignore_filter.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/parsers/__init__.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/parsers/agent.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/parsers/command.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/parsers/mcp.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/parsers/plugin.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/parsers/rules.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/parsers/skill.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/parsers/tool.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/styles/app.tcss +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/themes.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/widgets/__init__.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/widgets/combined_panel.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/widgets/detail_pane.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/widgets/filter_input.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/widgets/helpers/__init__.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/widgets/helpers/rendering.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/widgets/level_selector.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/widgets/status_panel.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/widgets/type_panel.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/AGENTS.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/conftest.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/__init__.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/discovery/__init__.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/discovery/test_agents.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/discovery/test_commands.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/discovery/test_full_discovery.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/discovery/test_mcps.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/discovery/test_rules.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/discovery/test_skills.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/agent/explorer.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/command/greet.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/mcp/project-opencode.json +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/mcp/user-opencode.json +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/memory/AGENTS.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/project/AGENTS.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/project/agent/reviewer.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/project/command/project-cmd.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/project/docs/guidelines.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/project/skill/project-skill/SKILL.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/project/skill/project-skill/src/helper.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/skill/task-tracker/SKILL.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/skill/task-tracker/reference.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/skill/task-tracker/scripts/run.sh +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/SPEC.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/__init__.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/conftest.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/complex/.opencode/agent/reviewer.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/complex/.opencode/command/verify.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/complex/.opencode/plugin/metrics.ts +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/complex/.opencode/skill/deploy-helper/SKILL.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/complex/.opencode/skill/deploy-helper/scripts/deploy.sh +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/complex/.opencode/tool/search.ts +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/complex/AGENTS.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/complex/README.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/complex/docs/guidelines.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/complex/opencode.json +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/complex/prompts/auditor.txt +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/file_commands/.opencode/command/deploy.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/file_commands/.opencode/command/greet.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/file_commands/README.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/file_references/README.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/file_references/opencode.json +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/file_references/prompts/agent.txt +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/file_references/templates/cmd.txt +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/inline_commands/README.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/inline_commands/opencode.json +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/minimal/AGENTS.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/minimal/README.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/mixed_config/.opencode/agent/file-agent.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/mixed_config/.opencode/command/file-cmd.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/mixed_config/AGENTS.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/mixed_config/README.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/mixed_config/opencode.json +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/rich_opencode_json/README.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/rich_opencode_json/docs/api-standards.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/rich_opencode_json/opencode.json +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/rich_opencode_json/prompts/security-audit.txt +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/rich_opencode_json/scripts/fake-lint.sh +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/skills_with_tree/.opencode/skill/my-skill/SKILL.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/skills_with_tree/.opencode/skill/my-skill/docs/guide.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/skills_with_tree/.opencode/skill/my-skill/scripts/run.sh +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/skills_with_tree/README.md +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/test_scenarios.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/test_inline.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/test_version.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/unit/__init__.py +0 -0
- {lazyopencode-0.2.0 → lazyopencode-0.2.1}/uv.lock +0 -0
|
@@ -5,7 +5,7 @@ A keyboard-driven TUI for visualizing and managing OpenCode customizations.
|
|
|
5
5
|
## Environment Rules
|
|
6
6
|
- **OS**: Windows (Git Bash). Use forward slashes `/` and `/c/` prefix for absolute paths.
|
|
7
7
|
- **Search**: `rg` and `fd` are installed. Use them for fast searching.
|
|
8
|
-
- **Quality Gates**: Always run quality gates before asking the user to commit changes.
|
|
8
|
+
- **Quality Gates**: Always run quality gates before asking the user to commit changes. Run `bash scripts/check_quality.sh` or individual checks: `uv run ruff check src/ && uv run ruff format --check src/ && uv run mypy src/ && uv run pytest tests/ -q`
|
|
9
9
|
- **TUI Verification**: Do NOT run `uv run lazyopencode` to verify the application. It is a TUI and output cannot be captured effectively. Use unit tests or static analysis instead.
|
|
10
10
|
|
|
11
11
|
## Project Overview
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lazyopencode
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: A lazygit-style TUI for visualizing OpenCode customizations
|
|
5
5
|
Project-URL: Homepage, https://github.com/nikiforovall/lazyopencode
|
|
6
6
|
Project-URL: Repository, https://github.com/nikiforovall/lazyopencode
|
|
@@ -44,6 +44,7 @@ A keyboard-driven TUI for managing OpenCode customizations.
|
|
|
44
44
|
- View commands, agents, skills, rules, MCPs, and plugins
|
|
45
45
|
- Filter by configuration level (global/project)
|
|
46
46
|
- Search within customizations
|
|
47
|
+
- Claude Code compatibility mode (`--claude-code`)
|
|
47
48
|
|
|
48
49
|
## Installation
|
|
49
50
|
|
|
@@ -71,6 +72,8 @@ pip install lazyopencode
|
|
|
71
72
|
| `p` | Project filter |
|
|
72
73
|
| `/` | Search |
|
|
73
74
|
| `e` | Edit selected |
|
|
75
|
+
| `c` | Copy to level |
|
|
76
|
+
| `C` | Copy path |
|
|
74
77
|
| `r` | Refresh |
|
|
75
78
|
| `ctrl+u` | User Config |
|
|
76
79
|
| `?` | Help |
|
|
@@ -90,6 +93,24 @@ LazyOpenCode discovers customizations from:
|
|
|
90
93
|
| Tools | `~/.config/opencode/tool/` | `.opencode/tool/` |
|
|
91
94
|
| Plugins | `~/.config/opencode/plugin/` | `.opencode/plugin/` |
|
|
92
95
|
|
|
96
|
+
## Claude Code Mode
|
|
97
|
+
|
|
98
|
+
Enable Claude Code compatibility to also discover customizations from `~/.claude/`:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
lazyopencode --claude-code
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
This discovers commands, agents, and skills from:
|
|
105
|
+
|
|
106
|
+
| Scope | Path |
|
|
107
|
+
| ------- | ----------------------------------------- |
|
|
108
|
+
| User | `~/.claude/commands/`, `~/.claude/agents/` |
|
|
109
|
+
| Project | `.claude/commands/`, `.claude/agents/` |
|
|
110
|
+
| Plugins | Installed plugins from registry |
|
|
111
|
+
|
|
112
|
+
Claude Code items are marked with 👾 and can be copied to OpenCode paths using `c`.
|
|
113
|
+
|
|
93
114
|
## Inspired By
|
|
94
115
|
|
|
95
116
|
- [LazyClaude](https://github.com/NikiforovAll/lazyclaude) - Similar TUI for Claude Code
|
|
@@ -11,6 +11,7 @@ A keyboard-driven TUI for managing OpenCode customizations.
|
|
|
11
11
|
- View commands, agents, skills, rules, MCPs, and plugins
|
|
12
12
|
- Filter by configuration level (global/project)
|
|
13
13
|
- Search within customizations
|
|
14
|
+
- Claude Code compatibility mode (`--claude-code`)
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
16
17
|
|
|
@@ -38,6 +39,8 @@ pip install lazyopencode
|
|
|
38
39
|
| `p` | Project filter |
|
|
39
40
|
| `/` | Search |
|
|
40
41
|
| `e` | Edit selected |
|
|
42
|
+
| `c` | Copy to level |
|
|
43
|
+
| `C` | Copy path |
|
|
41
44
|
| `r` | Refresh |
|
|
42
45
|
| `ctrl+u` | User Config |
|
|
43
46
|
| `?` | Help |
|
|
@@ -57,6 +60,24 @@ LazyOpenCode discovers customizations from:
|
|
|
57
60
|
| Tools | `~/.config/opencode/tool/` | `.opencode/tool/` |
|
|
58
61
|
| Plugins | `~/.config/opencode/plugin/` | `.opencode/plugin/` |
|
|
59
62
|
|
|
63
|
+
## Claude Code Mode
|
|
64
|
+
|
|
65
|
+
Enable Claude Code compatibility to also discover customizations from `~/.claude/`:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
lazyopencode --claude-code
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
This discovers commands, agents, and skills from:
|
|
72
|
+
|
|
73
|
+
| Scope | Path |
|
|
74
|
+
| ------- | ----------------------------------------- |
|
|
75
|
+
| User | `~/.claude/commands/`, `~/.claude/agents/` |
|
|
76
|
+
| Project | `.claude/commands/`, `.claude/agents/` |
|
|
77
|
+
| Plugins | Installed plugins from registry |
|
|
78
|
+
|
|
79
|
+
Claude Code items are marked with 👾 and can be copied to OpenCode paths using `c`.
|
|
80
|
+
|
|
60
81
|
## Inspired By
|
|
61
82
|
|
|
62
83
|
- [LazyClaude](https://github.com/NikiforovAll/lazyclaude) - Similar TUI for Claude Code
|
|
Binary file
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.2.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 2,
|
|
31
|
+
__version__ = version = '0.2.1'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 2, 1)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -24,6 +24,7 @@ from lazyopencode.services.discovery import ConfigDiscoveryService
|
|
|
24
24
|
from lazyopencode.themes import CUSTOM_THEMES
|
|
25
25
|
from lazyopencode.widgets.app_footer import AppFooter
|
|
26
26
|
from lazyopencode.widgets.combined_panel import CombinedPanel
|
|
27
|
+
from lazyopencode.widgets.delete_confirm import DeleteConfirm
|
|
27
28
|
from lazyopencode.widgets.detail_pane import MainPane
|
|
28
29
|
from lazyopencode.widgets.filter_input import FilterInput
|
|
29
30
|
from lazyopencode.widgets.level_selector import LevelSelector
|
|
@@ -65,6 +66,7 @@ class LazyOpenCode(App, NavigationMixin, FilteringMixin, HelpMixin):
|
|
|
65
66
|
self._filter_input: FilterInput | None = None
|
|
66
67
|
self._app_footer: AppFooter | None = None
|
|
67
68
|
self._level_selector: LevelSelector | None = None
|
|
69
|
+
self._delete_confirm: DeleteConfirm | None = None
|
|
68
70
|
self._last_focused_panel: TypePanel | CombinedPanel | None = None
|
|
69
71
|
self._pending_customization: Customization | None = None
|
|
70
72
|
|
|
@@ -114,6 +116,9 @@ class LazyOpenCode(App, NavigationMixin, FilteringMixin, HelpMixin):
|
|
|
114
116
|
self._level_selector = LevelSelector(id="level-selector")
|
|
115
117
|
yield self._level_selector
|
|
116
118
|
|
|
119
|
+
self._delete_confirm = DeleteConfirm(id="delete-confirm")
|
|
120
|
+
yield self._delete_confirm
|
|
121
|
+
|
|
117
122
|
self._app_footer = AppFooter(id="app-footer")
|
|
118
123
|
yield self._app_footer
|
|
119
124
|
|
|
@@ -181,6 +186,7 @@ class LazyOpenCode(App, NavigationMixin, FilteringMixin, HelpMixin):
|
|
|
181
186
|
"""Handle selection change in a type panel."""
|
|
182
187
|
if self._main_pane:
|
|
183
188
|
self._main_pane.customization = message.customization
|
|
189
|
+
self._update_footer_can_delete(message.customization)
|
|
184
190
|
|
|
185
191
|
def on_type_panel_drill_down(self, message: TypePanel.DrillDown) -> None:
|
|
186
192
|
"""Handle drill down into a customization."""
|
|
@@ -202,6 +208,7 @@ class LazyOpenCode(App, NavigationMixin, FilteringMixin, HelpMixin):
|
|
|
202
208
|
"""Handle selection change in the combined panel."""
|
|
203
209
|
if self._main_pane:
|
|
204
210
|
self._main_pane.customization = message.customization
|
|
211
|
+
self._update_footer_can_delete(message.customization)
|
|
205
212
|
|
|
206
213
|
def on_combined_panel_drill_down(self, message: CombinedPanel.DrillDown) -> None:
|
|
207
214
|
"""Handle drill down from the combined panel."""
|
|
@@ -267,7 +274,6 @@ class LazyOpenCode(App, NavigationMixin, FilteringMixin, HelpMixin):
|
|
|
267
274
|
"""Refresh customizations from disk."""
|
|
268
275
|
self._discovery_service.refresh()
|
|
269
276
|
self._load_customizations()
|
|
270
|
-
self.notify("Refreshed", severity="information")
|
|
271
277
|
|
|
272
278
|
# action_toggle_help handled by HelpMixin
|
|
273
279
|
|
|
@@ -421,6 +427,63 @@ class LazyOpenCode(App, NavigationMixin, FilteringMixin, HelpMixin):
|
|
|
421
427
|
elif self._panels:
|
|
422
428
|
self._panels[0].focus()
|
|
423
429
|
|
|
430
|
+
def _update_footer_can_delete(self, customization: Customization | None) -> None:
|
|
431
|
+
"""Update footer delete indicator based on current selection."""
|
|
432
|
+
if self._app_footer:
|
|
433
|
+
self._app_footer.can_delete = (
|
|
434
|
+
customization is not None and customization.is_deletable()
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
# Delete actions
|
|
438
|
+
|
|
439
|
+
def action_delete_customization(self) -> None:
|
|
440
|
+
"""Delete selected customization."""
|
|
441
|
+
panel = self._get_focused_panel()
|
|
442
|
+
customization = panel.selected_customization if panel else None
|
|
443
|
+
|
|
444
|
+
if not customization:
|
|
445
|
+
self.notify("No customization selected", severity="warning")
|
|
446
|
+
return
|
|
447
|
+
|
|
448
|
+
if not customization.is_deletable():
|
|
449
|
+
self.notify(
|
|
450
|
+
f"Cannot delete {customization.type_label} customizations",
|
|
451
|
+
severity="warning",
|
|
452
|
+
)
|
|
453
|
+
return
|
|
454
|
+
|
|
455
|
+
self._last_focused_panel = panel
|
|
456
|
+
if self._delete_confirm:
|
|
457
|
+
self._delete_confirm.show(customization)
|
|
458
|
+
|
|
459
|
+
def on_delete_confirm_delete_confirmed(
|
|
460
|
+
self, message: DeleteConfirm.DeleteConfirmed
|
|
461
|
+
) -> None:
|
|
462
|
+
"""Handle delete confirmation."""
|
|
463
|
+
from lazyopencode.services.writer import CustomizationWriter
|
|
464
|
+
|
|
465
|
+
writer = CustomizationWriter(
|
|
466
|
+
global_config_path=self._discovery_service.global_config_path,
|
|
467
|
+
project_config_path=self._discovery_service.project_config_path,
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
success, msg = writer.delete_customization(message.customization)
|
|
471
|
+
|
|
472
|
+
if success:
|
|
473
|
+
self.notify(msg, severity="information")
|
|
474
|
+
self.action_refresh()
|
|
475
|
+
else:
|
|
476
|
+
self.notify(msg, severity="error")
|
|
477
|
+
|
|
478
|
+
self._restore_focus_after_selector()
|
|
479
|
+
|
|
480
|
+
def on_delete_confirm_delete_cancelled(
|
|
481
|
+
self,
|
|
482
|
+
message: DeleteConfirm.DeleteCancelled, # noqa: ARG002
|
|
483
|
+
) -> None:
|
|
484
|
+
"""Handle delete cancellation."""
|
|
485
|
+
self._restore_focus_after_selector()
|
|
486
|
+
|
|
424
487
|
|
|
425
488
|
def create_app(
|
|
426
489
|
project_root: Path | None = None,
|
|
@@ -8,6 +8,7 @@ APP_BINDINGS: list[BindingType] = [
|
|
|
8
8
|
Binding("r", "refresh", "Refresh"),
|
|
9
9
|
Binding("e", "open_in_editor", "Edit"),
|
|
10
10
|
Binding("c", "copy_customization", "Copy"),
|
|
11
|
+
Binding("d", "delete_customization", "Delete"),
|
|
11
12
|
Binding("C", "copy_path_to_clipboard", "Copy Path", key_display="shift+c"),
|
|
12
13
|
Binding("ctrl+u", "open_user_config", "User Config"),
|
|
13
14
|
Binding("tab", "focus_next_panel", "Next Panel", show=False),
|
|
@@ -149,3 +149,14 @@ class Customization:
|
|
|
149
149
|
return [ConfigLevel.PROJECT]
|
|
150
150
|
else:
|
|
151
151
|
return [ConfigLevel.GLOBAL]
|
|
152
|
+
|
|
153
|
+
def is_deletable(self) -> bool:
|
|
154
|
+
"""Check if this customization can be deleted."""
|
|
155
|
+
if self.source != ConfigSource.OPENCODE:
|
|
156
|
+
return False
|
|
157
|
+
deletable_types = (
|
|
158
|
+
CustomizationType.COMMAND,
|
|
159
|
+
CustomizationType.AGENT,
|
|
160
|
+
CustomizationType.SKILL,
|
|
161
|
+
)
|
|
162
|
+
return self.type in deletable_types
|
|
@@ -117,3 +117,31 @@ class CustomizationWriter:
|
|
|
117
117
|
target_dir,
|
|
118
118
|
dirs_exist_ok=False,
|
|
119
119
|
)
|
|
120
|
+
|
|
121
|
+
def delete_customization(
|
|
122
|
+
self,
|
|
123
|
+
customization: Customization,
|
|
124
|
+
) -> tuple[bool, str]:
|
|
125
|
+
"""
|
|
126
|
+
Delete customization from disk.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
customization: The customization to delete
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Tuple of (success: bool, message: str)
|
|
133
|
+
"""
|
|
134
|
+
try:
|
|
135
|
+
if customization.type == CustomizationType.SKILL:
|
|
136
|
+
shutil.rmtree(customization.path.parent)
|
|
137
|
+
else:
|
|
138
|
+
customization.path.unlink()
|
|
139
|
+
|
|
140
|
+
return (True, f"Deleted '{customization.name}'")
|
|
141
|
+
|
|
142
|
+
except PermissionError as e:
|
|
143
|
+
return (False, f"Permission denied deleting {e.filename}")
|
|
144
|
+
except FileNotFoundError:
|
|
145
|
+
return (False, f"File not found: {customization.path}")
|
|
146
|
+
except OSError as e:
|
|
147
|
+
return (False, f"Failed to delete '{customization.name}': {e}")
|
|
@@ -26,6 +26,7 @@ class AppFooter(Widget):
|
|
|
26
26
|
|
|
27
27
|
filter_level: reactive[str] = reactive("All")
|
|
28
28
|
search_active: reactive[bool] = reactive(False)
|
|
29
|
+
can_delete: reactive[bool] = reactive(False)
|
|
29
30
|
|
|
30
31
|
def compose(self) -> ComposeResult:
|
|
31
32
|
"""Compose the footer content."""
|
|
@@ -42,9 +43,11 @@ class AppFooter(Widget):
|
|
|
42
43
|
)
|
|
43
44
|
search_key = format_keybinding("/", "Search", active=self.search_active)
|
|
44
45
|
|
|
46
|
+
delete_part = " [bold]d[/] Delete" if self.can_delete else ""
|
|
47
|
+
|
|
45
48
|
return (
|
|
46
49
|
f"[bold]q[/] Quit [bold]?[/] Help [bold]r[/] Refresh "
|
|
47
|
-
f"[bold]e[/] Edit [bold]c[/] Copy "
|
|
50
|
+
f"[bold]e[/] Edit [bold]c[/] Copy{delete_part} "
|
|
48
51
|
f"{all_key} {user_key} {project_key} "
|
|
49
52
|
f"{search_key} │ [bold][$accent]^p[/][/] Palette"
|
|
50
53
|
)
|
|
@@ -69,3 +72,7 @@ class AppFooter(Widget):
|
|
|
69
72
|
def watch_search_active(self, _active: bool) -> None:
|
|
70
73
|
"""React to search active changes."""
|
|
71
74
|
self._update_content()
|
|
75
|
+
|
|
76
|
+
def watch_can_delete(self, _can: bool) -> None:
|
|
77
|
+
"""React to can_delete changes."""
|
|
78
|
+
self._update_content()
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Confirmation widget for delete operations."""
|
|
2
|
+
|
|
3
|
+
from textual.app import ComposeResult
|
|
4
|
+
from textual.binding import Binding
|
|
5
|
+
from textual.message import Message
|
|
6
|
+
from textual.widget import Widget
|
|
7
|
+
from textual.widgets import Static
|
|
8
|
+
|
|
9
|
+
from lazyopencode.models.customization import Customization
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DeleteConfirm(Widget):
|
|
13
|
+
"""Bottom bar for confirming delete operations."""
|
|
14
|
+
|
|
15
|
+
BINDINGS = [
|
|
16
|
+
Binding("y", "confirm", "Yes", show=False),
|
|
17
|
+
Binding("n", "deny", "No", show=False),
|
|
18
|
+
Binding("escape", "cancel", "Cancel", show=False),
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
DEFAULT_CSS = """
|
|
22
|
+
DeleteConfirm {
|
|
23
|
+
dock: bottom;
|
|
24
|
+
height: 4;
|
|
25
|
+
border: solid $error;
|
|
26
|
+
padding: 0 1;
|
|
27
|
+
margin-bottom: 1;
|
|
28
|
+
display: none;
|
|
29
|
+
background: $surface;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
DeleteConfirm.visible {
|
|
33
|
+
display: block;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
DeleteConfirm:focus {
|
|
37
|
+
border: double $error;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
DeleteConfirm #prompt {
|
|
41
|
+
width: 100%;
|
|
42
|
+
text-align: center;
|
|
43
|
+
}
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
can_focus = True
|
|
47
|
+
|
|
48
|
+
class DeleteConfirmed(Message):
|
|
49
|
+
"""Emitted when delete is confirmed."""
|
|
50
|
+
|
|
51
|
+
def __init__(self, customization: Customization) -> None:
|
|
52
|
+
self.customization = customization
|
|
53
|
+
super().__init__()
|
|
54
|
+
|
|
55
|
+
class DeleteCancelled(Message):
|
|
56
|
+
"""Emitted when delete is cancelled."""
|
|
57
|
+
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
name: str | None = None,
|
|
63
|
+
id: str | None = None,
|
|
64
|
+
classes: str | None = None,
|
|
65
|
+
) -> None:
|
|
66
|
+
"""Initialize DeleteConfirm."""
|
|
67
|
+
super().__init__(name=name, id=id, classes=classes)
|
|
68
|
+
self._customization: Customization | None = None
|
|
69
|
+
|
|
70
|
+
def compose(self) -> ComposeResult:
|
|
71
|
+
"""Compose the confirmation bar."""
|
|
72
|
+
yield Static("", id="prompt")
|
|
73
|
+
|
|
74
|
+
def show(self, customization: Customization) -> None:
|
|
75
|
+
"""Show the confirmation bar and focus it."""
|
|
76
|
+
self._customization = customization
|
|
77
|
+
self._update_prompt(customization)
|
|
78
|
+
self.add_class("visible")
|
|
79
|
+
self.focus()
|
|
80
|
+
|
|
81
|
+
def hide(self) -> None:
|
|
82
|
+
"""Hide the confirmation bar."""
|
|
83
|
+
self.remove_class("visible")
|
|
84
|
+
self._customization = None
|
|
85
|
+
|
|
86
|
+
def _update_prompt(self, customization: Customization) -> None:
|
|
87
|
+
"""Update the prompt text."""
|
|
88
|
+
prompt_widget = self.query_one("#prompt", Static)
|
|
89
|
+
error_color = self.app.get_css_variables().get("error", "red")
|
|
90
|
+
prompt_widget.update(
|
|
91
|
+
f'Delete [{error_color}]"{customization.name}"[/] ({customization.type_label})?\n'
|
|
92
|
+
"\\[y] Yes \\[n] No \\[Esc] Cancel"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
def action_confirm(self) -> None:
|
|
96
|
+
"""Confirm the delete."""
|
|
97
|
+
if self._customization:
|
|
98
|
+
customization = self._customization
|
|
99
|
+
self.hide()
|
|
100
|
+
self.post_message(self.DeleteConfirmed(customization))
|
|
101
|
+
|
|
102
|
+
def action_deny(self) -> None:
|
|
103
|
+
"""Deny the delete."""
|
|
104
|
+
self.hide()
|
|
105
|
+
self.post_message(self.DeleteCancelled())
|
|
106
|
+
|
|
107
|
+
def action_cancel(self) -> None:
|
|
108
|
+
"""Cancel the delete."""
|
|
109
|
+
self.hide()
|
|
110
|
+
self.post_message(self.DeleteCancelled())
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def is_visible(self) -> bool:
|
|
114
|
+
"""Check if the confirmation bar is visible."""
|
|
115
|
+
return self.has_class("visible")
|
|
Binary file
|
|
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
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/claude_code/discovery.py
RENAMED
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/claude_code/parsers/__init__.py
RENAMED
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/claude_code/parsers/agent.py
RENAMED
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/claude_code/parsers/command.py
RENAMED
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/claude_code/parsers/skill.py
RENAMED
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/src/lazyopencode/services/claude_code/plugin_loader.py
RENAMED
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/discovery/test_full_discovery.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/mcp/project-opencode.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/project/agent/reviewer.md
RENAMED
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/project/command/project-cmd.md
RENAMED
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/project/docs/guidelines.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/skill/task-tracker/SKILL.md
RENAMED
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/integration/fixtures/skill/task-tracker/reference.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/complex/.opencode/agent/reviewer.md
RENAMED
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/complex/.opencode/command/verify.md
RENAMED
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/complex/.opencode/plugin/metrics.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/complex/.opencode/tool/search.ts
RENAMED
|
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
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/file_references/opencode.json
RENAMED
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/file_references/prompts/agent.txt
RENAMED
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/file_references/templates/cmd.txt
RENAMED
|
File without changes
|
|
File without changes
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/inline_commands/opencode.json
RENAMED
|
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
|
{lazyopencode-0.2.0 → lazyopencode-0.2.1}/tests/spec/scenarios/rich_opencode_json/opencode.json
RENAMED
|
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
|