diff --git a/.github/workflows/build-tests.yml b/.github/workflows/build-tests.yml new file mode 100644 index 0000000..b77ed02 --- /dev/null +++ b/.github/workflows/build-tests.yml @@ -0,0 +1,16 @@ +name: Build Tests + +on: + pull_request: + branches: [dev, master, main] + workflow_dispatch: + +jobs: + build: + uses: OpenVoiceOS/gh-automations/.github/workflows/build-tests.yml@dev + secrets: inherit + with: + python_versions: '["3.10", "3.11", "3.12", "3.13", "3.14"]' + system_deps: 'swig libfann-dev' + install_extras: 'test' + test_path: 'test/unittests' diff --git a/.github/workflows/build_tests.yml b/.github/workflows/build_tests.yml deleted file mode 100644 index 01a5205..0000000 --- a/.github/workflows/build_tests.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Run Build Tests -on: - push: - branches: - - master - pull_request: - branches: - - dev - workflow_dispatch: - -jobs: - build_tests: - strategy: - max-parallel: 2 - matrix: - python-version: [ "3.10", "3.11" ] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Install Build Tools - run: | - python -m pip install build wheel - - name: Install System Dependencies - run: | - sudo apt-get update - sudo apt install python3-dev swig libssl-dev - - name: Build Source Packages - run: | - python setup.py sdist - - name: Build Distribution Packages - run: | - python setup.py bdist_wheel - - name: Install skill - run: | - pip install . \ No newline at end of file diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..e58231f --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,18 @@ +name: Code Coverage + +on: + pull_request: + branches: [dev] + workflow_dispatch: + +jobs: + coverage: + uses: OpenVoiceOS/gh-automations/.github/workflows/coverage.yml@dev + secrets: inherit + with: + python_version: '3.11' + coverage_source: 'ovos_skill_cmd' + test_path: 'test/unittests/' + system_deps: 'swig libfann-dev' + test_extras: 'test' + min_coverage: 0 diff --git a/.github/workflows/license_check.yml b/.github/workflows/license_check.yml new file mode 100644 index 0000000..8757eee --- /dev/null +++ b/.github/workflows/license_check.yml @@ -0,0 +1,11 @@ +name: License Check + +on: + pull_request: + branches: [dev] + workflow_dispatch: + +jobs: + license_check: + uses: OpenVoiceOS/gh-automations/.github/workflows/license-check.yml@dev + secrets: inherit diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..9a6b7a5 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,14 @@ +name: Lint + +on: + pull_request: + branches: [dev, master, main] + workflow_dispatch: + +jobs: + lint: + uses: OpenVoiceOS/gh-automations/.github/workflows/lint.yml@dev + secrets: inherit + with: + ruff: true + pre_commit: false # set true if .pre-commit-config.yaml exists diff --git a/.github/workflows/locale_check.yml b/.github/workflows/locale_check.yml new file mode 100644 index 0000000..26471ef --- /dev/null +++ b/.github/workflows/locale_check.yml @@ -0,0 +1,13 @@ +name: Locale Build Check + +on: + pull_request: + branches: [dev] + workflow_dispatch: + +jobs: + locale_check: + uses: OpenVoiceOS/gh-automations/.github/workflows/locale-check.yml@dev + secrets: inherit + with: + pr_comment: true diff --git a/.github/workflows/ovoscope.yml b/.github/workflows/ovoscope.yml new file mode 100644 index 0000000..0f48332 --- /dev/null +++ b/.github/workflows/ovoscope.yml @@ -0,0 +1,15 @@ +name: Skill End-to-End Tests (ovoscope) + +on: + pull_request: + branches: [dev, master, main] + workflow_dispatch: + +jobs: + ovoscope: + uses: OpenVoiceOS/gh-automations/.github/workflows/ovoscope.yml@dev + secrets: inherit + with: + python_version: '3.11' + install_extras: 'test' + test_path: 'test/end2end/' diff --git a/.github/workflows/pip_audit.yml b/.github/workflows/pip_audit.yml new file mode 100644 index 0000000..bb3ca4d --- /dev/null +++ b/.github/workflows/pip_audit.yml @@ -0,0 +1,11 @@ +name: PIP Audit + +on: + pull_request: + branches: [dev] + workflow_dispatch: + +jobs: + pip_audit: + uses: OpenVoiceOS/gh-automations/.github/workflows/pip-audit.yml@dev + secrets: inherit diff --git a/.github/workflows/publish_stable.yml b/.github/workflows/publish_stable.yml index 3abbee2..da4b385 100644 --- a/.github/workflows/publish_stable.yml +++ b/.github/workflows/publish_stable.yml @@ -1,72 +1,23 @@ -name: Stable Release +name: Publish Stable Release + on: - push: - branches: [master] workflow_dispatch: + push: + branches: [master, main] + +permissions: + contents: write # required for version bump commit and release tag jobs: publish_stable: - uses: TigreGotico/gh-automations/.github/workflows/publish-stable.yml@master - secrets: inherit + if: github.actor != 'github-actions[bot]' + uses: OpenVoiceOS/gh-automations/.github/workflows/publish-stable.yml@dev + secrets: + PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} + MATRIX_TOKEN: ${{ secrets.MATRIX_TOKEN }} with: - branch: 'master' - version_file: 'version.py' - setup_py: 'setup.py' + version_file: 'ovos_skill_cmd/version.py' + publish_pypi: true publish_release: true - - publish_pypi: - needs: publish_stable - if: success() # Ensure this job only runs if the previous job succeeds - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: master - fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository. - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - name: Install Build Tools - run: | - python -m pip install build wheel - - name: version - run: echo "::set-output name=version::$(python setup.py --version)" - id: version - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token - with: - tag_name: V${{ steps.version.outputs.version }} - release_name: Release ${{ steps.version.outputs.version }} - body: | - Changes in this Release - ${{ steps.changelog.outputs.changelog }} - draft: false - prerelease: true - commitish: dev - - name: Build Distribution Packages - run: | - python setup.py sdist bdist_wheel - - name: Publish to Test PyPI - uses: pypa/gh-action-pypi-publish@master - with: - password: ${{secrets.PYPI_TOKEN}} - - - sync_dev: - needs: publish_stable - if: success() # Ensure this job only runs if the previous job succeeds - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository. - ref: master - - name: Push master -> dev - uses: ad-m/github-push-action@master - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - branch: dev + sync_dev: true + notify_matrix: true diff --git a/.github/workflows/release-preview.yml b/.github/workflows/release-preview.yml new file mode 100644 index 0000000..ccc27ed --- /dev/null +++ b/.github/workflows/release-preview.yml @@ -0,0 +1,14 @@ +name: Release Preview + +on: + pull_request: + branches: [dev] + workflow_dispatch: + +jobs: + release_preview: + uses: OpenVoiceOS/gh-automations/.github/workflows/release-preview.yml@dev + secrets: inherit + with: + package_name: 'ovos_skill_cmd' + version_file: 'ovos_skill_cmd/version.py' diff --git a/.github/workflows/release_workflow.yml b/.github/workflows/release_workflow.yml index 27e824e..a841f5f 100644 --- a/.github/workflows/release_workflow.yml +++ b/.github/workflows/release_workflow.yml @@ -1,133 +1,28 @@ name: Release Alpha and Propose Stable on: + workflow_dispatch: pull_request: types: [closed] branches: [dev] - workflow_dispatch: - -jobs: - - translations: - runs-on: ubuntu-latest - steps: - - name: Check out repository - uses: actions/checkout@v2 - with: - ref: dev - fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository. - - name: Setup Python - uses: actions/setup-python@v1 - with: - python-version: "3.11" - - name: Sync translations by gitlocalize-app[bot] - run: | - python scripts/sync_translations.py - - - name: Commit to dev - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: Update translations - branch: dev +permissions: + contents: write + pull-requests: write +jobs: publish_alpha: - needs: translations - uses: TigreGotico/gh-automations/.github/workflows/publish-alpha.yml@master - secrets: inherit + if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' + uses: OpenVoiceOS/gh-automations/.github/workflows/publish-alpha.yml@dev + secrets: + PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} + MATRIX_TOKEN: ${{ secrets.MATRIX_TOKEN }} with: branch: 'dev' - version_file: 'version.py' - setup_py: 'setup.py' + version_file: 'ovos_skill_cmd/version.py' update_changelog: true publish_prerelease: true - changelog_max_issues: 100 - - notify: - if: github.event.pull_request.merged == true - needs: publish_alpha - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Send message to Matrix bots channel - id: matrix-chat-message - uses: fadenb/matrix-chat-message@v0.0.6 - with: - homeserver: 'matrix.org' - token: ${{ secrets.MATRIX_TOKEN }} - channel: '!WjxEKjjINpyBRPFgxl:krbel.duckdns.org' - message: | - new ${{ github.event.repository.name }} PR merged! https://github.com/${{ github.repository }}/pull/${{ github.event.number }} - - - publish_pypi: - needs: publish_alpha - if: success() # Ensure this job only runs if the previous job succeeds - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - ref: dev - fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository. - - name: Setup Python - uses: actions/setup-python@v1 - with: - python-version: "3.11" - - name: Install Build Tools - run: | - python -m pip install build wheel - - name: version - run: echo "::set-output name=version::$(python setup.py --version)" - id: version - - name: Build Distribution Packages - run: | - python setup.py sdist bdist_wheel - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@master - with: - password: ${{secrets.PYPI_TOKEN}} - - propose_release: - needs: publish_alpha - if: success() # Ensure this job only runs if the previous job succeeds - runs-on: ubuntu-latest - steps: - - name: Checkout dev branch - uses: actions/checkout@v3 - with: - ref: dev - - - name: Setup Python - uses: actions/setup-python@v2 - with: - python-version: '3.10' - - - name: Get version from setup.py - id: get_version - run: | - VERSION=$(python setup.py --version) - echo "VERSION=$VERSION" >> $GITHUB_ENV - - - name: Create and push new branch - run: | - git checkout -b release-${{ env.VERSION }} - git push origin release-${{ env.VERSION }} - - - name: Open Pull Request from dev to master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # Variables - BRANCH_NAME="release-${{ env.VERSION }}" - BASE_BRANCH="master" - HEAD_BRANCH="release-${{ env.VERSION }}" - PR_TITLE="Release ${{ env.VERSION }}" - PR_BODY="Human review requested!" - - # Create a PR using GitHub API - curl -X POST \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: token $GITHUB_TOKEN" \ - -d "{\"title\":\"$PR_TITLE\",\"body\":\"$PR_BODY\",\"head\":\"$HEAD_BRANCH\",\"base\":\"$BASE_BRANCH\"}" \ - https://api.github.com/repos/${{ github.repository }}/pulls - + propose_release: true + changelog_max_issues: 50 + publish_pypi: true + notify_matrix: true diff --git a/.github/workflows/repo-health.yml b/.github/workflows/repo-health.yml new file mode 100644 index 0000000..b09125e --- /dev/null +++ b/.github/workflows/repo-health.yml @@ -0,0 +1,13 @@ +name: Repo Health + +on: + pull_request: + branches: [dev, master, main] + workflow_dispatch: + +jobs: + repo_health: + uses: OpenVoiceOS/gh-automations/.github/workflows/repo-health.yml@dev + secrets: inherit + with: + version_file: 'ovos_skill_cmd/version.py' diff --git a/.github/workflows/skill-check.yml b/.github/workflows/skill-check.yml new file mode 100644 index 0000000..ebcfd68 --- /dev/null +++ b/.github/workflows/skill-check.yml @@ -0,0 +1,15 @@ +name: Skill Check + +on: + pull_request: + branches: [dev, master, main] + workflow_dispatch: + +jobs: + skill_check: + uses: OpenVoiceOS/gh-automations/.github/workflows/skill-check.yml@dev + secrets: inherit + with: + fail_on_missing_en_us: true + fail_on_invalid_skill_json: false + skip_if_not_skill: true diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml deleted file mode 100644 index b4c67be..0000000 --- a/.github/workflows/unit_tests.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: Run UnitTests -on: - pull_request: - branches: - - dev - paths-ignore: - - 'version.py' - - 'examples/**' - - '.github/**' - - '.gitignore' - - 'LICENSE' - - 'CHANGELOG.md' - - 'MANIFEST.in' - - 'README.md' - - 'scripts/**' - push: - branches: - - master - paths-ignore: - - 'version.py' - - 'examples/**' - - '.github/**' - - '.gitignore' - - 'LICENSE' - - 'CHANGELOG.md' - - 'MANIFEST.in' - - 'README.md' - - 'scripts/**' - workflow_dispatch: - -jobs: - unit_tests: - strategy: - matrix: - python-version: [3.9, "3.10" ] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install System Dependencies - run: | - sudo apt-get update - sudo apt install python3-dev - python -m pip install build wheel - - name: Install core repo - run: | - pip install . - - name: Install test dependencies - run: | - pip install pytest pytest-timeout pytest-cov - - name: Install System Dependencies - run: | - sudo apt-get update - - name: Install ovos dependencies - run: | - pip install ovos-plugin-manager - - name: Run unittests - run: | - pytest --cov=ovos-skill-cmd --cov-report xml test - - name: Upload coverage - env: - CODECOV_TOKEN: ${{secrets.CODECOV_TOKEN}} - uses: codecov/codecov-action@v2 diff --git a/CHANGELOG.md b/CHANGELOG.md index ad4a0c7..0ef2823 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,156 @@ # Changelog -## [0.2.11a1](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.2.11a1) (2025-04-02) +## [0.7.0a2](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.7.0a2) (2026-09-09) -[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.2.10...0.2.11a1) +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.7.0a1...0.7.0a2) **Merged pull requests:** -- optimizing German translation for better usage [\#43](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/43) ([gitlocalize-app[bot]](https://github.com/apps/gitlocalize-app)) -- optimizing German translation for better usage [\#42](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/42) ([gitlocalize-app[bot]](https://github.com/apps/gitlocalize-app)) +- Locale sv-SE: Look over and improve language [\#84](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/84) ([forslund](https://github.com/forslund)) + +## [0.7.0a1](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.7.0a1) (2026-09-09) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.6.0a1...0.7.0a1) + +**Merged pull requests:** + +- feat: list\_scripts intent and fallback dialogs in every locale [\#83](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/83) ([JarbasAl](https://github.com/JarbasAl)) + +## [0.6.0a1](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.6.0a1) (2026-09-07) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.5.0a2...0.6.0a1) + +**Merged pull requests:** + +- feat: oc-FR locale parity [\#78](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/78) ([JarbasAl](https://github.com/JarbasAl)) + +## [0.5.0a2](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.5.0a2) (2026-09-07) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.5.0a1...0.5.0a2) + +**Merged pull requests:** + +- chore: drop unused ovos-adapt-parser dependency [\#80](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/80) ([JarbasAl](https://github.com/JarbasAl)) + +## [0.5.0a1](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.5.0a1) (2026-09-07) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.4.0a1...0.5.0a1) + +**Merged pull requests:** + +- feat: pt-BR nl-NL sv-SE locale parity [\#77](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/77) ([JarbasAl](https://github.com/JarbasAl)) + +## [0.4.0a1](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.4.0a1) (2026-09-02) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.3.2a2...0.4.0a1) + +**Merged pull requests:** + +- feat: add list-aliases intent for configured scripts [\#74](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/74) ([JarbasAl](https://github.com/JarbasAl)) + +## [0.3.2a2](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.3.2a2) (2026-09-02) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.3.2a1...0.3.2a2) + +**Merged pull requests:** + +- refactor: migrate RunScriptCommandIntent to a .intent file [\#71](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/71) ([JarbasAl](https://github.com/JarbasAl)) + +## [0.3.2a1](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.3.2a1) (2026-08-26) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.3.1a2...0.3.2a1) + +**Merged pull requests:** + +- fix: rename bare lang code locale directories [\#69](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/69) ([ovos-localize[bot]](https://github.com/apps/ovos-localize)) + +## [0.3.1a2](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.3.1a2) (2026-08-25) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.3.1a1...0.3.1a2) + +**Merged pull requests:** + +- translate\(kab-DZ\): update skill.json [\#67](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/67) ([ovos-localize[bot]](https://github.com/apps/ovos-localize)) +- translate\(kab-DZ\): update running.dialog [\#66](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/66) ([ovos-localize[bot]](https://github.com/apps/ovos-localize)) +- translate\(kab-DZ\): update Run.voc [\#65](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/65) ([ovos-localize[bot]](https://github.com/apps/ovos-localize)) + +## [0.3.1a1](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.3.1a1) (2026-08-14) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.3.0a2...0.3.1a1) + +**Merged pull requests:** + +- fix: allow ovos-workshop 9.x [\#63](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/63) ([JarbasAl](https://github.com/JarbasAl)) + +## [0.3.0a2](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.3.0a2) (2026-08-13) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.3.0a1...0.3.0a2) + +**Merged pull requests:** + +- test: golden-utterance e2e suite for ovos-skill-cmd [\#61](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/61) ([JarbasAl](https://github.com/JarbasAl)) + +## [0.3.0a1](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.3.0a1) (2026-08-10) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.2.14a4...0.3.0a1) + +**Merged pull requests:** + +- feat: add ovoscope end2end intent-routing tests [\#57](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/57) ([JarbasAl](https://github.com/JarbasAl)) + +## [0.2.14a4](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.2.14a4) (2026-08-01) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.2.14a3...0.2.14a4) + +**Merged pull requests:** + +- docs: rewrite README in Simplified Technical English [\#58](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/58) ([JarbasAl](https://github.com/JarbasAl)) + +## [0.2.14a3](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.2.14a3) (2026-04-09) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.2.14a2...0.2.14a3) + +**Merged pull requests:** + +- chore\(ovos-skill-cmd\): allow ovos-workshop\<9.0.0 [\#55](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/55) ([JarbasAl](https://github.com/JarbasAl)) + +## [0.2.14a2](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.2.14a2) (2026-04-04) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.2.14a1...0.2.14a2) + +**Merged pull requests:** + +- ci: modernize GitHub workflows [\#53](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/53) ([JarbasAl](https://github.com/JarbasAl)) +- fix\(i18n\): normalize locale folders to canonical BCP-47 [\#52](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/52) ([JarbasAl](https://github.com/JarbasAl)) + +## [0.2.14a1](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.2.14a1) (2026-03-24) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.2.13a1...0.2.14a1) + +**Merged pull requests:** + +- fix: rename bare lang code locale directories [\#50](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/50) ([ovos-localize[bot]](https://github.com/apps/ovos-localize)) + +## [0.2.13a1](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.2.13a1) (2026-03-24) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.2.12a1...0.2.13a1) + +**Merged pull requests:** + +- fix: rename bare lang code locale directories [\#48](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/48) ([ovos-localize[bot]](https://github.com/apps/ovos-localize)) +- Add French cmd locale [\#47](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/47) ([goldyfruit](https://github.com/goldyfruit)) + +## [0.2.12a1](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/0.2.12a1) (2026-01-19) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/V0.2.11...0.2.12a1) + +**Merged pull requests:** + +- es-es/translate [\#45](https://github.com/OpenVoiceOS/ovos-skill-cmd/pull/45) ([gitlocalize-app[bot]](https://github.com/apps/gitlocalize-app)) + +## [V0.2.11](https://github.com/OpenVoiceOS/ovos-skill-cmd/tree/V0.2.11) (2025-06-08) + +[Full Changelog](https://github.com/OpenVoiceOS/ovos-skill-cmd/compare/0.2.11...V0.2.11) diff --git a/MANIFEST.in b/MANIFEST.in index fb05bf3..e276efc 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,2 @@ -recursive-include locale * +recursive-include ovos_skill_cmd/locale * include *.txt \ No newline at end of file diff --git a/README.md b/README.md index 36f7219..bdd2541 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,23 @@ -# Commands Skill +# OVOS Cmd Skill -A simple OVOS skill for running shell scripts and other commands. The commands execute quietly without requiring confirmation from OVOS. +This OVOS skill runs shell scripts and other commands. The commands run without asking for confirmation. -## Features +## Install -- Run shell scripts or commands by speaking human-readable phrases. -- Configure aliases for easy-to-remember commands. -- Optionally run commands under a specific user's privileges. +```bash +pip install ovos-skill-cmd +``` ## Usage -Trigger commands using phrases like: +Trigger a command with a spoken phrase: -- **"Hey Mycroft, launch command echo TEST"** -- **"Hey Mycroft, run script generate report"** +- "Hey Mycroft, launch command echo TEST" +- "Hey Mycroft, run script generate report" ## Configuration -The skill can be configured to map spoken phrases to scripts or commands in the `settings.json` file. For example: +Configure the skill in `settings.json`. Map a spoken phrase to a script or command under `alias`. For example: ```json { @@ -27,27 +27,30 @@ The skill can be configured to map spoken phrases to scripts or commands in the } ``` -### Example: -- User says: **"Run script generate report"** -- The skill executes: `/home/forslund/scripts/generate_report.sh` +With this setting, the phrase "run script generate report" makes the skill execute `/home/forslund/scripts/generate_report.sh`. -### Additional Settings: +### user -- **`user`** *(optional)*: Specify a username to run commands under their privileges. Example: - ```json - { - "user": "ovos" - } - ``` +Set `user` to run commands under a specific user's privileges: -- **`shell`** *(optional)*: Determines whether commands are executed via a shell. Defaults to `true`. Example: - ```json - { - "shell": false - } - ``` +```json +{ + "user": "ovos" +} +``` + +### shell + +Set `shell` to control whether commands run through a shell. It defaults to `true`: + +```json +{ + "shell": false +} +``` + +### Full example -### Full Configuration Example: ```json { "user": "ovos", @@ -60,21 +63,17 @@ The skill can be configured to map spoken phrases to scripts or commands in the } ``` -## Security Notes +## Security notes -1. **Shell Commands**: - - By default, commands are executed via the shell, which allows complex operations but may expose security risks. If your commands don’t require shell features, set `shell` to `false`. +- Running commands through a shell allows complex operations but can expose security risks. If your commands do not need shell features, set `shell` to `false`. +- A command can run under a specific user through the `user` field. Make sure that user has the permissions the command needs. +- Do not configure dangerous commands, such as `rm -rf`, without additional safeguards. -2. **User Permissions**: - - Commands can run under a specific user by configuring the `user` field. Ensure that the user has appropriate permissions to execute the commands. +## Docker containers -3. **Validation**: - - Avoid configuring dangerous commands like `rm -rf` without additional safeguards. +Commands run only inside the Docker container. If a command also needs an effect outside the container, use an additional mechanism, such as a named pipe. - -## Note for using the skill in Docker containers: - -All commands run exclusively within the Docker container. If the commands/scripts are also supposed to have an effect outside the container, additional solutions are required. Here is an example for steering kodi (outsight the container) with ovos-skill-cmd: +This example steers Kodi, which runs outside the container, from `ovos-skill-cmd`: ```json { @@ -93,10 +92,19 @@ All commands run exclusively within the Docker container. If the commands/script } ``` -"/home/ovos/.config/mycroft/joespipe" is a named pipe placed in the shared volume of the ovos config folder. Outside of the container is a mini script watching for commands in the pipe: +`/home/ovos/.config/mycroft/joespipe` is a named pipe in the shared volume of the OVOS config folder. Outside the container, a small script watches the pipe for commands: -```json +```bash #!/bin/bash while true; do eval "$(cat /storage/ovos/config/joespipe)"; done ``` -A description for the named pipe solution you can find [here](https://stackoverflow.com/questions/32163955/how-to-run-shell-script-on-host-from-docker-container) + +Read more about this named-pipe approach on [Stack Overflow](https://stackoverflow.com/questions/32163955/how-to-run-shell-script-on-host-from-docker-container). + +## Related projects + +- [OpenVoiceOS/OpenVoiceOS](https://github.com/OpenVoiceOS/OpenVoiceOS) — the OVOS platform this skill runs on. + +## License + +Apache License 2.0. See [LICENSE.txt](LICENSE.txt). diff --git a/__init__.py b/ovos_skill_cmd/__init__.py similarity index 74% rename from __init__.py rename to ovos_skill_cmd/__init__.py index 8fc52cc..a292750 100644 --- a/__init__.py +++ b/ovos_skill_cmd/__init__.py @@ -17,10 +17,10 @@ import subprocess from pwd import getpwnam +from ovos_utils.dialog import join_list from ovos_utils.log import LOG from ovos_workshop.decorators import intent_handler -from ovos_workshop.intents import IntentBuilder from ovos_workshop.skills import OVOSSkill @@ -46,15 +46,26 @@ def initialize(self): self.gid = pwnam.pw_gid self.alias = self.settings.get('alias') or {} - for alias in self.alias: - LOG.info(f"Adding script keyword: {alias}") + samples = list(self.alias) + if samples: for lang in self.native_langs: - self.register_vocabulary(alias, 'Script', lang=lang) + LOG.info(f"Adding script entities: {samples}") + self.intent_service.register_entity('script', samples, lang) - @intent_handler(IntentBuilder('RunScriptCommandIntent') - .require('Script').require('Run')) + @intent_handler("list_scripts.intent") + def handle_list_scripts(self, message): + names = list(self.alias) + if not names: + self.speak_dialog("no.scripts") + return + self.speak(join_list(names, "and")) + + @intent_handler("RunScriptCommandIntent.intent") def run(self, message): - alias = message.data.get('Script') + alias = message.data.get('script') + if alias not in self.alias: + self.speak_dialog("unknown.script", {"name": alias}) + return self.speak_dialog("running", {"alias": alias}) script = self.alias[alias] LOG.info(f"alias: {alias} | command: {script}") diff --git a/locale/ca-es/Run.voc b/ovos_skill_cmd/locale/ca-ES/Run.voc similarity index 100% rename from locale/ca-es/Run.voc rename to ovos_skill_cmd/locale/ca-ES/Run.voc diff --git a/ovos_skill_cmd/locale/ca-ES/RunScriptCommandIntent.intent b/ovos_skill_cmd/locale/ca-ES/RunScriptCommandIntent.intent new file mode 100644 index 0000000..2602ae7 --- /dev/null +++ b/ovos_skill_cmd/locale/ca-ES/RunScriptCommandIntent.intent @@ -0,0 +1 @@ +(executa|corre|llança) (el|un|) (script|comandament|ordre) {script} diff --git a/ovos_skill_cmd/locale/ca-ES/list_scripts.intent b/ovos_skill_cmd/locale/ca-ES/list_scripts.intent new file mode 100644 index 0000000..e52c39f --- /dev/null +++ b/ovos_skill_cmd/locale/ca-ES/list_scripts.intent @@ -0,0 +1,3 @@ +(llista|quins són) els meus (àlies|scripts|comandaments|ordres) +quins (scripts|comandaments|ordres) (coneixes|estan configurats) +quins (scripts|comandaments|ordres) pots executar diff --git a/ovos_skill_cmd/locale/ca-ES/no.scripts.dialog b/ovos_skill_cmd/locale/ca-ES/no.scripts.dialog new file mode 100644 index 0000000..b4236b5 --- /dev/null +++ b/ovos_skill_cmd/locale/ca-ES/no.scripts.dialog @@ -0,0 +1,2 @@ +No tinc cap script configurat +Encara no hi ha cap ordre configurada diff --git a/locale/ca-es/running.dialog b/ovos_skill_cmd/locale/ca-ES/running.dialog similarity index 100% rename from locale/ca-es/running.dialog rename to ovos_skill_cmd/locale/ca-ES/running.dialog diff --git a/locale/ca-es/skill.json b/ovos_skill_cmd/locale/ca-ES/skill.json similarity index 100% rename from locale/ca-es/skill.json rename to ovos_skill_cmd/locale/ca-ES/skill.json diff --git a/ovos_skill_cmd/locale/ca-ES/unknown.script.dialog b/ovos_skill_cmd/locale/ca-ES/unknown.script.dialog new file mode 100644 index 0000000..006ed50 --- /dev/null +++ b/ovos_skill_cmd/locale/ca-ES/unknown.script.dialog @@ -0,0 +1 @@ +No conec cap script anomenat {name} diff --git a/locale/da/Run.voc b/ovos_skill_cmd/locale/da-DK/Run.voc similarity index 100% rename from locale/da/Run.voc rename to ovos_skill_cmd/locale/da-DK/Run.voc diff --git a/ovos_skill_cmd/locale/da-DK/RunScriptCommandIntent.intent b/ovos_skill_cmd/locale/da-DK/RunScriptCommandIntent.intent new file mode 100644 index 0000000..d01969f --- /dev/null +++ b/ovos_skill_cmd/locale/da-DK/RunScriptCommandIntent.intent @@ -0,0 +1 @@ +(kør|udfør|start) (kommando|kommandoen|script|scriptet) {script} diff --git a/ovos_skill_cmd/locale/da-DK/list_scripts.intent b/ovos_skill_cmd/locale/da-DK/list_scripts.intent new file mode 100644 index 0000000..f454cbd --- /dev/null +++ b/ovos_skill_cmd/locale/da-DK/list_scripts.intent @@ -0,0 +1,3 @@ +(vis|hvad er) mine (aliasser|scripts|kommandoer) +hvilke (scripts|kommandoer) (kender du|er sat op) +hvilke (scripts|kommandoer) kan du køre diff --git a/ovos_skill_cmd/locale/da-DK/no.scripts.dialog b/ovos_skill_cmd/locale/da-DK/no.scripts.dialog new file mode 100644 index 0000000..c008953 --- /dev/null +++ b/ovos_skill_cmd/locale/da-DK/no.scripts.dialog @@ -0,0 +1,2 @@ +Jeg har ingen scripts sat op +Der er ingen kommandoer sat op endnu diff --git a/locale/da/running.dialog b/ovos_skill_cmd/locale/da-DK/running.dialog similarity index 100% rename from locale/da/running.dialog rename to ovos_skill_cmd/locale/da-DK/running.dialog diff --git a/locale/da/skill.json b/ovos_skill_cmd/locale/da-DK/skill.json similarity index 100% rename from locale/da/skill.json rename to ovos_skill_cmd/locale/da-DK/skill.json diff --git a/ovos_skill_cmd/locale/da-DK/unknown.script.dialog b/ovos_skill_cmd/locale/da-DK/unknown.script.dialog new file mode 100644 index 0000000..ab1a443 --- /dev/null +++ b/ovos_skill_cmd/locale/da-DK/unknown.script.dialog @@ -0,0 +1 @@ +Jeg kender ikke et script der hedder {name} diff --git a/locale/de-de/Run.voc b/ovos_skill_cmd/locale/de-DE/Run.voc similarity index 100% rename from locale/de-de/Run.voc rename to ovos_skill_cmd/locale/de-DE/Run.voc diff --git a/ovos_skill_cmd/locale/de-DE/RunScriptCommandIntent.intent b/ovos_skill_cmd/locale/de-DE/RunScriptCommandIntent.intent new file mode 100644 index 0000000..86b3f9f --- /dev/null +++ b/ovos_skill_cmd/locale/de-DE/RunScriptCommandIntent.intent @@ -0,0 +1,2 @@ +(führe|starte) (das|den|ein|einen|) (skript|befehl|kommando) {script} aus +(starte|führe) (skript|befehl|kommando) {script} diff --git a/ovos_skill_cmd/locale/de-DE/list_scripts.intent b/ovos_skill_cmd/locale/de-DE/list_scripts.intent new file mode 100644 index 0000000..b4234a9 --- /dev/null +++ b/ovos_skill_cmd/locale/de-DE/list_scripts.intent @@ -0,0 +1,3 @@ +(zeige|liste|was sind) meine (aliasse|skripte|befehle|kommandos) +welche (skripte|befehle|kommandos) (kennst du|sind eingerichtet|sind konfiguriert) +welche (skripte|befehle|kommandos) kannst du ausführen diff --git a/ovos_skill_cmd/locale/de-DE/no.scripts.dialog b/ovos_skill_cmd/locale/de-DE/no.scripts.dialog new file mode 100644 index 0000000..7b2fe97 --- /dev/null +++ b/ovos_skill_cmd/locale/de-DE/no.scripts.dialog @@ -0,0 +1,2 @@ +Ich habe keine Skripte eingerichtet +Es sind noch keine Befehle konfiguriert diff --git a/locale/de-de/running.dialog b/ovos_skill_cmd/locale/de-DE/running.dialog similarity index 100% rename from locale/de-de/running.dialog rename to ovos_skill_cmd/locale/de-DE/running.dialog diff --git a/locale/de-de/skill.json b/ovos_skill_cmd/locale/de-DE/skill.json similarity index 100% rename from locale/de-de/skill.json rename to ovos_skill_cmd/locale/de-DE/skill.json diff --git a/ovos_skill_cmd/locale/de-DE/unknown.script.dialog b/ovos_skill_cmd/locale/de-DE/unknown.script.dialog new file mode 100644 index 0000000..85d083b --- /dev/null +++ b/ovos_skill_cmd/locale/de-DE/unknown.script.dialog @@ -0,0 +1 @@ +Ich kenne kein Skript namens {name} diff --git a/locale/en-us/Run.voc b/ovos_skill_cmd/locale/en-US/Run.voc similarity index 100% rename from locale/en-us/Run.voc rename to ovos_skill_cmd/locale/en-US/Run.voc diff --git a/ovos_skill_cmd/locale/en-US/RunScriptCommandIntent.intent b/ovos_skill_cmd/locale/en-US/RunScriptCommandIntent.intent new file mode 100644 index 0000000..f8d3288 --- /dev/null +++ b/ovos_skill_cmd/locale/en-US/RunScriptCommandIntent.intent @@ -0,0 +1 @@ +(run|execute|launch) (command|script) {script} diff --git a/ovos_skill_cmd/locale/en-US/list_scripts.intent b/ovos_skill_cmd/locale/en-US/list_scripts.intent new file mode 100644 index 0000000..953d583 --- /dev/null +++ b/ovos_skill_cmd/locale/en-US/list_scripts.intent @@ -0,0 +1,3 @@ +(list|what are) my (aliases|scripts|commands) +what (scripts|commands) (do you know|are configured) +which (scripts|commands) can you run diff --git a/ovos_skill_cmd/locale/en-US/no.scripts.dialog b/ovos_skill_cmd/locale/en-US/no.scripts.dialog new file mode 100644 index 0000000..f10d7f6 --- /dev/null +++ b/ovos_skill_cmd/locale/en-US/no.scripts.dialog @@ -0,0 +1,2 @@ +I don't have any scripts configured +No commands are set up yet diff --git a/locale/en-us/running.dialog b/ovos_skill_cmd/locale/en-US/running.dialog similarity index 100% rename from locale/en-us/running.dialog rename to ovos_skill_cmd/locale/en-US/running.dialog diff --git a/locale/en-us/skill.json b/ovos_skill_cmd/locale/en-US/skill.json similarity index 100% rename from locale/en-us/skill.json rename to ovos_skill_cmd/locale/en-US/skill.json diff --git a/ovos_skill_cmd/locale/en-US/unknown.script.dialog b/ovos_skill_cmd/locale/en-US/unknown.script.dialog new file mode 100644 index 0000000..7eade0c --- /dev/null +++ b/ovos_skill_cmd/locale/en-US/unknown.script.dialog @@ -0,0 +1 @@ +I don't know a script called {name} \ No newline at end of file diff --git a/ovos_skill_cmd/locale/es-ES/Run.voc b/ovos_skill_cmd/locale/es-ES/Run.voc new file mode 100644 index 0000000..de21fd2 --- /dev/null +++ b/ovos_skill_cmd/locale/es-ES/Run.voc @@ -0,0 +1 @@ +(correr|ejecutar|lanzar) (un|una|)(comando|script) \ No newline at end of file diff --git a/ovos_skill_cmd/locale/es-ES/RunScriptCommandIntent.intent b/ovos_skill_cmd/locale/es-ES/RunScriptCommandIntent.intent new file mode 100644 index 0000000..6528d50 --- /dev/null +++ b/ovos_skill_cmd/locale/es-ES/RunScriptCommandIntent.intent @@ -0,0 +1 @@ +(corre[r]|ejecuta[r]|lanza[r]) (el|un|) (comando|script) {script} diff --git a/ovos_skill_cmd/locale/es-ES/list_scripts.intent b/ovos_skill_cmd/locale/es-ES/list_scripts.intent new file mode 100644 index 0000000..686ab02 --- /dev/null +++ b/ovos_skill_cmd/locale/es-ES/list_scripts.intent @@ -0,0 +1,3 @@ +(lista|cuáles son) mis (alias|scripts|comandos) +qué (scripts|comandos) (conoces|están configurados) +qué (scripts|comandos) puedes ejecutar diff --git a/ovos_skill_cmd/locale/es-ES/no.scripts.dialog b/ovos_skill_cmd/locale/es-ES/no.scripts.dialog new file mode 100644 index 0000000..2df8c19 --- /dev/null +++ b/ovos_skill_cmd/locale/es-ES/no.scripts.dialog @@ -0,0 +1,2 @@ +No tengo ningún script configurado +Todavía no hay comandos configurados diff --git a/ovos_skill_cmd/locale/es-ES/running.dialog b/ovos_skill_cmd/locale/es-ES/running.dialog new file mode 100644 index 0000000..67cb866 --- /dev/null +++ b/ovos_skill_cmd/locale/es-ES/running.dialog @@ -0,0 +1 @@ +ejecutando {alias} (comando|script) \ No newline at end of file diff --git a/ovos_skill_cmd/locale/es-ES/skill.json b/ovos_skill_cmd/locale/es-ES/skill.json new file mode 100644 index 0000000..f64d1ec --- /dev/null +++ b/ovos_skill_cmd/locale/es-ES/skill.json @@ -0,0 +1,6 @@ +{ + "examples": [ + "comando de lanzamiento ___", + "ejecutar script ___" + ] +} diff --git a/ovos_skill_cmd/locale/es-ES/unknown.script.dialog b/ovos_skill_cmd/locale/es-ES/unknown.script.dialog new file mode 100644 index 0000000..e7b3a3a --- /dev/null +++ b/ovos_skill_cmd/locale/es-ES/unknown.script.dialog @@ -0,0 +1 @@ +No conozco ningún script llamado {name} \ No newline at end of file diff --git a/locale/eu/Run.voc b/ovos_skill_cmd/locale/eu-ES/Run.voc similarity index 100% rename from locale/eu/Run.voc rename to ovos_skill_cmd/locale/eu-ES/Run.voc diff --git a/ovos_skill_cmd/locale/eu-ES/RunScriptCommandIntent.intent b/ovos_skill_cmd/locale/eu-ES/RunScriptCommandIntent.intent new file mode 100644 index 0000000..a2bce8d --- /dev/null +++ b/ovos_skill_cmd/locale/eu-ES/RunScriptCommandIntent.intent @@ -0,0 +1 @@ +(jarri martxan|exekutatu|abiarazi) (komandoa|script-a) {script} diff --git a/ovos_skill_cmd/locale/eu-ES/list_scripts.intent b/ovos_skill_cmd/locale/eu-ES/list_scripts.intent new file mode 100644 index 0000000..46e63b1 --- /dev/null +++ b/ovos_skill_cmd/locale/eu-ES/list_scripts.intent @@ -0,0 +1,3 @@ +(zerrendatu|zein dira) nire (aliasak|scriptak|komandoak) +zein (script|komando) (ezagutzen dituzu|daude konfiguratuta) +zein (script|komando) exekuta ditzakezu diff --git a/ovos_skill_cmd/locale/eu-ES/no.scripts.dialog b/ovos_skill_cmd/locale/eu-ES/no.scripts.dialog new file mode 100644 index 0000000..32fba7f --- /dev/null +++ b/ovos_skill_cmd/locale/eu-ES/no.scripts.dialog @@ -0,0 +1,2 @@ +Ez daukat scriptik konfiguratuta +Oraindik ez dago komandorik konfiguratuta diff --git a/ovos_skill_cmd/locale/eu-ES/running.dialog b/ovos_skill_cmd/locale/eu-ES/running.dialog new file mode 100644 index 0000000..149655f --- /dev/null +++ b/ovos_skill_cmd/locale/eu-ES/running.dialog @@ -0,0 +1 @@ +{alias} (komandoa|scripta) exekutatzen diff --git a/ovos_skill_cmd/locale/eu-ES/unknown.script.dialog b/ovos_skill_cmd/locale/eu-ES/unknown.script.dialog new file mode 100644 index 0000000..50c3a18 --- /dev/null +++ b/ovos_skill_cmd/locale/eu-ES/unknown.script.dialog @@ -0,0 +1 @@ +Ez dut ezagutzen {name} izeneko script bat \ No newline at end of file diff --git a/ovos_skill_cmd/locale/fr-FR/Run.voc b/ovos_skill_cmd/locale/fr-FR/Run.voc new file mode 100644 index 0000000..fe6dbff --- /dev/null +++ b/ovos_skill_cmd/locale/fr-FR/Run.voc @@ -0,0 +1 @@ +(lance|exécute|démarre) (la|le|) (commande|script) \ No newline at end of file diff --git a/ovos_skill_cmd/locale/fr-FR/RunScriptCommandIntent.intent b/ovos_skill_cmd/locale/fr-FR/RunScriptCommandIntent.intent new file mode 100644 index 0000000..3d43214 --- /dev/null +++ b/ovos_skill_cmd/locale/fr-FR/RunScriptCommandIntent.intent @@ -0,0 +1 @@ +(lance|exécute|démarre) (la|le|) (commande|script) {script} diff --git a/ovos_skill_cmd/locale/fr-FR/list_scripts.intent b/ovos_skill_cmd/locale/fr-FR/list_scripts.intent new file mode 100644 index 0000000..d4bfdb4 --- /dev/null +++ b/ovos_skill_cmd/locale/fr-FR/list_scripts.intent @@ -0,0 +1,3 @@ +(liste|quels sont) mes (alias|scripts|commandes) +quels (scripts|commandes) (connais-tu|sont configurés) +quels (scripts|commandes) peux-tu (lancer|exécuter) diff --git a/ovos_skill_cmd/locale/fr-FR/no.scripts.dialog b/ovos_skill_cmd/locale/fr-FR/no.scripts.dialog new file mode 100644 index 0000000..67cd803 --- /dev/null +++ b/ovos_skill_cmd/locale/fr-FR/no.scripts.dialog @@ -0,0 +1,2 @@ +Je n'ai aucun script configuré +Aucune commande n'est encore configurée diff --git a/ovos_skill_cmd/locale/fr-FR/running.dialog b/ovos_skill_cmd/locale/fr-FR/running.dialog new file mode 100644 index 0000000..63a740c --- /dev/null +++ b/ovos_skill_cmd/locale/fr-FR/running.dialog @@ -0,0 +1 @@ +J'exécute {alias}. \ No newline at end of file diff --git a/ovos_skill_cmd/locale/fr-FR/skill.json b/ovos_skill_cmd/locale/fr-FR/skill.json new file mode 100644 index 0000000..d2d47c4 --- /dev/null +++ b/ovos_skill_cmd/locale/fr-FR/skill.json @@ -0,0 +1,6 @@ +{ + "examples": [ + "lance la commande ___", + "exécute le script ___" + ] +} diff --git a/ovos_skill_cmd/locale/fr-FR/unknown.script.dialog b/ovos_skill_cmd/locale/fr-FR/unknown.script.dialog new file mode 100644 index 0000000..ffa253d --- /dev/null +++ b/ovos_skill_cmd/locale/fr-FR/unknown.script.dialog @@ -0,0 +1 @@ +Je ne connais pas de script appelé {name} \ No newline at end of file diff --git a/locale/gl-es/Run.voc b/ovos_skill_cmd/locale/gl-ES/Run.voc similarity index 100% rename from locale/gl-es/Run.voc rename to ovos_skill_cmd/locale/gl-ES/Run.voc diff --git a/ovos_skill_cmd/locale/gl-ES/RunScriptCommandIntent.intent b/ovos_skill_cmd/locale/gl-ES/RunScriptCommandIntent.intent new file mode 100644 index 0000000..12a342f --- /dev/null +++ b/ovos_skill_cmd/locale/gl-ES/RunScriptCommandIntent.intent @@ -0,0 +1 @@ +(executa[r]|arrinca[r]) [o] (comando|script) {script} diff --git a/ovos_skill_cmd/locale/gl-ES/list_scripts.intent b/ovos_skill_cmd/locale/gl-ES/list_scripts.intent new file mode 100644 index 0000000..b37766f --- /dev/null +++ b/ovos_skill_cmd/locale/gl-ES/list_scripts.intent @@ -0,0 +1,3 @@ +(lista|cales son) os meus (alias|scripts|comandos) +que (scripts|comandos) (coñeces|están configurados) +que (scripts|comandos) podes executar diff --git a/ovos_skill_cmd/locale/gl-ES/no.scripts.dialog b/ovos_skill_cmd/locale/gl-ES/no.scripts.dialog new file mode 100644 index 0000000..6667138 --- /dev/null +++ b/ovos_skill_cmd/locale/gl-ES/no.scripts.dialog @@ -0,0 +1,2 @@ +Non teño ningún script configurado +Aínda non hai comandos configurados diff --git a/locale/gl-es/running.dialog b/ovos_skill_cmd/locale/gl-ES/running.dialog similarity index 100% rename from locale/gl-es/running.dialog rename to ovos_skill_cmd/locale/gl-ES/running.dialog diff --git a/locale/gl-es/skill.json b/ovos_skill_cmd/locale/gl-ES/skill.json similarity index 100% rename from locale/gl-es/skill.json rename to ovos_skill_cmd/locale/gl-ES/skill.json diff --git a/ovos_skill_cmd/locale/gl-ES/unknown.script.dialog b/ovos_skill_cmd/locale/gl-ES/unknown.script.dialog new file mode 100644 index 0000000..9f20873 --- /dev/null +++ b/ovos_skill_cmd/locale/gl-ES/unknown.script.dialog @@ -0,0 +1 @@ +Non coñezo ningún script chamado {name} \ No newline at end of file diff --git a/locale/it-it/Run.voc b/ovos_skill_cmd/locale/it-IT/Run.voc similarity index 100% rename from locale/it-it/Run.voc rename to ovos_skill_cmd/locale/it-IT/Run.voc diff --git a/ovos_skill_cmd/locale/it-IT/RunScriptCommandIntent.intent b/ovos_skill_cmd/locale/it-IT/RunScriptCommandIntent.intent new file mode 100644 index 0000000..bd1dc8f --- /dev/null +++ b/ovos_skill_cmd/locale/it-IT/RunScriptCommandIntent.intent @@ -0,0 +1 @@ +(avvia|esegui|lancia|apri) (il|lo|un|uno|) (comando|script|codice) {script} diff --git a/ovos_skill_cmd/locale/it-IT/list_scripts.intent b/ovos_skill_cmd/locale/it-IT/list_scripts.intent new file mode 100644 index 0000000..60a068c --- /dev/null +++ b/ovos_skill_cmd/locale/it-IT/list_scripts.intent @@ -0,0 +1,3 @@ +(elenca|quali sono) i miei (alias|script|comandi) +quali (script|comandi) (conosci|sono configurati) +quali (script|comandi) puoi eseguire diff --git a/ovos_skill_cmd/locale/it-IT/no.scripts.dialog b/ovos_skill_cmd/locale/it-IT/no.scripts.dialog new file mode 100644 index 0000000..d5c0886 --- /dev/null +++ b/ovos_skill_cmd/locale/it-IT/no.scripts.dialog @@ -0,0 +1,2 @@ +Non ho nessuno script configurato +Nessun comando è ancora configurato diff --git a/ovos_skill_cmd/locale/it-IT/running.dialog b/ovos_skill_cmd/locale/it-IT/running.dialog new file mode 100644 index 0000000..3e71303 --- /dev/null +++ b/ovos_skill_cmd/locale/it-IT/running.dialog @@ -0,0 +1 @@ +eseguo (il comando|lo script) {alias} diff --git a/ovos_skill_cmd/locale/it-IT/unknown.script.dialog b/ovos_skill_cmd/locale/it-IT/unknown.script.dialog new file mode 100644 index 0000000..4fc5000 --- /dev/null +++ b/ovos_skill_cmd/locale/it-IT/unknown.script.dialog @@ -0,0 +1 @@ +Non conosco nessuno script chiamato {name} \ No newline at end of file diff --git a/ovos_skill_cmd/locale/kab/Run.voc b/ovos_skill_cmd/locale/kab/Run.voc new file mode 100644 index 0000000..6c01d46 --- /dev/null +++ b/ovos_skill_cmd/locale/kab/Run.voc @@ -0,0 +1 @@ +(selkem|sekker) (taladna|askript) diff --git a/ovos_skill_cmd/locale/kab/running.dialog b/ovos_skill_cmd/locale/kab/running.dialog new file mode 100644 index 0000000..3db4668 --- /dev/null +++ b/ovos_skill_cmd/locale/kab/running.dialog @@ -0,0 +1 @@ +aselkem n (taladna|uskript) {alias} diff --git a/ovos_skill_cmd/locale/kab/skill.json b/ovos_skill_cmd/locale/kab/skill.json new file mode 100644 index 0000000..bc74cc8 --- /dev/null +++ b/ovos_skill_cmd/locale/kab/skill.json @@ -0,0 +1,6 @@ +{ + "examples": [ + "examples[0]: sekker taladna ___", + "examples[1]: sekker askript ___" + ] +} diff --git a/ovos_skill_cmd/locale/nl-NL/Run.voc b/ovos_skill_cmd/locale/nl-NL/Run.voc new file mode 100644 index 0000000..518b233 --- /dev/null +++ b/ovos_skill_cmd/locale/nl-NL/Run.voc @@ -0,0 +1 @@ +(voer uit|start|lanceer) (commando|script) diff --git a/ovos_skill_cmd/locale/nl-NL/RunScriptCommandIntent.intent b/ovos_skill_cmd/locale/nl-NL/RunScriptCommandIntent.intent new file mode 100644 index 0000000..2ac1a09 --- /dev/null +++ b/ovos_skill_cmd/locale/nl-NL/RunScriptCommandIntent.intent @@ -0,0 +1 @@ +(voer uit|start|lanceer) (commando|script) {script} diff --git a/ovos_skill_cmd/locale/nl-NL/list_scripts.intent b/ovos_skill_cmd/locale/nl-NL/list_scripts.intent new file mode 100644 index 0000000..4b46dce --- /dev/null +++ b/ovos_skill_cmd/locale/nl-NL/list_scripts.intent @@ -0,0 +1,3 @@ +(toon|wat zijn) mijn (aliassen|scripts|commando's) +welke (scripts|commando's) (ken je|zijn geconfigureerd) +welke (scripts|commando's) kun je uitvoeren diff --git a/ovos_skill_cmd/locale/nl-NL/no.scripts.dialog b/ovos_skill_cmd/locale/nl-NL/no.scripts.dialog new file mode 100644 index 0000000..8516efa --- /dev/null +++ b/ovos_skill_cmd/locale/nl-NL/no.scripts.dialog @@ -0,0 +1,2 @@ +Ik heb geen scripts geconfigureerd +Er zijn nog geen commando's ingesteld diff --git a/ovos_skill_cmd/locale/nl-NL/running.dialog b/ovos_skill_cmd/locale/nl-NL/running.dialog new file mode 100644 index 0000000..32a30c8 --- /dev/null +++ b/ovos_skill_cmd/locale/nl-NL/running.dialog @@ -0,0 +1 @@ +(commando|script) {alias} wordt uitgevoerd diff --git a/ovos_skill_cmd/locale/nl-NL/skill.json b/ovos_skill_cmd/locale/nl-NL/skill.json new file mode 100644 index 0000000..6b95b90 --- /dev/null +++ b/ovos_skill_cmd/locale/nl-NL/skill.json @@ -0,0 +1,6 @@ +{ + "examples": [ + "start commando ___", + "voer script ___ uit" + ] +} diff --git a/ovos_skill_cmd/locale/nl-NL/unknown.script.dialog b/ovos_skill_cmd/locale/nl-NL/unknown.script.dialog new file mode 100644 index 0000000..dbadfe5 --- /dev/null +++ b/ovos_skill_cmd/locale/nl-NL/unknown.script.dialog @@ -0,0 +1 @@ +Ik ken geen script genaamd {name} diff --git a/ovos_skill_cmd/locale/oc-FR/Run.voc b/ovos_skill_cmd/locale/oc-FR/Run.voc new file mode 100644 index 0000000..bad2a2c --- /dev/null +++ b/ovos_skill_cmd/locale/oc-FR/Run.voc @@ -0,0 +1 @@ +(executa|lança|inicia) (un |una |l'|la |lo )(òrdre|comanda|script) diff --git a/ovos_skill_cmd/locale/oc-FR/RunScriptCommandIntent.intent b/ovos_skill_cmd/locale/oc-FR/RunScriptCommandIntent.intent new file mode 100644 index 0000000..1f385b6 --- /dev/null +++ b/ovos_skill_cmd/locale/oc-FR/RunScriptCommandIntent.intent @@ -0,0 +1 @@ +(executa|lança|inicia) (l'òrdre|l'script|la comanda) {script} diff --git a/ovos_skill_cmd/locale/oc-FR/list_scripts.intent b/ovos_skill_cmd/locale/oc-FR/list_scripts.intent new file mode 100644 index 0000000..ad7dd8b --- /dev/null +++ b/ovos_skill_cmd/locale/oc-FR/list_scripts.intent @@ -0,0 +1,3 @@ +(lista|quinas son) las miás (alias|scripts|òrdres) +quins (scripts|comandas) (coneisses|son configurats) +quinas òrdres pòdes executar diff --git a/ovos_skill_cmd/locale/oc-FR/no.scripts.dialog b/ovos_skill_cmd/locale/oc-FR/no.scripts.dialog new file mode 100644 index 0000000..a6f06c2 --- /dev/null +++ b/ovos_skill_cmd/locale/oc-FR/no.scripts.dialog @@ -0,0 +1,2 @@ +Ai pas cap de script configurat +I a pas encara cap d'òrdre configurada diff --git a/ovos_skill_cmd/locale/oc-FR/running.dialog b/ovos_skill_cmd/locale/oc-FR/running.dialog new file mode 100644 index 0000000..0fc8241 --- /dev/null +++ b/ovos_skill_cmd/locale/oc-FR/running.dialog @@ -0,0 +1 @@ +s'executa l'(òrdre|script) {alias} diff --git a/ovos_skill_cmd/locale/oc-FR/skill.json b/ovos_skill_cmd/locale/oc-FR/skill.json new file mode 100644 index 0000000..f9e9ab0 --- /dev/null +++ b/ovos_skill_cmd/locale/oc-FR/skill.json @@ -0,0 +1,6 @@ +{ + "examples": [ + "executa l'òrdre ___", + "executa l'script ___" + ] +} diff --git a/ovos_skill_cmd/locale/oc-FR/unknown.script.dialog b/ovos_skill_cmd/locale/oc-FR/unknown.script.dialog new file mode 100644 index 0000000..47d5d57 --- /dev/null +++ b/ovos_skill_cmd/locale/oc-FR/unknown.script.dialog @@ -0,0 +1 @@ +Coneissi pas cap de script nomenat {name} diff --git a/ovos_skill_cmd/locale/pt-BR/Run.voc b/ovos_skill_cmd/locale/pt-BR/Run.voc new file mode 100644 index 0000000..170f496 --- /dev/null +++ b/ovos_skill_cmd/locale/pt-BR/Run.voc @@ -0,0 +1 @@ +(execute|executar|lance|lançar|inicie|iniciar) (comando|script) diff --git a/ovos_skill_cmd/locale/pt-BR/RunScriptCommandIntent.intent b/ovos_skill_cmd/locale/pt-BR/RunScriptCommandIntent.intent new file mode 100644 index 0000000..69ed413 --- /dev/null +++ b/ovos_skill_cmd/locale/pt-BR/RunScriptCommandIntent.intent @@ -0,0 +1 @@ +(execute|executar|lance|lançar|inicie|iniciar) (comando|script) {script} diff --git a/ovos_skill_cmd/locale/pt-BR/list_scripts.intent b/ovos_skill_cmd/locale/pt-BR/list_scripts.intent new file mode 100644 index 0000000..cdf9ce0 --- /dev/null +++ b/ovos_skill_cmd/locale/pt-BR/list_scripts.intent @@ -0,0 +1,3 @@ +(liste|quais são) meus (apelidos|scripts|comandos) +quais (scripts|comandos) (você conhece|estão configurados) +quais (scripts|comandos) você pode executar diff --git a/ovos_skill_cmd/locale/pt-BR/no.scripts.dialog b/ovos_skill_cmd/locale/pt-BR/no.scripts.dialog new file mode 100644 index 0000000..0153746 --- /dev/null +++ b/ovos_skill_cmd/locale/pt-BR/no.scripts.dialog @@ -0,0 +1,2 @@ +Eu não tenho nenhum script configurado +Nenhum comando está configurado ainda diff --git a/ovos_skill_cmd/locale/pt-BR/running.dialog b/ovos_skill_cmd/locale/pt-BR/running.dialog new file mode 100644 index 0000000..9aa5ec5 --- /dev/null +++ b/ovos_skill_cmd/locale/pt-BR/running.dialog @@ -0,0 +1 @@ +executando (comando|script) {alias} diff --git a/ovos_skill_cmd/locale/pt-BR/skill.json b/ovos_skill_cmd/locale/pt-BR/skill.json new file mode 100644 index 0000000..50a3301 --- /dev/null +++ b/ovos_skill_cmd/locale/pt-BR/skill.json @@ -0,0 +1,6 @@ +{ + "examples": [ + "executar comando ___", + "rodar script ___" + ] +} diff --git a/ovos_skill_cmd/locale/pt-BR/unknown.script.dialog b/ovos_skill_cmd/locale/pt-BR/unknown.script.dialog new file mode 100644 index 0000000..647ea21 --- /dev/null +++ b/ovos_skill_cmd/locale/pt-BR/unknown.script.dialog @@ -0,0 +1 @@ +Eu não conheço um script chamado {name} diff --git a/locale/pt-pt/Run.voc b/ovos_skill_cmd/locale/pt-PT/Run.voc similarity index 100% rename from locale/pt-pt/Run.voc rename to ovos_skill_cmd/locale/pt-PT/Run.voc diff --git a/ovos_skill_cmd/locale/pt-PT/RunScriptCommandIntent.intent b/ovos_skill_cmd/locale/pt-PT/RunScriptCommandIntent.intent new file mode 100644 index 0000000..5ca38bf --- /dev/null +++ b/ovos_skill_cmd/locale/pt-PT/RunScriptCommandIntent.intent @@ -0,0 +1 @@ +(executa[r]|lança[r]|inicia[r]) [o] (comando|script) {script} diff --git a/ovos_skill_cmd/locale/pt-PT/list_scripts.intent b/ovos_skill_cmd/locale/pt-PT/list_scripts.intent new file mode 100644 index 0000000..feec5ca --- /dev/null +++ b/ovos_skill_cmd/locale/pt-PT/list_scripts.intent @@ -0,0 +1,3 @@ +(lista|quais são) os meus (atalhos|scripts|comandos) +que (scripts|comandos) (conheces|estão configurados) +que (scripts|comandos) consegues executar diff --git a/ovos_skill_cmd/locale/pt-PT/no.scripts.dialog b/ovos_skill_cmd/locale/pt-PT/no.scripts.dialog new file mode 100644 index 0000000..17d58df --- /dev/null +++ b/ovos_skill_cmd/locale/pt-PT/no.scripts.dialog @@ -0,0 +1,2 @@ +Não tenho nenhum script configurado +Ainda não há comandos configurados diff --git a/locale/pt-pt/running.dialog b/ovos_skill_cmd/locale/pt-PT/running.dialog similarity index 100% rename from locale/pt-pt/running.dialog rename to ovos_skill_cmd/locale/pt-PT/running.dialog diff --git a/locale/pt-pt/skill.json b/ovos_skill_cmd/locale/pt-PT/skill.json similarity index 100% rename from locale/pt-pt/skill.json rename to ovos_skill_cmd/locale/pt-PT/skill.json diff --git a/ovos_skill_cmd/locale/pt-PT/unknown.script.dialog b/ovos_skill_cmd/locale/pt-PT/unknown.script.dialog new file mode 100644 index 0000000..e58a427 --- /dev/null +++ b/ovos_skill_cmd/locale/pt-PT/unknown.script.dialog @@ -0,0 +1 @@ +Não conheço nenhum script chamado {name} \ No newline at end of file diff --git a/ovos_skill_cmd/locale/sv-SE/Run.voc b/ovos_skill_cmd/locale/sv-SE/Run.voc new file mode 100644 index 0000000..9417a5d --- /dev/null +++ b/ovos_skill_cmd/locale/sv-SE/Run.voc @@ -0,0 +1 @@ +(kör|starta) (kommando|skript) diff --git a/ovos_skill_cmd/locale/sv-SE/RunScriptCommandIntent.intent b/ovos_skill_cmd/locale/sv-SE/RunScriptCommandIntent.intent new file mode 100644 index 0000000..59404ce --- /dev/null +++ b/ovos_skill_cmd/locale/sv-SE/RunScriptCommandIntent.intent @@ -0,0 +1 @@ +(kör|starta) (kommandot|skriptet) {script} diff --git a/ovos_skill_cmd/locale/sv-SE/list_scripts.intent b/ovos_skill_cmd/locale/sv-SE/list_scripts.intent new file mode 100644 index 0000000..f9d29a5 --- /dev/null +++ b/ovos_skill_cmd/locale/sv-SE/list_scripts.intent @@ -0,0 +1,3 @@ +(lista|vilka är) mina (alias|skript|kommandon) +vilka (skript|kommandon) (känner du till|är konfigurerade|finns) +vilka (skript|kommandon) kan du köra diff --git a/ovos_skill_cmd/locale/sv-SE/no.scripts.dialog b/ovos_skill_cmd/locale/sv-SE/no.scripts.dialog new file mode 100644 index 0000000..ca3982a --- /dev/null +++ b/ovos_skill_cmd/locale/sv-SE/no.scripts.dialog @@ -0,0 +1,2 @@ +Jag har inga skript konfigurerade +Inga kommandon är inställda än diff --git a/ovos_skill_cmd/locale/sv-SE/running.dialog b/ovos_skill_cmd/locale/sv-SE/running.dialog new file mode 100644 index 0000000..1de83c4 --- /dev/null +++ b/ovos_skill_cmd/locale/sv-SE/running.dialog @@ -0,0 +1 @@ +kör (kommandot|skriptet) {alias} diff --git a/ovos_skill_cmd/locale/sv-SE/skill.json b/ovos_skill_cmd/locale/sv-SE/skill.json new file mode 100644 index 0000000..83f0b1a --- /dev/null +++ b/ovos_skill_cmd/locale/sv-SE/skill.json @@ -0,0 +1,6 @@ +{ + "examples": [ + "starta kommandot ___", + "kör skriptet ___" + ] +} diff --git a/ovos_skill_cmd/locale/sv-SE/unknown.script.dialog b/ovos_skill_cmd/locale/sv-SE/unknown.script.dialog new file mode 100644 index 0000000..e5b1baa --- /dev/null +++ b/ovos_skill_cmd/locale/sv-SE/unknown.script.dialog @@ -0,0 +1 @@ +Jag känner inte till något skript med namnet {name} diff --git a/ovos_skill_cmd/version.py b/ovos_skill_cmd/version.py new file mode 100644 index 0000000..e002bf6 --- /dev/null +++ b/ovos_skill_cmd/version.py @@ -0,0 +1,8 @@ +# START_VERSION_BLOCK +VERSION_MAJOR = 0 +VERSION_MINOR = 7 +VERSION_BUILD = 0 +VERSION_ALPHA = 2 +# END_VERSION_BLOCK + +__version__ = f"{VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_BUILD}" + (f"a{VERSION_ALPHA}" if VERSION_ALPHA else "") diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f49e22d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,45 @@ +[build-system] +requires = ["setuptools>=61", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "ovos-skill-cmd" +dynamic = ["version"] +description = "OVOS run command skill" +readme = "README.md" +license = {text = "Apache-2.0"} +authors = [ + {name = "JarbasAi", email = "jarbasai@mailfence.com"} +] +keywords = ["ovos", "skill", "plugin"] +dependencies = [ + "ovos-plugin-manager>=2.1.0,<3.0.0", + "ovos-utils>=0.0.38,<1.0.0", + "ovos-workshop>=0.0.15,<10.0.0" +] + +[project.optional-dependencies] +test = [ + "ovoscope>=0.13.1,<1.0.0", + "pytest>=7.0.0,<9", + "pytest-timeout>=2.0.0" +] + +[project.urls] +Homepage = "https://github.com/OpenVoiceOS/ovos-skill-cmd" + +[project.entry-points."opm.skill"] +"ovos-skill-cmd.openvoiceos" = "ovos_skill_cmd:CmdSkill" + +[tool.setuptools.dynamic] +version = {attr = "ovos_skill_cmd.version.__version__"} + +[tool.setuptools.packages.find] +where = ["."] +include = ["ovos_skill_cmd*"] + +[tool.setuptools.package-data] +ovos_skill_cmd = [ + "*.json", + "locale/**/*", +] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index ecb2f7a..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -ovos-workshop>=0.0.15,<8.0.0 diff --git a/scripts/sync_translations.py b/scripts/sync_translations.py deleted file mode 100644 index 33831a0..0000000 --- a/scripts/sync_translations.py +++ /dev/null @@ -1,73 +0,0 @@ -"""this script should run in every PR originated from @gitlocalize-app -TODO - before PR merge -""" - -import json -from os.path import dirname -import os - -locale = f"{dirname(dirname(__file__))}/locale" -tx = f"{dirname(dirname(__file__))}/translations" - - -for lang in os.listdir(tx): - intents = f"{tx}/{lang}/intents.json" - dialogs = f"{tx}/{lang}/dialogs.json" - vocs = f"{tx}/{lang}/vocabs.json" - regexes = f"{tx}/{lang}/regexes.json" - - if os.path.isfile(intents): - with open(intents) as f: - data = json.load(f) - for fid, samples in data.items(): - if samples: - samples = [s.strip() for s in samples - if s and s.strip() != "[UNUSED]"] # s may be None - with open(f"{locale}/{lang.lower()}/{fid}", "w") as f: - f.write("\n".join(sorted(samples))) - - if os.path.isfile(dialogs): - with open(dialogs) as f: - data = json.load(f) - for fid, samples in data.items(): - if samples: - samples = list(set([s.strip() for s in samples - if s and s.strip() != "[UNUSED]"])) # s may be None - if fid.startswith("/"): - p = f"{locale}/{lang.lower()}{fid}" - else: - p = f"{locale}/{lang.lower()}/{fid}" - os.makedirs(os.path.dirname(p), exist_ok=True) - with open(f"{locale}/{lang.lower()}/{fid}", "w") as f: - f.write("\n".join(sorted(samples))) - - if os.path.isfile(vocs): - with open(vocs) as f: - data = json.load(f) - for fid, samples in data.items(): - if samples: - samples = list(set([s.strip() for s in samples - if s and s.strip() != "[UNUSED]"])) # s may be None - if fid.startswith("/"): - p = f"{locale}/{lang.lower()}{fid}" - else: - p = f"{locale}/{lang.lower()}/{fid}" - os.makedirs(os.path.dirname(p), exist_ok=True) - with open(f"{locale}/{lang.lower()}/{fid}", "w") as f: - f.write("\n".join(sorted(samples))) - - if os.path.isfile(regexes): - with open(regexes) as f: - data = json.load(f) - for fid, samples in data.items(): - if samples: - samples = list(set([s.strip() for s in samples - if s and s.strip() != "[UNUSED]"])) # s may be None - if fid.startswith("/"): - p = f"{locale}/{lang.lower()}{fid}" - else: - p = f"{locale}/{lang.lower()}/{fid}" - os.makedirs(os.path.dirname(p), exist_ok=True) - with open(f"{locale}/{lang.lower()}/{fid}", "w") as f: - f.write("\n".join(sorted(samples))) - diff --git a/setup.py b/setup.py deleted file mode 100644 index afecf88..0000000 --- a/setup.py +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env python3 -from setuptools import setup -from os.path import abspath, dirname, join, isfile, isdir -from os import walk, environ - -# Define package information -SKILL_CLAZZ = "CmdSkill" # Make sure it matches __init__.py class name -URL = "https://github.com/OpenVoiceOS/ovos-skill-cmd" -AUTHOR = "forslund" -EMAIL = "" -LICENSE = "Apache2.0" -DESCRIPTION = SKILL_CLAZZ # TODO - -PYPI_NAME = URL.split("/")[-1] # pip install PYPI_NAME - -# Construct entry point for plugin -SKILL_ID = f"{PYPI_NAME.lower()}.{AUTHOR.lower()}" -SKILL_PKG = PYPI_NAME.lower().replace('-', '_') -PLUGIN_ENTRY_POINT = f"{SKILL_ID}={SKILL_PKG}:{SKILL_CLAZZ}" - - -# Function to parse requirements from file -def get_requirements(requirements_filename: str = "requirements.txt"): - requirements_file = join(abspath(dirname(__file__)), requirements_filename) - if isfile(requirements_file): - with open(requirements_file, 'r', encoding='utf-8') as r: - requirements = r.readlines() - requirements = [r.strip() for r in requirements - if r.strip() and not r.strip().startswith("#")] - if 'MYCROFT_LOOSE_REQUIREMENTS' in environ: - print('USING LOOSE REQUIREMENTS!') - requirements = [r.replace('==', '>=').replace('~=', '>=') - for r in requirements] - return requirements - return [] - - -# Function to find resource files -def find_resource_files(): - resource_base_dirs = ["locale"] - base_dir = abspath(dirname(__file__)) - package_data = ["*.json"] - for res in resource_base_dirs: - if isdir(join(base_dir, res)): - for (directory, _, files) in walk(join(base_dir, res)): - if files: - p = join(directory.replace(base_dir, "").lstrip('/'), '*') - package_data.append(p) - return package_data - - -def get_version(): - """ Find the version of this skill""" - version_file = join(dirname(__file__), 'version.py') - major, minor, build, alpha = (None, None, None, None) - with open(version_file) as f: - for line in f: - if 'VERSION_MAJOR' in line: - major = line.split('=')[1].strip() - elif 'VERSION_MINOR' in line: - minor = line.split('=')[1].strip() - elif 'VERSION_BUILD' in line: - build = line.split('=')[1].strip() - elif 'VERSION_ALPHA' in line: - alpha = line.split('=')[1].strip() - - if ((major and minor and build and alpha) or - '# END_VERSION_BLOCK' in line): - break - version = f"{major}.{minor}.{build}" - if int(alpha): - version += f"a{alpha}" - return version - - -# Setup configuration -setup( - name=PYPI_NAME, - version=get_version(), - description=DESCRIPTION, - url=URL, - author=AUTHOR, - author_email=EMAIL, - license=LICENSE, - package_dir={SKILL_PKG: ""}, - package_data={SKILL_PKG: find_resource_files()}, - packages=[SKILL_PKG], - include_package_data=True, - install_requires=get_requirements(), - keywords='ovos skill plugin', - entry_points={'ovos.plugin.skill': PLUGIN_ENTRY_POINT} -) diff --git a/test/end2end/golden_utterances.jsonl b/test/end2end/golden_utterances.jsonl new file mode 100644 index 0000000..ced72e3 --- /dev/null +++ b/test/end2end/golden_utterances.jsonl @@ -0,0 +1,12 @@ +{"skill_id": "ovos-skill-cmd.openvoiceos", "utterance": "run command backup", "intent_method": "run", "intent_label": "RunScriptCommandIntent", "intent_type": "adapt", "expected_messages": ["speak"], "needs_manual": false} +{"skill_id": "ovos-skill-cmd.openvoiceos", "utterance": "execute script weather", "intent_method": "run", "intent_label": "RunScriptCommandIntent", "intent_type": "adapt", "expected_messages": ["speak"], "needs_manual": false} +{"skill_id": "ovos-skill-cmd.openvoiceos", "utterance": "launch command backup", "intent_method": "run", "intent_label": "RunScriptCommandIntent", "intent_type": "adapt", "expected_messages": ["speak"], "needs_manual": false} +{"skill_id": "ovos-skill-cmd.openvoiceos", "utterance": "run script weather", "intent_method": "run", "intent_label": "RunScriptCommandIntent", "intent_type": "adapt", "expected_messages": ["speak"], "needs_manual": false} +{"skill_id": "ovos-skill-cmd.openvoiceos", "utterance": "execute command backup", "intent_method": "run", "intent_label": "RunScriptCommandIntent", "intent_type": "adapt", "expected_messages": ["speak"], "needs_manual": false} +{"skill_id": "ovos-skill-cmd.openvoiceos", "utterance": "launch script weather", "intent_method": "run", "intent_label": "RunScriptCommandIntent", "intent_type": "adapt", "expected_messages": ["speak"], "needs_manual": false} +{"skill_id": "ovos-skill-cmd.openvoiceos", "utterance": "please run command backup for me", "intent_method": "run", "intent_label": "RunScriptCommandIntent", "intent_type": "adapt", "expected_messages": ["speak"], "needs_manual": true} +{"skill_id": "ovos-skill-cmd.openvoiceos", "utterance": "can you execute script weather now", "intent_method": "run", "intent_label": "RunScriptCommandIntent", "intent_type": "adapt", "expected_messages": ["speak"], "needs_manual": true} +{"skill_id": "ovos-skill-cmd.openvoiceos", "utterance": "run command", "intent_method": "run", "intent_label": "RunScriptCommandIntent", "intent_type": "adapt", "expected_messages": ["speak"], "needs_manual": true} +{"skill_id": "ovos-skill-cmd.openvoiceos", "utterance": "list my aliases", "intent_method": "handle_list_scripts", "intent_label": "list_scripts", "intent_type": "padatious", "expected_messages": ["speak"], "needs_manual": false} +{"skill_id": "ovos-skill-cmd.openvoiceos", "utterance": "what commands are configured", "intent_method": "handle_list_scripts", "intent_label": "list_scripts", "intent_type": "padatious", "expected_messages": ["speak"], "needs_manual": false} +{"skill_id": "ovos-skill-cmd.openvoiceos", "utterance": "which scripts can you run", "intent_method": "handle_list_scripts", "intent_label": "list_scripts", "intent_type": "padatious", "expected_messages": ["speak"], "needs_manual": false} diff --git a/test/end2end/test_golden_utterances.py b/test/end2end/test_golden_utterances.py new file mode 100644 index 0000000..d9ef1ea --- /dev/null +++ b/test/end2end/test_golden_utterances.py @@ -0,0 +1,171 @@ +"""Golden-utterance end-to-end coverage for ovos-skill-cmd (en-US). + +The golden corpus (``golden_utterances.jsonl``) is a vendored slice of the +shared ovoscope golden-utterance dataset (skill_id +"ovos-skill-cmd.openvoiceos"), supplemented with rows derived from this +skill's own ``RunScriptCommandIntent.intent``/``skill.json`` templates: the +master corpus carries only one bare stub row ("run command") for this +skill, which cannot route on its own because ``RunScriptCommandIntent`` is +trained from ``RunScriptCommandIntent.intent`` and needs the ``{script}`` +slot filled by a dynamically-registered Padatious entity (see +``initialize()`` in ``ovos_skill_cmd/__init__.py``). That stub is kept here +with ``needs_manual: true`` (flagged, not deleted) as a finding for the +master corpus: it needs an alias suffix (e.g. "run command backup") to be a +valid routable row. + +A settings file seeding two aliases ("backup", "weather") is written under a +private XDG config root before the MiniCroft loads the skill, matching +``test_intents_en_us.py``'s existing mechanism. + +Run: + uv run pytest test/end2end/ -v +""" +import json +import os +import tempfile +from pathlib import Path + +import pytest + +SKILL_ID = "ovos-skill-cmd.openvoiceos" +LANG = "en-US" +ALIASES = {"backup": "echo backup", "weather": "echo weather"} + + +def _seed_settings() -> None: + root = tempfile.mkdtemp(prefix="ovos-skill-cmd-golden-") + os.environ["XDG_CONFIG_HOME"] = os.path.join(root, "config") + settings_dir = os.path.join(root, "config", "mycroft", "skills", SKILL_ID) + os.makedirs(settings_dir, exist_ok=True) + with open(os.path.join(settings_dir, "settings.json"), "w") as handle: + json.dump({"alias": ALIASES, "__mycroft_skill_firstrun": False}, handle) + + +_seed_settings() + +from ovos_bus_client.message import Message # noqa: E402 +from ovos_bus_client.session import Session # noqa: E402 +from ovoscope import PADACIOSO_PIPELINE, CaptureSession, get_minicroft # noqa: E402 + +GOLDEN_PATH = Path(__file__).parent / "golden_utterances.jsonl" + +# Confusables lifted from other skills' domains, picked for lexical overlap +# with "run"/"execute"/"launch"/"command"/"script". +NEGATIVE_UTTERANCES = [ + ("what's the weather", "ovos-skill-weather.openvoiceos"), + ("play some music", "ovos-skill-music.openvoiceos"), + ("launch a timer for 5 minutes", "ovos-skill-alerts.openvoiceos"), + ("run the news for me", "ovos-skill-news.openvoiceos"), + ("execute a wolfram alpha search", "ovos-skill-wolfie.openvoiceos"), + ("tell me a joke", "skill-icanhazdadjokes.openvoiceos"), + ("search the web for cats", "ovos-skill-ddg.openvoiceos"), +] + + +_NEEDS_MANUAL_REASONS = { + "run command": ( + "bare master-corpus stub with no script alias suffix -- " + "RunScriptCommandIntent is trained from RunScriptCommandIntent.intent " + "and needs the {script} slot filled by a dynamically-registered " + "Padatious entity (see initialize() in " + "ovos_skill_cmd/__init__.py), so this utterance alone can never " + "route. Kept as a flagged row (not deleted) for master-corpus " + "absorption: needs an alias suffix, e.g. 'run command backup'." + ), + "please run command backup for me": ( + "RunScriptCommandIntent.intent is a fixed Padatious template with no " + "filler-word tolerance; the prior Adapt keyword match ('Run' + " + "'Script' present anywhere in the utterance) routed this regardless " + "of the surrounding 'please ... for me', which the .intent template " + "does not model. Kept as a flagged row (not deleted) for " + "master-corpus absorption." + ), + "can you execute script weather now": ( + "RunScriptCommandIntent.intent is a fixed Padatious template with no " + "filler-word tolerance; the prior Adapt keyword match ('Run' + " + "'Script' present anywhere in the utterance) routed this regardless " + "of the surrounding 'can you ... now', which the .intent template " + "does not model. Kept as a flagged row (not deleted) for " + "master-corpus absorption." + ), +} + + +def _load_golden_rows(): + rows = [] + with open(GOLDEN_PATH, encoding="utf-8") as f: + for line in f: + line = line.strip() + if not line: + continue + rows.append(json.loads(line)) + return rows + + +def _as_param(row): + if row.get("needs_manual"): + reason = _NEEDS_MANUAL_REASONS.get(row["utterance"]) + assert reason, f"missing _NEEDS_MANUAL_REASONS entry for {row['utterance']!r}" + return pytest.param(row, id=row["utterance"], marks=pytest.mark.xfail(strict=True, reason=reason)) + return pytest.param(row, id=row["utterance"]) + + +GOLDEN_ROWS = [_as_param(r) for r in _load_golden_rows()] + + +@pytest.fixture(scope="module") +def minicroft(): + mc = get_minicroft([SKILL_ID]) + yield mc + mc.stop() + + +def _capture(mc, text, session_id): + session = Session(session_id) + session.lang = LANG + session.pipeline = PADACIOSO_PIPELINE + utterance = Message( + "recognizer_loop:utterance", + {"utterances": [text], "lang": LANG}, + {"session": session.serialize(), "source": "A", "destination": "B"}, + ) + capture = CaptureSession(mc) + capture.capture(utterance, timeout=30) + return capture.finish() + + +@pytest.mark.timeout(60) +@pytest.mark.parametrize("row", GOLDEN_ROWS, ids=lambda r: r["utterance"]) +def test_golden_utterance(minicroft, row): + # NOTE: on this ovos-core/ovos-bus-client version, the Padatious pipeline + # emits the matched intent directly as msg_type + # ":" (observed via capture below) rather than a + # generic "ovos.intent.matched" wrapper carrying data.intent_name -- the + # latter is not present in this message stream. Assert against the + # observed msg_type, matching the pattern already used by + # ovos-skill-volume/ovos-skill-weather's golden suites. + expected_intent = f"{SKILL_ID}:{row['intent_label']}" + messages = _capture(minicroft, row["utterance"], f"golden-{row['utterance']}") + types = [m.msg_type for m in messages] + assert expected_intent in types, ( + f"{row['utterance']!r}: expected {expected_intent!r} in message types, got {types!r}" + ) + + +@pytest.mark.timeout(60) +@pytest.mark.parametrize("negative", NEGATIVE_UTTERANCES, ids=lambda n: n[0]) +def test_negative_confusable_not_claimed(minicroft, negative): + # NOTE: this repo's ovos-core/ovos-bus-client version never emits a + # generic "ovos.intent.matched" wrapper message (see the identical note + # on test_golden_utterance above) -- filtering on that msg_type here + # made this assertion vacuously true (`assert not []` always passes) + # regardless of what the skill actually claimed. Confirmed by mutation: + # feeding this check one of this suite's own golden rows as a + # "negative" still passed. Fixed to key on the observed + # ":" msg_type shape directly, matching the + # positive assertion above (and ddg/personal's negative checks). + text, source_skill = negative + messages = _capture(minicroft, text, f"negative-{text}") + types = [m.msg_type for m in messages] + claimed = any(t.startswith(f"{SKILL_ID}:") for t in types) + assert not claimed, f"{text!r} (from {source_skill}) was incorrectly claimed by {SKILL_ID}: {types!r}" diff --git a/test/end2end/test_intent_file_migration.py b/test/end2end/test_intent_file_migration.py new file mode 100644 index 0000000..95a0d11 --- /dev/null +++ b/test/end2end/test_intent_file_migration.py @@ -0,0 +1,89 @@ +"""End-to-end proof that ``RunScriptCommandIntent`` is a Padatious ``.intent`` +match, not an Adapt keyword match. + +The session pipeline is pinned to ``PADACIOSO_PIPELINE`` only (Adapt excluded), +so this test can only pass if the intent is trained from +``RunScriptCommandIntent.intent`` and the ``{script}`` slot is populated from +a dynamically registered Padatious entity (the skill's ``alias`` setting). + +Run: + uv run pytest test/end2end/test_intent_file_migration.py -v +""" +import json +import os +import tempfile +from unittest import TestCase + +SKILL_ID = "ovos-skill-cmd.openvoiceos" +LANG = "en-US" +RUN_INTENT = f"{SKILL_ID}:RunScriptCommandIntent" +ALIASES = {"backup": "echo backup"} + + +def _seed_settings() -> None: + root = tempfile.mkdtemp(prefix="ovos-skill-cmd-e2e-intentfile-") + os.environ["XDG_CONFIG_HOME"] = os.path.join(root, "config") + settings_dir = os.path.join(root, "config", "mycroft", "skills", SKILL_ID) + os.makedirs(settings_dir, exist_ok=True) + with open(os.path.join(settings_dir, "settings.json"), "w") as handle: + json.dump( + {"alias": ALIASES, "__mycroft_skill_firstrun": False}, handle + ) + + +_seed_settings() + +from ovos_bus_client.message import Message # noqa: E402 +from ovos_bus_client.session import Session # noqa: E402 +from ovoscope import get_minicroft, CaptureSession, PADACIOSO_PIPELINE # noqa: E402 + + +def _session(tag: str) -> Session: + session = Session(f"e2e-en_us-cmd-intentfile-{tag}") + session.lang = LANG + session.pipeline = PADACIOSO_PIPELINE + return session + + +def _utterance(utt: str, session: Session) -> Message: + return Message( + "recognizer_loop:utterance", + {"utterances": [utt], "lang": LANG}, + {"session": session.serialize(), "source": "A", "destination": "B"}, + ) + + +class TestRunScriptCommandIntentFile(TestCase): + """RunScriptCommandIntent must route via Padatious/.intent alone.""" + + @classmethod + def setUpClass(cls): + cls.minicroft = get_minicroft([SKILL_ID]) + + @classmethod + def tearDownClass(cls): + if getattr(cls, "minicroft", None): + cls.minicroft.stop() + + def test_run_script_backup_padatious_only(self): + session = _session("run-script-backup") + capture = CaptureSession(self.minicroft) + capture.capture( + _utterance("run script backup", session), timeout=30 + ) + messages = capture.finish() + types = [m.msg_type for m in messages] + self.assertIn( + RUN_INTENT, types, + f"expected {RUN_INTENT!r} to be matched via the Adapt-less " + f"(Padatious-only) pipeline for 'run script backup', got {types}", + ) + spoken = [ + m.data.get("utterance", "") + for m in messages + if m.msg_type in ("speak", "ovos.utterance.speak") + ] + self.assertTrue( + any("running backup" in utt for utt in spoken), + f"expected a spoken response mentioning 'backup', got {spoken}", + ) diff --git a/test/end2end/test_intents_en_us.py b/test/end2end/test_intents_en_us.py new file mode 100644 index 0000000..44b87f3 --- /dev/null +++ b/test/end2end/test_intents_en_us.py @@ -0,0 +1,134 @@ +"""End-to-end intent-routing tests for ovos-skill-cmd (en-US). + +These assert *per-utterance* that the Padatious pipeline routes an utterance +to the ``RunScriptCommandIntent`` handler and that the skill speaks the +configured alias back. They deliberately use subset assertions over the +captured message stream rather than a strict full-sequence match: the exact +ordered sequence drifts across ovos-core / ovoscope releases (e.g. an extra +``ovos.intent.matched`` message, or ``speak`` vs ``ovos.utterance.speak``), +which is orthogonal to what this skill is responsible for. + +``RunScriptCommandIntent`` is trained from ``RunScriptCommandIntent.intent``, +with the ``{script}`` slot filled from a Padatious entity that is registered +dynamically from the skill's ``alias`` setting. The suite therefore seeds a +settings file with a couple of aliases under a private XDG config root +*before* the MiniCroft loads the skill, so ``initialize()`` registers the +matching entity. + +Run: + uv run pytest test/end2end/ -v +""" +import json +import os +import tempfile +from unittest import TestCase + +SKILL_ID = "ovos-skill-cmd.openvoiceos" +LANG = "en-US" +RUN_INTENT = f"{SKILL_ID}:RunScriptCommandIntent" +ALIASES = {"backup": "echo backup", "weather": "echo weather"} + + +def _seed_settings() -> None: + """Write a skill settings file with aliases under a private XDG root. + + Must run before the MiniCroft loads the skill so ``initialize()`` picks up + the aliases and registers the ``script`` entity the intent requires. + """ + root = tempfile.mkdtemp(prefix="ovos-skill-cmd-e2e-") + os.environ["XDG_CONFIG_HOME"] = os.path.join(root, "config") + settings_dir = os.path.join(root, "config", "mycroft", "skills", SKILL_ID) + os.makedirs(settings_dir, exist_ok=True) + with open(os.path.join(settings_dir, "settings.json"), "w") as handle: + json.dump( + {"alias": ALIASES, "__mycroft_skill_firstrun": False}, handle + ) + + +_seed_settings() + +from ovos_bus_client.message import Message # noqa: E402 +from ovos_bus_client.session import Session # noqa: E402 +from ovoscope import get_minicroft, CaptureSession, PADACIOSO_PIPELINE # noqa: E402 + + +def _session(tag: str) -> Session: + session = Session(f"e2e-en_us-cmd-{tag}") + session.lang = LANG + session.pipeline = PADACIOSO_PIPELINE + return session + + +def _utterance(utt: str, session: Session) -> Message: + return Message( + "recognizer_loop:utterance", + {"utterances": [utt], "lang": LANG}, + {"session": session.serialize(), "source": "A", "destination": "B"}, + ) + + +class _RunRoutingMixin: + """Shared MiniCroft wiring for the cmd skill.""" + + @classmethod + def setUpClass(cls): + cls.minicroft = get_minicroft([SKILL_ID]) + + @classmethod + def tearDownClass(cls): + if getattr(cls, "minicroft", None): + cls.minicroft.stop() + + def _capture(self, utterance: str): + session = _session(str(hash(utterance))) + capture = CaptureSession(self.minicroft) + capture.capture(_utterance(utterance, session), timeout=30) + return capture.finish() + + def _spoken(self, messages): + return [ + m.data.get("utterance", "") + for m in messages + if m.msg_type in ("speak", "ovos.utterance.speak") + ] + + def assertRoutesToRun(self, utterance: str, alias: str): + messages = self._capture(utterance) + types = [m.msg_type for m in messages] + self.assertIn( + RUN_INTENT, types, + f"expected {RUN_INTENT!r} to be matched for {utterance!r}, " + f"got {types}", + ) + spoken = self._spoken(messages) + self.assertTrue( + any(f"running {alias}" in utt for utt in spoken), + f"expected a spoken response mentioning {alias!r} for " + f"{utterance!r}, got {spoken}", + ) + + +class TestRunScriptCommandIntent(_RunRoutingMixin, TestCase): + """RunScriptCommandIntent routes across the RunScriptCommandIntent.intent phrasings.""" + + def test_run_command_alias(self): + self.assertRoutesToRun("run command backup", "backup") + + def test_execute_script_alias(self): + self.assertRoutesToRun("execute script weather", "weather") + + def test_launch_command_alias(self): + self.assertRoutesToRun("launch command backup", "backup") + + def test_unknown_alias_does_not_run(self): + messages = self._capture("run script that does not exist") + self.assertIn(RUN_INTENT, [m.msg_type for m in messages]) + spoken = self._spoken(messages) + self.assertTrue( + any("don't know a script" in utt for utt in spoken), + f"expected the unknown-script dialog to be spoken, got {spoken}", + ) + self.assertFalse( + any("running" in utt for utt in spoken), + f"did not expect the running dialog to be spoken, got {spoken}", + ) diff --git a/test/end2end/test_list_scripts_intent_en_us.py b/test/end2end/test_list_scripts_intent_en_us.py new file mode 100644 index 0000000..fdc6b1c --- /dev/null +++ b/test/end2end/test_list_scripts_intent_en_us.py @@ -0,0 +1,99 @@ +"""End-to-end intent-routing tests for the ``list_scripts`` intent (en-US). + +Asserts that phrasings from ``list_scripts.intent`` route to +``handle_list_scripts`` and that the skill speaks back the configured alias +names when present, or the ``no.scripts`` dialog when the ``alias`` setting +is empty. + +Run: + uv run pytest test/end2end/test_list_scripts_intent_en_us.py -v +""" +import json +import os +import tempfile +from unittest import TestCase + +SKILL_ID = "ovos-skill-cmd.openvoiceos" +LANG = "en-US" +LIST_INTENT = f"{SKILL_ID}:list_scripts" +ALIASES = {"backup": "echo backup", "weather": "echo weather"} + + +def _seed_settings(alias) -> None: + root = tempfile.mkdtemp(prefix="ovos-skill-cmd-e2e-list-") + os.environ["XDG_CONFIG_HOME"] = os.path.join(root, "config") + settings_dir = os.path.join(root, "config", "mycroft", "skills", SKILL_ID) + os.makedirs(settings_dir, exist_ok=True) + with open(os.path.join(settings_dir, "settings.json"), "w") as handle: + json.dump({"alias": alias, "__mycroft_skill_firstrun": False}, handle) + + +from ovos_bus_client.message import Message # noqa: E402 +from ovos_bus_client.session import Session # noqa: E402 +from ovoscope import get_minicroft, CaptureSession, PADACIOSO_PIPELINE # noqa: E402 + + +def _session(tag: str) -> Session: + session = Session(f"e2e-en_us-cmd-list-{tag}") + session.lang = LANG + session.pipeline = PADACIOSO_PIPELINE + return session + + +def _utterance(utt: str, session: Session) -> Message: + return Message( + "recognizer_loop:utterance", + {"utterances": [utt], "lang": LANG}, + {"session": session.serialize(), "source": "A", "destination": "B"}, + ) + + +class TestListScriptsIntent(TestCase): + """list_scripts.intent routes across its phrasings and speaks the aliases.""" + + @classmethod + def setUpClass(cls): + _seed_settings(ALIASES) + cls.minicroft = get_minicroft([SKILL_ID]) + + @classmethod + def tearDownClass(cls): + if getattr(cls, "minicroft", None): + cls.minicroft.stop() + + def _capture(self, utterance: str): + session = _session(str(hash(utterance))) + capture = CaptureSession(self.minicroft) + capture.capture(_utterance(utterance, session), timeout=30) + return capture.finish() + + def _spoken(self, messages): + return [ + m.data.get("utterance", "") + for m in messages + if m.msg_type in ("speak", "ovos.utterance.speak") + ] + + def assertRoutesToList(self, utterance: str): + messages = self._capture(utterance) + types = [m.msg_type for m in messages] + self.assertIn( + LIST_INTENT, types, + f"expected {LIST_INTENT!r} to be matched for {utterance!r}, " + f"got {types}", + ) + spoken = self._spoken(messages) + self.assertTrue( + any("backup" in utt and "weather" in utt for utt in spoken), + f"expected a spoken response mentioning both aliases for " + f"{utterance!r}, got {spoken}", + ) + + def test_list_my_aliases(self): + self.assertRoutesToList("list my aliases") + + def test_what_scripts_do_you_know(self): + self.assertRoutesToList("what scripts do you know") + + def test_which_commands_can_you_run(self): + self.assertRoutesToList("which commands can you run") diff --git a/test/end2end/test_list_scripts_no_alias_en_us.py b/test/end2end/test_list_scripts_no_alias_en_us.py new file mode 100644 index 0000000..7dea486 --- /dev/null +++ b/test/end2end/test_list_scripts_no_alias_en_us.py @@ -0,0 +1,67 @@ +"""End-to-end coverage for ``list_scripts`` when no alias is configured.""" +import json +import os +import tempfile +from unittest import TestCase + +SKILL_ID = "ovos-skill-cmd.openvoiceos" +LANG = "en-US" +LIST_INTENT = f"{SKILL_ID}:list_scripts" + + +def _seed_settings() -> None: + root = tempfile.mkdtemp(prefix="ovos-skill-cmd-e2e-list-empty-") + os.environ["XDG_CONFIG_HOME"] = os.path.join(root, "config") + settings_dir = os.path.join(root, "config", "mycroft", "skills", SKILL_ID) + os.makedirs(settings_dir, exist_ok=True) + with open(os.path.join(settings_dir, "settings.json"), "w") as handle: + json.dump({"alias": {}, "__mycroft_skill_firstrun": False}, handle) + + +from ovos_bus_client.message import Message # noqa: E402 +from ovos_bus_client.session import Session # noqa: E402 +from ovoscope import get_minicroft, CaptureSession, PADACIOSO_PIPELINE # noqa: E402 + + +class TestListScriptsIntentNoAlias(TestCase): + """list_scripts.intent speaks the no.scripts dialog when unconfigured.""" + + @classmethod + def setUpClass(cls): + _seed_settings() + cls.minicroft = get_minicroft([SKILL_ID]) + + @classmethod + def tearDownClass(cls): + if getattr(cls, "minicroft", None): + cls.minicroft.stop() + + def test_no_scripts_configured(self): + session = Session("e2e-en_us-cmd-list-empty") + session.lang = LANG + session.pipeline = PADACIOSO_PIPELINE + capture = CaptureSession(self.minicroft) + capture.capture( + Message( + "recognizer_loop:utterance", + {"utterances": ["list my aliases"], "lang": LANG}, + {"session": session.serialize(), "source": "A", "destination": "B"}, + ), + timeout=30, + ) + messages = capture.finish() + types = [m.msg_type for m in messages] + self.assertIn(LIST_INTENT, types) + spoken = [ + m.data.get("utterance", "") + for m in messages + if m.msg_type in ("speak", "ovos.utterance.speak") + ] + self.assertTrue( + any( + "don't have any scripts configured" in utt + or "No commands are set up yet" in utt + for utt in spoken + ), + f"expected the no.scripts dialog to be spoken, got {spoken}", + ) diff --git a/test/test_plugin.py b/test/unittests/test_plugin.py similarity index 100% rename from test/test_plugin.py rename to test/unittests/test_plugin.py diff --git a/test/test_skill_loading.py b/test/unittests/test_skill_loading.py similarity index 96% rename from test/test_skill_loading.py rename to test/unittests/test_skill_loading.py index 6d5143a..2edb5be 100644 --- a/test/test_skill_loading.py +++ b/test/unittests/test_skill_loading.py @@ -4,6 +4,7 @@ from ovos_plugin_manager.skills import find_skill_plugins from ovos_utils.messagebus import FakeBus from ovos_workshop.skill_launcher import PluginSkillLoader, SkillLoader +import ovos_skill_cmd from ovos_skill_cmd import CmdSkill @@ -11,7 +12,7 @@ class TestSkillLoading(unittest.TestCase): @classmethod def setUpClass(self): self.skill_id = "ovos-skill-cmd.openvoiceos" - self.path = dirname(dirname(__file__)) + self.path = dirname(ovos_skill_cmd.__file__) def test_from_class(self): bus = FakeBus() diff --git a/translations/ca-es/dialogs.json b/translations/ca-es/dialogs.json deleted file mode 100644 index 8bee48c..0000000 --- a/translations/ca-es/dialogs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "running.dialog": [ - "s'està executant l'(ordre|script) {alias}" - ] -} diff --git a/translations/ca-es/vocabs.json b/translations/ca-es/vocabs.json deleted file mode 100644 index c15d134..0000000 --- a/translations/ca-es/vocabs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "Run.voc": [ - "(executa|llança|inicia) (un |una |la|l'|sa |s')(ordre|orde|comanda|script)" - ] -} diff --git a/translations/da/dialogs.json b/translations/da/dialogs.json deleted file mode 100644 index 7a33274..0000000 --- a/translations/da/dialogs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "running.dialog": [ - "kører {alias} (kommando|script)" - ] -} diff --git a/translations/da/vocabs.json b/translations/da/vocabs.json deleted file mode 100644 index 3ca9aa1..0000000 --- a/translations/da/vocabs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "Run.voc": [ - "(kør|udfør|start) (kommando|script)" - ] -} diff --git a/translations/de-de/dialogs.json b/translations/de-de/dialogs.json deleted file mode 100644 index c577bd3..0000000 --- a/translations/de-de/dialogs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "running.dialog": [ - "führe {alias} (Befehl|Skript) aus" - ] -} diff --git a/translations/de-de/vocabs.json b/translations/de-de/vocabs.json deleted file mode 100644 index f3e4a85..0000000 --- a/translations/de-de/vocabs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "Run.voc": [ - "starte (Befehl|Skript)\nführe (Befehl|Skript) aus\n(Skript|Befehl) ausführen1\n(Skript|Befehl) starten" - ] -} diff --git a/translations/en-us/dialogs.json b/translations/en-us/dialogs.json deleted file mode 100644 index 72e8a6d..0000000 --- a/translations/en-us/dialogs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "running.dialog": [ - "running {alias} (command|script)" - ] -} \ No newline at end of file diff --git a/translations/en-us/vocabs.json b/translations/en-us/vocabs.json deleted file mode 100644 index 3d79c35..0000000 --- a/translations/en-us/vocabs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "Run.voc": [ - "(run|execute|launch) (command|script)" - ] -} \ No newline at end of file diff --git a/translations/eu/vocabs.json b/translations/eu/vocabs.json deleted file mode 100644 index 4a0fe61..0000000 --- a/translations/eu/vocabs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "Run.voc": [ - "(jarri martxan|exekutatu|abiarazi) (komandoa|script-a)" - ] -} \ No newline at end of file diff --git a/translations/gl-es/dialogs.json b/translations/gl-es/dialogs.json deleted file mode 100644 index 84c5254..0000000 --- a/translations/gl-es/dialogs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "running.dialog": [ - "executando {alias} (comando|script)" - ] -} diff --git a/translations/gl-es/vocabs.json b/translations/gl-es/vocabs.json deleted file mode 100644 index e1cc523..0000000 --- a/translations/gl-es/vocabs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "Run.voc": [ - "(executar|arrincar) (comando|script)" - ] -} \ No newline at end of file diff --git a/translations/it-it/vocabs.json b/translations/it-it/vocabs.json deleted file mode 100644 index d2d297e..0000000 --- a/translations/it-it/vocabs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "Run.voc": [ - "(avvia|esegui|lancia|apri) (un|uno|il|lo|) (comando|script|codice)" - ] -} diff --git a/translations/pt-pt/dialogs.json b/translations/pt-pt/dialogs.json deleted file mode 100644 index 5b7175d..0000000 --- a/translations/pt-pt/dialogs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "running.dialog": [ - "a executar (comando|script) {alias}" - ] -} diff --git a/translations/pt-pt/vocabs.json b/translations/pt-pt/vocabs.json deleted file mode 100644 index c4f3300..0000000 --- a/translations/pt-pt/vocabs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "Run.voc": [ - "(executar|lançar|iniciar) (comando|script)" - ] -} diff --git a/version.py b/version.py deleted file mode 100644 index afedc35..0000000 --- a/version.py +++ /dev/null @@ -1,6 +0,0 @@ -# START_VERSION_BLOCK -VERSION_MAJOR = 0 -VERSION_MINOR = 2 -VERSION_BUILD = 11 -VERSION_ALPHA = 0 -# END_VERSION_BLOCK