agr-opentui 0.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.
- agr_opentui-0.2.0/.github/renovate.json5 +19 -0
- agr_opentui-0.2.0/.github/workflows/build-bin.yml +75 -0
- agr_opentui-0.2.0/.github/workflows/ci.yml +48 -0
- agr_opentui-0.2.0/.github/workflows/publish-pypi.yml +76 -0
- agr_opentui-0.2.0/.gitignore +34 -0
- agr_opentui-0.2.0/Makefile +34 -0
- agr_opentui-0.2.0/PKG-INFO +6 -0
- agr_opentui-0.2.0/README.md +144 -0
- agr_opentui-0.2.0/agr_opentui/__init__.py +1 -0
- agr_opentui-0.2.0/agr_opentui/bridge.py +157 -0
- agr_opentui-0.2.0/bun.lock +206 -0
- agr_opentui-0.2.0/bunfig.toml +2 -0
- agr_opentui-0.2.0/package.json +19 -0
- agr_opentui-0.2.0/pyproject.toml +12 -0
- agr_opentui-0.2.0/skills.json +85 -0
- agr_opentui-0.2.0/src/app.ts +914 -0
- agr_opentui-0.2.0/src/app_logic.ts +371 -0
- agr_opentui-0.2.0/src/commands.ts +50 -0
- agr_opentui-0.2.0/src/deps.ts +33 -0
- agr_opentui-0.2.0/src/main.ts +1 -0
- agr_opentui-0.2.0/src/runtime/doctor.ts +56 -0
- agr_opentui-0.2.0/src/runtime/handlers.ts +261 -0
- agr_opentui-0.2.0/src/runtime/modal_input_handler.ts +218 -0
- agr_opentui-0.2.0/src/services/add_flow.ts +28 -0
- agr_opentui-0.2.0/src/services/agr.ts +85 -0
- agr_opentui-0.2.0/src/services/agr_actions.ts +262 -0
- agr_opentui-0.2.0/src/services/clipboard.ts +38 -0
- agr_opentui-0.2.0/src/services/data.ts +81 -0
- agr_opentui-0.2.0/src/services/discover_filter.ts +82 -0
- agr_opentui-0.2.0/src/services/discover_labels.ts +158 -0
- agr_opentui-0.2.0/src/services/feedback.ts +62 -0
- agr_opentui-0.2.0/src/services/handle_match.ts +32 -0
- agr_opentui-0.2.0/src/services/input_mode.ts +35 -0
- agr_opentui-0.2.0/src/services/navigation.ts +75 -0
- agr_opentui-0.2.0/src/services/preview.ts +19 -0
- agr_opentui-0.2.0/src/services/render_actions.ts +11 -0
- agr_opentui-0.2.0/src/services/render_all.ts +29 -0
- agr_opentui-0.2.0/src/services/render_details.ts +42 -0
- agr_opentui-0.2.0/src/services/render_footer.ts +34 -0
- agr_opentui-0.2.0/src/services/render_help.ts +20 -0
- agr_opentui-0.2.0/src/services/render_list.ts +37 -0
- agr_opentui-0.2.0/src/services/render_missing_config.ts +14 -0
- agr_opentui-0.2.0/src/services/render_preview.ts +23 -0
- agr_opentui-0.2.0/src/services/render_run_modal.ts +35 -0
- agr_opentui-0.2.0/src/services/render_run_options.ts +30 -0
- agr_opentui-0.2.0/src/services/render_tabs.ts +23 -0
- agr_opentui-0.2.0/src/services/render_update_confirm.ts +26 -0
- agr_opentui-0.2.0/src/services/render_verify.ts +39 -0
- agr_opentui-0.2.0/src/services/runtime_ops.ts +59 -0
- agr_opentui-0.2.0/src/services/selection.ts +76 -0
- agr_opentui-0.2.0/src/services/skills_file.ts +90 -0
- agr_opentui-0.2.0/src/services/skills_source.ts +353 -0
- agr_opentui-0.2.0/src/services/ui_feedback.ts +41 -0
- agr_opentui-0.2.0/src/services/update.ts +163 -0
- agr_opentui-0.2.0/src/services/verify.ts +125 -0
- agr_opentui-0.2.0/src/services/verify_coordinator.ts +129 -0
- agr_opentui-0.2.0/src/services/visible_items.ts +24 -0
- agr_opentui-0.2.0/src/state.ts +134 -0
- agr_opentui-0.2.0/src/ui/controller.ts +291 -0
- agr_opentui-0.2.0/src/ui/layout.ts +504 -0
- agr_opentui-0.2.0/src/ui/render.ts +24 -0
- agr_opentui-0.2.0/src/ui/rows_render.ts +40 -0
- agr_opentui-0.2.0/src/ui.ts +17 -0
- agr_opentui-0.2.0/test/add_flow_service.test.ts +83 -0
- agr_opentui-0.2.0/test/agr_actions.test.ts +123 -0
- agr_opentui-0.2.0/test/agr_service.test.ts +151 -0
- agr_opentui-0.2.0/test/app_logic.test.ts +328 -0
- agr_opentui-0.2.0/test/clipboard_service.test.ts +53 -0
- agr_opentui-0.2.0/test/commands_service.test.ts +36 -0
- agr_opentui-0.2.0/test/data_service.test.ts +91 -0
- agr_opentui-0.2.0/test/discover_filter_service.test.ts +64 -0
- agr_opentui-0.2.0/test/discover_labels.test.ts +106 -0
- agr_opentui-0.2.0/test/feedback_service.test.ts +113 -0
- agr_opentui-0.2.0/test/handle_match_service.test.ts +39 -0
- agr_opentui-0.2.0/test/input_mode_service.test.ts +26 -0
- agr_opentui-0.2.0/test/navigation_service.test.ts +132 -0
- agr_opentui-0.2.0/test/preview_service.test.ts +43 -0
- agr_opentui-0.2.0/test/render_actions_service.test.ts +22 -0
- agr_opentui-0.2.0/test/render_all_service.test.ts +40 -0
- agr_opentui-0.2.0/test/render_details_service.test.ts +74 -0
- agr_opentui-0.2.0/test/render_footer_service.test.ts +51 -0
- agr_opentui-0.2.0/test/render_help_service.test.ts +45 -0
- agr_opentui-0.2.0/test/render_list_service.test.ts +77 -0
- agr_opentui-0.2.0/test/render_missing_config_service.test.ts +36 -0
- agr_opentui-0.2.0/test/render_preview_service.test.ts +61 -0
- agr_opentui-0.2.0/test/render_run_modal_service.test.ts +48 -0
- agr_opentui-0.2.0/test/render_run_options_service.test.ts +71 -0
- agr_opentui-0.2.0/test/render_tabs_service.test.ts +27 -0
- agr_opentui-0.2.0/test/render_update_confirm_service.test.ts +42 -0
- agr_opentui-0.2.0/test/render_verify_service.test.ts +47 -0
- agr_opentui-0.2.0/test/rows_render.test.ts +44 -0
- agr_opentui-0.2.0/test/runtime_doctor.test.ts +75 -0
- agr_opentui-0.2.0/test/runtime_handlers.test.ts +226 -0
- agr_opentui-0.2.0/test/runtime_ops_service.test.ts +68 -0
- agr_opentui-0.2.0/test/selection_service.test.ts +47 -0
- agr_opentui-0.2.0/test/skills_file_service.test.ts +50 -0
- agr_opentui-0.2.0/test/skills_source.test.ts +71 -0
- agr_opentui-0.2.0/test/ui_controller.test.ts +94 -0
- agr_opentui-0.2.0/test/ui_feedback_adapter.test.ts +48 -0
- agr_opentui-0.2.0/test/update_service.test.ts +108 -0
- agr_opentui-0.2.0/test/verify_coordinator_service.test.ts +52 -0
- agr_opentui-0.2.0/test/verify_service.test.ts +65 -0
- agr_opentui-0.2.0/test/visible_items_service.test.ts +42 -0
- agr_opentui-0.2.0/tsconfig.json +12 -0
- agr_opentui-0.2.0/uv.lock +216 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
$schema: "https://docs.renovatebot.com/renovate-schema.json",
|
|
3
|
+
extends: [
|
|
4
|
+
"config:recommended",
|
|
5
|
+
":dependencyDashboard",
|
|
6
|
+
":automergeMinor"
|
|
7
|
+
],
|
|
8
|
+
timezone: "UTC",
|
|
9
|
+
schedule: ["before 6am on monday"],
|
|
10
|
+
labels: ["dependencies"],
|
|
11
|
+
rangeStrategy: "bump",
|
|
12
|
+
packageRules: [
|
|
13
|
+
{
|
|
14
|
+
matchManagers: ["npm"],
|
|
15
|
+
matchUpdateTypes: ["major"],
|
|
16
|
+
automerge: false
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
name: build-bin
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
release:
|
|
6
|
+
types: [published]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build-linux:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
env:
|
|
12
|
+
BASE_NAME: agr-opentui
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout
|
|
15
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
16
|
+
|
|
17
|
+
- name: Setup Bun
|
|
18
|
+
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2
|
|
19
|
+
with:
|
|
20
|
+
bun-version: '1.3.8'
|
|
21
|
+
|
|
22
|
+
- name: Cache Bun dependencies
|
|
23
|
+
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
|
24
|
+
with:
|
|
25
|
+
path: |
|
|
26
|
+
~/.bun/install/cache
|
|
27
|
+
node_modules
|
|
28
|
+
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
|
|
29
|
+
restore-keys: |
|
|
30
|
+
${{ runner.os }}-bun-
|
|
31
|
+
|
|
32
|
+
- name: Install deps
|
|
33
|
+
run: bun install
|
|
34
|
+
|
|
35
|
+
- name: Build binary
|
|
36
|
+
run: make build
|
|
37
|
+
|
|
38
|
+
- name: Compute artifact name
|
|
39
|
+
id: name
|
|
40
|
+
shell: bash
|
|
41
|
+
run: |
|
|
42
|
+
sanitize() {
|
|
43
|
+
local value="$1"
|
|
44
|
+
value="${value//\//-}"
|
|
45
|
+
value="${value//_/-}"
|
|
46
|
+
echo "$value"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
ref="${GITHUB_REF#refs/heads/}"
|
|
50
|
+
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
|
|
51
|
+
tag="${GITHUB_REF#refs/tags/}"
|
|
52
|
+
safe_tag="$(sanitize "$tag")"
|
|
53
|
+
name="${BASE_NAME}-tag-${safe_tag}"
|
|
54
|
+
elif [[ "${ref}" == "master" ]]; then
|
|
55
|
+
name="${BASE_NAME}"
|
|
56
|
+
else
|
|
57
|
+
safe_ref="$(sanitize "$ref")"
|
|
58
|
+
name="${BASE_NAME}-branch-${safe_ref}"
|
|
59
|
+
fi
|
|
60
|
+
echo "name=${name}" >> "$GITHUB_OUTPUT"
|
|
61
|
+
|
|
62
|
+
- name: Rename binary
|
|
63
|
+
run: mv bin/agr-tui "bin/${{ steps.name.outputs.name }}"
|
|
64
|
+
|
|
65
|
+
- name: Upload artifact
|
|
66
|
+
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
|
67
|
+
with:
|
|
68
|
+
name: ${{ steps.name.outputs.name }}-linux
|
|
69
|
+
path: bin/${{ steps.name.outputs.name }}
|
|
70
|
+
|
|
71
|
+
- name: Upload to release
|
|
72
|
+
if: github.event_name == 'release'
|
|
73
|
+
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
|
74
|
+
with:
|
|
75
|
+
files: bin/${{ steps.name.outputs.name }}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
lint-workflows:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- name: Checkout
|
|
12
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
13
|
+
|
|
14
|
+
- name: Lint GitHub workflows
|
|
15
|
+
uses: rhysd/actionlint@0933c147c9d6587653d45fdcb4c497c57a65f9af # v1.7.10
|
|
16
|
+
|
|
17
|
+
test:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout
|
|
21
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
22
|
+
|
|
23
|
+
- name: Setup Bun
|
|
24
|
+
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2
|
|
25
|
+
with:
|
|
26
|
+
bun-version: "1.3.8"
|
|
27
|
+
|
|
28
|
+
- name: Cache Bun dependencies
|
|
29
|
+
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
|
30
|
+
with:
|
|
31
|
+
path: |
|
|
32
|
+
~/.bun/install/cache
|
|
33
|
+
node_modules
|
|
34
|
+
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
|
|
35
|
+
restore-keys: |
|
|
36
|
+
${{ runner.os }}-bun-
|
|
37
|
+
|
|
38
|
+
- name: Install dependencies
|
|
39
|
+
run: bun install
|
|
40
|
+
|
|
41
|
+
- name: Run checks
|
|
42
|
+
run: bun run check
|
|
43
|
+
|
|
44
|
+
- name: Build binary
|
|
45
|
+
run: bun run build
|
|
46
|
+
|
|
47
|
+
- name: Run strict unused checks
|
|
48
|
+
run: bunx tsc --noEmit --noUnusedLocals --noUnusedParameters
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
name: publish-pypi
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
target:
|
|
9
|
+
description: "Where to publish the package"
|
|
10
|
+
required: true
|
|
11
|
+
type: choice
|
|
12
|
+
options:
|
|
13
|
+
- pypi
|
|
14
|
+
- testpypi
|
|
15
|
+
default: pypi
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
build:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- name: Checkout
|
|
22
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
23
|
+
|
|
24
|
+
- name: Setup Python
|
|
25
|
+
uses: actions/setup-python@v6
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.14.3"
|
|
28
|
+
|
|
29
|
+
- name: Build package
|
|
30
|
+
run: |
|
|
31
|
+
python -m pip install --upgrade pip build
|
|
32
|
+
python -m build
|
|
33
|
+
|
|
34
|
+
- name: Upload dist artifacts
|
|
35
|
+
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
|
36
|
+
with:
|
|
37
|
+
name: python-dist
|
|
38
|
+
path: dist/
|
|
39
|
+
|
|
40
|
+
publish-pypi:
|
|
41
|
+
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.target == 'pypi')
|
|
42
|
+
needs: build
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
permissions:
|
|
45
|
+
id-token: write
|
|
46
|
+
environment:
|
|
47
|
+
name: pypi
|
|
48
|
+
steps:
|
|
49
|
+
- name: Download dist artifacts
|
|
50
|
+
uses: actions/download-artifact@v7
|
|
51
|
+
with:
|
|
52
|
+
name: python-dist
|
|
53
|
+
path: dist/
|
|
54
|
+
|
|
55
|
+
- name: Publish to PyPI
|
|
56
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
57
|
+
|
|
58
|
+
publish-testpypi:
|
|
59
|
+
if: github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi'
|
|
60
|
+
needs: build
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
permissions:
|
|
63
|
+
id-token: write
|
|
64
|
+
environment:
|
|
65
|
+
name: testpypi
|
|
66
|
+
steps:
|
|
67
|
+
- name: Download dist artifacts
|
|
68
|
+
uses: actions/download-artifact@v7
|
|
69
|
+
with:
|
|
70
|
+
name: python-dist
|
|
71
|
+
path: dist/
|
|
72
|
+
|
|
73
|
+
- name: Publish to TestPyPI
|
|
74
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
75
|
+
with:
|
|
76
|
+
repository-url: https://test.pypi.org/legacy/
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
node_modules/
|
|
2
|
+
dist/
|
|
3
|
+
bin/
|
|
4
|
+
.tmp/
|
|
5
|
+
.cache/
|
|
6
|
+
*.log
|
|
7
|
+
.env
|
|
8
|
+
.env.*
|
|
9
|
+
.DS_Store
|
|
10
|
+
__pycache__/
|
|
11
|
+
*.py[cod]
|
|
12
|
+
.python-version
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
.mypy_cache/
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.coverage
|
|
19
|
+
coverage/
|
|
20
|
+
htmlcov/
|
|
21
|
+
.idea/
|
|
22
|
+
.vscode/
|
|
23
|
+
*.tsbuildinfo
|
|
24
|
+
*.pid
|
|
25
|
+
*.pid.lock
|
|
26
|
+
.bun/
|
|
27
|
+
*.tmp
|
|
28
|
+
*.swp
|
|
29
|
+
*.swo
|
|
30
|
+
/tmp/
|
|
31
|
+
.uv/
|
|
32
|
+
.claude/
|
|
33
|
+
.agents/
|
|
34
|
+
agr.toml
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
BUN ?= bun
|
|
2
|
+
PYTHON ?= python3
|
|
3
|
+
ENTRY ?= src/main.ts
|
|
4
|
+
OUT_DIR ?= bin
|
|
5
|
+
OUT ?= $(OUT_DIR)/agr-tui
|
|
6
|
+
DIST_DIR ?= dist
|
|
7
|
+
|
|
8
|
+
.PHONY: build clean
|
|
9
|
+
.PHONY: test check
|
|
10
|
+
.PHONY: py-build py-publish py-publish-test
|
|
11
|
+
|
|
12
|
+
build:
|
|
13
|
+
@mkdir -p $(OUT_DIR)
|
|
14
|
+
$(BUN) build $(ENTRY) --compile --outfile $(OUT)
|
|
15
|
+
|
|
16
|
+
test:
|
|
17
|
+
$(BUN) test
|
|
18
|
+
|
|
19
|
+
check:
|
|
20
|
+
$(BUN) run typecheck
|
|
21
|
+
$(BUN) test
|
|
22
|
+
|
|
23
|
+
py-build:
|
|
24
|
+
rm -rf $(DIST_DIR)
|
|
25
|
+
$(PYTHON) -m build
|
|
26
|
+
|
|
27
|
+
py-publish: py-build
|
|
28
|
+
$(PYTHON) -m twine upload $(DIST_DIR)/*
|
|
29
|
+
|
|
30
|
+
py-publish-test: py-build
|
|
31
|
+
$(PYTHON) -m twine upload --repository testpypi $(DIST_DIR)/*
|
|
32
|
+
|
|
33
|
+
clean:
|
|
34
|
+
rm -rf $(OUT_DIR)
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# OpenTUI for agr
|
|
2
|
+
|
|
3
|
+
OpenTUI front-end for managing `agr` skills: browse `agr.toml`, install/remove skills, preview `SKILL.md`, and run skills via `agrx`. Skill discovery is based on `https://github.com/kasperjunge/agent-resources`.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
Tabs:
|
|
8
|
+
- `Skills`: shows dependencies from `agr.toml` and install state.
|
|
9
|
+
- `Discover`: shows skills from `skills.json` (optional).
|
|
10
|
+
|
|
11
|
+
Common keys:
|
|
12
|
+
- `Tab` / `Shift+Tab`: switch tabs
|
|
13
|
+
- Arrow keys: move selection
|
|
14
|
+
- `space`: toggle select
|
|
15
|
+
- `H`: help
|
|
16
|
+
- `q`: quit
|
|
17
|
+
|
|
18
|
+
Skills tab keys:
|
|
19
|
+
- `a`: add skill (handle or `owner/repo/path`)
|
|
20
|
+
- `i`: install selected (bulk)
|
|
21
|
+
- `r`: remove selected (bulk)
|
|
22
|
+
- `v`: preview `SKILL.md`
|
|
23
|
+
- `g`: run with default options (`agrx`)
|
|
24
|
+
- `G`: open run options (tool, interactive, prompt, extra args)
|
|
25
|
+
- `u`: check discover list updates
|
|
26
|
+
- `U`: apply update (no confirm)
|
|
27
|
+
- `s`: apply update (confirm)
|
|
28
|
+
- `S`: apply update + sync (no confirm)
|
|
29
|
+
- `c`: reload config
|
|
30
|
+
|
|
31
|
+
Discover tab keys:
|
|
32
|
+
- `i`: add selected
|
|
33
|
+
- `y`: copy handle/repo to clipboard
|
|
34
|
+
- `a`: add skill
|
|
35
|
+
- `c`: reload config
|
|
36
|
+
|
|
37
|
+
Run options keys (when the modal is open):
|
|
38
|
+
- `t`: cycle tool
|
|
39
|
+
- `u`: toggle `--interactive`
|
|
40
|
+
- `p`: edit `--prompt`
|
|
41
|
+
- `e`: edit extra args
|
|
42
|
+
- `Enter`: run
|
|
43
|
+
- `Esc`: close
|
|
44
|
+
|
|
45
|
+
## Quick Cheat Sheet
|
|
46
|
+
|
|
47
|
+
Primary flow:
|
|
48
|
+
- `Tab` to `Skills`
|
|
49
|
+
- Arrow keys to select
|
|
50
|
+
- `g` to run, `G` for run options
|
|
51
|
+
|
|
52
|
+
Bulk actions:
|
|
53
|
+
- `space` to multi-select
|
|
54
|
+
- `i` install or `r` remove (Skills tab)
|
|
55
|
+
|
|
56
|
+
Discover flow:
|
|
57
|
+
- `Tab` to `Discover`
|
|
58
|
+
- Arrow keys to select
|
|
59
|
+
- `i` to add, `y` to copy handle
|
|
60
|
+
|
|
61
|
+
## Requirements
|
|
62
|
+
|
|
63
|
+
- Bun 1.3.8+
|
|
64
|
+
- Zig (required by OpenTUI build tooling)
|
|
65
|
+
- Python 3.10+
|
|
66
|
+
- `uv`
|
|
67
|
+
- `agr` + `agrx` on your `PATH`
|
|
68
|
+
|
|
69
|
+
## Install
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
bun install
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Run
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
bun run src/main.ts
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Run it from the repo you want to manage (the current working directory is the target repo).
|
|
82
|
+
`agr.toml` is expected in that target repo for `agr add/remove/sync` operations.
|
|
83
|
+
|
|
84
|
+
### Run From Target Repo
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
cd /path/to/your/project
|
|
88
|
+
agr-tui
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`agr-opentui` itself does not need to contain your target repo's `agr.toml`.
|
|
92
|
+
|
|
93
|
+
## Build
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
bun run build
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
This creates `bin/agr-tui`.
|
|
100
|
+
|
|
101
|
+
## Publish to PyPI
|
|
102
|
+
|
|
103
|
+
One-time setup:
|
|
104
|
+
- Create a `pypi` environment in this GitHub repo.
|
|
105
|
+
- In your PyPI project settings, add this repo/workflow as a Trusted Publisher for `.github/workflows/publish-pypi.yml`.
|
|
106
|
+
- Optional: add `testpypi` environment and TestPyPI Trusted Publisher too.
|
|
107
|
+
|
|
108
|
+
Publish via GitHub Actions:
|
|
109
|
+
- Release publish: creating a GitHub release triggers publish to PyPI.
|
|
110
|
+
- Manual: run `publish-pypi` workflow and choose `pypi` or `testpypi`.
|
|
111
|
+
|
|
112
|
+
Publish from local machine:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
python -m pip install --upgrade build twine
|
|
116
|
+
make py-publish # Upload to PyPI
|
|
117
|
+
make py-publish-test # Upload to TestPyPI
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Discover List (`skills.json`)
|
|
121
|
+
|
|
122
|
+
If `skills.json` exists, the `Discover` tab will list its entries. It supports:
|
|
123
|
+
|
|
124
|
+
- An array of strings or objects (`{ "label": "...", "handle": "...", "repo": "owner/repo" }`).
|
|
125
|
+
- An object with `source` metadata and `skills` array (see `skills.json` in this repo).
|
|
126
|
+
|
|
127
|
+
When a `source` is configured, the app checks the remote list periodically (about every 6 hours) and can update `skills.json` using the `u`/`U`/`s`/`S` controls.
|
|
128
|
+
|
|
129
|
+
## Troubleshooting
|
|
130
|
+
|
|
131
|
+
- `Missing agr.toml`: run the app from a repo that has `agr.toml`, or create one.
|
|
132
|
+
- `uv not found`: install `uv` and ensure it’s on `PATH`.
|
|
133
|
+
- `agr/agrx not found`: install `agr` and ensure it’s on `PATH`.
|
|
134
|
+
- `python not found`: install Python 3.
|
|
135
|
+
- `skills.json not found` or parse errors: fix the file format (array or `{ "source": ..., "skills": [...] }`).
|
|
136
|
+
- Discover list not updating: check the `source` URL/repo/branch/path and network access.
|
|
137
|
+
- `SKILL.md` preview says “not found”: the skill may not ship a `SKILL.md` or the path is nonstandard.
|
|
138
|
+
- Copy to clipboard doesn’t work: install `pbcopy` (macOS), `wl-copy` (Wayland), or `xclip` (X11).
|
|
139
|
+
|
|
140
|
+
## Notes
|
|
141
|
+
|
|
142
|
+
- All actions run through `uv` (`uv run agr`, `uv run agrx`, and `uv run python -m agr_opentui.bridge`).
|
|
143
|
+
- If `agr.toml` is missing in the current directory, install/remove/sync commands are blocked and a warning is shown.
|
|
144
|
+
- Logs are written to `/tmp/agr-opentui.log`.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""OpenTUI bridge package for agr."""
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""JSON bridge for the OpenTUI interface."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from agr.config import AgrConfig, Dependency, find_config, find_repo_root
|
|
11
|
+
from agr.fetcher import get_installed_skills
|
|
12
|
+
from agr.handle import ParsedHandle, parse_handle
|
|
13
|
+
from agr.skill import SKILL_MARKER
|
|
14
|
+
from agr.tool import get_tool
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class BridgeDependency:
|
|
19
|
+
identifier: str
|
|
20
|
+
handle: str | None
|
|
21
|
+
path: str | None
|
|
22
|
+
is_local: bool
|
|
23
|
+
display: str
|
|
24
|
+
candidates: list[str]
|
|
25
|
+
candidates_by_tool: dict[str, list[str]]
|
|
26
|
+
installed: bool
|
|
27
|
+
skill_md_path: str | None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _parse_dependency(dep: Dependency) -> ParsedHandle:
|
|
31
|
+
if dep.handle:
|
|
32
|
+
return parse_handle(dep.handle, prefer_local=False)
|
|
33
|
+
if dep.path:
|
|
34
|
+
path = Path(dep.path)
|
|
35
|
+
return ParsedHandle(is_local=True, name=path.name, local_path=path)
|
|
36
|
+
raise ValueError("Dependency missing handle/path")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _candidates_for_install(handle: ParsedHandle, tool_name: str) -> list[str]:
|
|
40
|
+
tool = get_tool(tool_name)
|
|
41
|
+
candidates = [
|
|
42
|
+
handle.to_skill_path(tool).as_posix(),
|
|
43
|
+
handle.name,
|
|
44
|
+
handle.to_installed_name(),
|
|
45
|
+
]
|
|
46
|
+
if handle.is_local:
|
|
47
|
+
candidates.append(f"local/{handle.name}")
|
|
48
|
+
return list(dict.fromkeys(candidates))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _installed_by_tool(repo_root: Path, tools: list[str]) -> dict[str, list[str]]:
|
|
52
|
+
installed: dict[str, list[str]] = {}
|
|
53
|
+
for tool in tools:
|
|
54
|
+
try:
|
|
55
|
+
installed[tool] = get_installed_skills(repo_root, get_tool(tool))
|
|
56
|
+
except Exception:
|
|
57
|
+
installed[tool] = []
|
|
58
|
+
return installed
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _resolve_skill_md_path(
|
|
62
|
+
dep: Dependency,
|
|
63
|
+
*,
|
|
64
|
+
handle: ParsedHandle,
|
|
65
|
+
repo_root: Path | None,
|
|
66
|
+
default_tool: str | None,
|
|
67
|
+
) -> str | None:
|
|
68
|
+
if repo_root is None:
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
if dep.path:
|
|
72
|
+
path = Path(dep.path)
|
|
73
|
+
if not path.is_absolute():
|
|
74
|
+
path = repo_root / path
|
|
75
|
+
skill_md = path / SKILL_MARKER
|
|
76
|
+
return str(skill_md) if skill_md.exists() else None
|
|
77
|
+
|
|
78
|
+
if not default_tool:
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
tool = get_tool(default_tool)
|
|
82
|
+
skills_dir = tool.get_skills_dir(repo_root)
|
|
83
|
+
candidates = _candidates_for_install(handle, default_tool)
|
|
84
|
+
for candidate in candidates:
|
|
85
|
+
skill_md = skills_dir / candidate / SKILL_MARKER
|
|
86
|
+
if skill_md.exists():
|
|
87
|
+
return str(skill_md)
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def build_payload() -> dict[str, object]:
|
|
92
|
+
repo_root = find_repo_root()
|
|
93
|
+
config_path = find_config()
|
|
94
|
+
config = AgrConfig.load(config_path) if config_path else AgrConfig()
|
|
95
|
+
|
|
96
|
+
tool_names = list(config.tools)
|
|
97
|
+
default_tool = config.default_tool or (tool_names[0] if tool_names else None)
|
|
98
|
+
|
|
99
|
+
installed = (
|
|
100
|
+
_installed_by_tool(repo_root, tool_names) if repo_root and tool_names else {}
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
deps_payload: list[dict[str, object]] = []
|
|
104
|
+
for dep in config.dependencies:
|
|
105
|
+
handle = _parse_dependency(dep)
|
|
106
|
+
candidates = (
|
|
107
|
+
_candidates_for_install(handle, default_tool)
|
|
108
|
+
if default_tool
|
|
109
|
+
else []
|
|
110
|
+
)
|
|
111
|
+
candidates_by_tool = {
|
|
112
|
+
tool_name: _candidates_for_install(handle, tool_name)
|
|
113
|
+
for tool_name in tool_names
|
|
114
|
+
}
|
|
115
|
+
installed_names = installed.get(default_tool or "", [])
|
|
116
|
+
is_installed = any(name in installed_names for name in candidates)
|
|
117
|
+
skill_md_path = _resolve_skill_md_path(
|
|
118
|
+
dep,
|
|
119
|
+
handle=handle,
|
|
120
|
+
repo_root=repo_root,
|
|
121
|
+
default_tool=default_tool,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
deps_payload.append(
|
|
125
|
+
BridgeDependency(
|
|
126
|
+
identifier=dep.identifier,
|
|
127
|
+
handle=dep.handle,
|
|
128
|
+
path=dep.path,
|
|
129
|
+
is_local=dep.is_local,
|
|
130
|
+
display=dep.identifier,
|
|
131
|
+
candidates=candidates,
|
|
132
|
+
candidates_by_tool=candidates_by_tool,
|
|
133
|
+
installed=is_installed,
|
|
134
|
+
skill_md_path=skill_md_path,
|
|
135
|
+
).__dict__
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
"repo_root": str(repo_root) if repo_root else None,
|
|
140
|
+
"config_path": str(config_path) if config_path else None,
|
|
141
|
+
"tools": tool_names,
|
|
142
|
+
"default_tool": default_tool,
|
|
143
|
+
"dependencies": deps_payload,
|
|
144
|
+
"installed": installed,
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def main() -> None:
|
|
149
|
+
parser = argparse.ArgumentParser(description="OpenTUI data bridge")
|
|
150
|
+
parser.parse_args()
|
|
151
|
+
|
|
152
|
+
payload = build_payload()
|
|
153
|
+
print(json.dumps(payload))
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
if __name__ == "__main__":
|
|
157
|
+
main()
|