diff --git a/.github/assets/logo/logo-dark.png b/.github/assets/logo/logo-dark.png new file mode 100644 index 0000000..4893136 Binary files /dev/null and b/.github/assets/logo/logo-dark.png differ diff --git a/.github/assets/logo/logo-light.png b/.github/assets/logo/logo-light.png new file mode 100644 index 0000000..4893136 Binary files /dev/null and b/.github/assets/logo/logo-light.png differ diff --git a/.github/assets/social-preview/social-preview.png b/.github/assets/social-preview/social-preview.png new file mode 100644 index 0000000..aefa0bc Binary files /dev/null and b/.github/assets/social-preview/social-preview.png differ diff --git a/.github/brand/brand.lock.json b/.github/brand/brand.lock.json deleted file mode 100644 index 7f0e937..0000000 --- a/.github/brand/brand.lock.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "schema_version": 2, - "authority": "https://github.com/computer-mcp/.github", - "revision": "2d9913a442afcb22faca90f5272d3988eb961be6e858b71c5067303cf7c627d6", - "consumer": "plugin-computer-use", - "files": { - "Documentation/Brand/header-light.png": "25ff7aa6d8201d288ee575f5e90fdfe104c10d2c7f1b66e6cfa35a7b04f97b65", - "Documentation/Brand/header-dark.png": "38ed51e1ebeefadf2ff1c7dce4a387738bffae38eeb53bddc38fc484dedc9559", - ".github/brand/social.png": "c5290a1b0895e351f38f21ef7dd2a6b304053bc4bb02bd408495b852aca8babb" - }, - "entries": { - "README.md": [ - "Computer Use", - "Documentation/Brand/header-light.png", - "Documentation/Brand/header-dark.png" - ] - } -} diff --git a/.github/brand/social.png b/.github/brand/social.png deleted file mode 100644 index 375ecbc..0000000 Binary files a/.github/brand/social.png and /dev/null differ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 74e04aa..8e44366 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,13 +1,14 @@ name: CI on: pull_request: + types: [opened, synchronize, reopened] push: branches: [master] workflow_dispatch: permissions: contents: read concurrency: - group: ci-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: package: @@ -22,8 +23,8 @@ jobs: python-version: '3.13' - name: Verify deterministic declaration package run: | - python3 -m unittest discover -s Tests -p 'test_*.py' - python3 Scripts/build_package.py dist > package-receipt.json + python3 -m unittest discover -s tests -p 'test_*.py' + python3 scripts/build_package.py dist > package-receipt.json - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: computer-use-plugin-${{ github.sha }} @@ -44,7 +45,11 @@ jobs: python-version: '3.11' - name: Verify the oldest supported build interpreter run: | - python3 -m unittest discover -s Tests -p 'test_*.py' - python3 Scripts/build_package.py dist - brand: - uses: computer-mcp/.github/.github/workflows/brand-check.yml@f402911abcdfb87ef0c41a2759e8c51387b18935 # master + python3 -m unittest discover -s tests -p 'test_*.py' + python3 scripts/build_package.py dist + validate: + if: always() + needs: [package, minimum-python] + uses: computer-mcp/github-workflows/.github/workflows/result.yml@d65bc20504f39c3a30232d67043abeca43d2c7b1 + with: + needs-json: ${{ toJSON(needs) }} diff --git a/.github/workflows/notify-catalog.yml b/.github/workflows/notify-catalog.yml index 74fc389..27da391 100644 --- a/.github/workflows/notify-catalog.yml +++ b/.github/workflows/notify-catalog.yml @@ -23,7 +23,7 @@ jobs: steps: - name: Request complete catalog reconciliation id: catalog - uses: computer-mcp/computer-mcp.github.io/.github/actions/notify-catalog@cda692839487f03a2b4740d945f9a8bd818554e5 + uses: computer-mcp/github-workflows/actions/notify-catalog@d65bc20504f39c3a30232d67043abeca43d2c7b1 with: client-id: ${{ vars.CATALOG_APP_CLIENT_ID }} private-key: ${{ secrets.CATALOG_APP_PRIVATE_KEY }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 549d87b..74077b4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,11 +16,11 @@ jobs: permissions: contents: write actions: read - uses: computer-mcp/.github/.github/workflows/plugin-release.yml@35766b350427ed900b456aa3c39098e97d9d3aa1 # master + uses: computer-mcp/github-workflows/.github/workflows/plugin-release.yml@d65bc20504f39c3a30232d67043abeca43d2c7b1 with: ref: ${{ inputs.ref }} publish: ${{ inputs.publish }} - build: Scripts/build-package.sh + build: scripts/build-package.sh ci_artifact: computer-use-plugin-${commit} notify-catalog: needs: release diff --git a/.github/workflows/source-policy.yml b/.github/workflows/source-policy.yml new file mode 100644 index 0000000..6179997 --- /dev/null +++ b/.github/workflows/source-policy.yml @@ -0,0 +1,17 @@ +name: Source policy checks +on: + pull_request: + types: [opened, synchronize, reopened, edited] + push: + branches: [master] + merge_group: +permissions: + contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true +jobs: + policy: + uses: computer-mcp/github-workflows/.github/workflows/policy.yml@d65bc20504f39c3a30232d67043abeca43d2c7b1 + with: + declaration-revision: 5c54a40cb7953db7472ebbefd3db608036b7b3ab diff --git a/AGENTS.md b/AGENTS.md index 573f948..b5bc6a8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -70,17 +70,17 @@ Use this check: - Before changing versions, dependencies, packaging or release workflows, read the [organization VERSIONING](https://github.com/computer-mcp/.github/blob/master/VERSIONING.md) - for version rules and `Documentation/Reference/Release.md` for release facts + for version rules and `docs/Reference/Release.md` for release facts and check entry points. -- For repository-native documentation placement, read `Documentation/README.md` +- For repository-native documentation placement, read `docs/README.md` before editing. - For current canonical structure, read - `Documentation/Architecture/README.md` and the relevant architecture files. -- For design-in-progress, use `Documentation/Proposals/*` when that subtree is + `docs/Architecture/README.md` and the relevant architecture files. +- For design-in-progress, use `docs/Proposals/*` when that subtree is present. -- For change history, consult `Documentation/Decisions/*`, - `Documentation/Migrations/*`, and `Documentation/Archive/*` when those +- For change history, consult `docs/Decisions/*`, + `docs/Migrations/*`, and `docs/Archive/*` when those subtrees are present. - For GitHub-facing collaboration files, use `.github/` and root governance files. @@ -92,10 +92,10 @@ Use this check: - `AGENTS.md` is the agent guide: first-principles guardrails, task route, authority boundaries, and boundary guardrails. - `README`-class files index scope and placement. -- `Documentation/Architecture/*` is current truth. -- `Documentation/Proposals/*` is proposal space when that subtree is present. -- `Documentation/Decisions/*`, `Documentation/Migrations/*`, and - `Documentation/Archive/*` are history when those subtrees are present. +- `docs/Architecture/*` is current truth. +- `docs/Proposals/*` is proposal space when that subtree is present. +- `docs/Decisions/*`, `docs/Migrations/*`, and + `docs/Archive/*` are history when those subtrees are present. - `.github/*` is GitHub-facing governance. ## Boundary Guardrails @@ -113,8 +113,8 @@ and operating guidance. Keep temporary implementation notes, local evidence, local paths, run-specific artifacts, and historical comparison notes out of README files, Reference docs, API docs, and bundled user-facing skills. Promote only accepted decision records into -`Documentation/Decisions/*` and durable transition or cutover records into -`Documentation/Migrations/*`. +`docs/Decisions/*` and durable transition or cutover records into +`docs/Migrations/*`. Use this decision test: @@ -147,7 +147,7 @@ source of truth instead of a literal value. - Keep Agent Guide and Index separate. - Keep current truth out of proposal and history subtrees when they are present. -- Keep GitHub collaboration configuration out of `Documentation/`. +- Keep GitHub collaboration configuration out of `docs/`. ## Repository Guardrails @@ -161,19 +161,9 @@ Read `README.md` and `computer-mcp-plugin.toml` before changing this package. defaults and skill text cannot grant access. - Keep `skills/observe-act-verify/SKILL.md` portable and independent of a particular user's paths, targets, credentials or tool catalog snapshot. -- Validate the package with `Scripts/build-package.sh OUTPUT_DIRECTORY`, then +- Validate the package with `scripts/build-package.sh OUTPUT_DIRECTORY`, then inspect it using the host's package validation and installation tests. - Keep test evidence outside the repository; do not publish repositories or artifacts without explicit authorization. - Preserve native AX fallback and never replay an uncertain write through a second backend. Real GUI verification requires a disposable, authorized target. - -## Brand delivery - -The organization `.github` repository owns the design system and renders every -image; the main Computer MCP repository owns ProductIdentity. Imported -`Documentation/Brand/header-light.png` and `header-dark.png` travel with the -packaged manual; `.github/brand/social.png` supplies the repository preview. -`.github/brand/brand.lock.json` binds them to the organization exports, and CI -verifies it with the shared brand-check workflow. Update imports with the -organization repository's `python3 Brand/brand.py sync`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6aef0e8..e43dc7b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,11 +6,11 @@ external dependencies and credentials. ## Validation -CI runs `python3 -m unittest discover -s Tests -p 'test_*.py'` and `python3 Scripts/build_package.py OUTPUT_DIRECTORY` on Python 3.13 and 3.11, and the organization brand check; run the first two before opening a pull request. Build an archive with `Scripts/build-package.sh OUTPUT_DIRECTORY` and inspect its file list. GUI checks use disposable, explicitly authorized targets. +CI runs `python3 -m unittest discover -s tests -p 'test_*.py'` and `python3 scripts/build_package.py OUTPUT_DIRECTORY` on Python 3.13 and 3.11; run both before opening a pull request. Build an archive with `scripts/build-package.sh OUTPUT_DIRECTORY` and inspect its file list. GUI checks use disposable, explicitly authorized targets. ## Documentation -The [documentation index](Documentation/README.md) links current architecture +The [documentation index](docs/README.md) links current architecture and reference material. Agent work routes live in AGENTS.md; public usage lives in the root README. GitHub collaboration files belong in .github/ and contributor policy belongs in root governance files. diff --git a/Documentation/Brand/header-dark.png b/Documentation/Brand/header-dark.png deleted file mode 100644 index b438aa8..0000000 Binary files a/Documentation/Brand/header-dark.png and /dev/null differ diff --git a/Documentation/Brand/header-light.png b/Documentation/Brand/header-light.png deleted file mode 100644 index 136cc8a..0000000 Binary files a/Documentation/Brand/header-light.png and /dev/null differ diff --git a/README.md b/README.md index 2d1a076..d62d87f 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,102 @@ - - - Computer MCP — Computer Use - +

+ + + + +

-# Computer MCP — Computer Use +

Computer Use

-Part of the [Computer MCP](https://computer-mcp.github.io/) family. -**Wherever you chat, your computer is there.** +

Computer MCP family plugin for native desktop observation and actions.

-A declarative Computer MCP package that connects to the vendor's native -Computer Use MCP server and supplies observation/action verification guidance. -It contains no proxy, vendor executable, installer or running skill service. +

+ CI + Python 3.11 or newer to build + License: FSL-1.1-ALv2 +

+ +

+ Install · + Usage · + Skill · + Documentation · + Contributing +

+ +## Overview + +A declarative [Computer MCP](https://computer-mcp.github.io/) package that connects the host to +the vendor's native Computer Use MCP server and adds guidance for verifying every observation and +action. It contains no proxy, vendor executable, installer or running skill service. The vendor +owns installation, updates and caller compatibility. + +## Quick look + +The plugin contributes: + +| Contribution | What it adds | +| --- | --- | +| `native` MCP server | The vendor client's own tools, started as `SkyComputerUseClient mcp` and exposed under the `computer-use` prefix | +| `observe-act-verify` skill | How to complete an authorized GUI task from current observations, scoped actions and post-action checks, and how to recover from stale references or uncertain results | + +Install the archive in the host, review the `native` contribution, choose which tools to expose, +and enable it. + +## Install + +1. Build the archive: `scripts/build-package.sh OUTPUT_DIRECTORY` writes `computer-use.zip`. +2. Install it through the host's Plugins page or management CLI with its declared ID and + version and the SHA-256 the build prints. A local development registration can point at this + checkout instead. +3. The host starts new plugins disabled. Review the `native` MCP contribution, choose its tool + whitelist or all-tools exposure explicitly, and keep the appropriate caller and profile + permissions. Enabling the package does not override those permissions. + +The host finds the client on its search path, then by the installed application's bundle +identifier, `com.openai.sky.CUAService`. When neither finds it, bind the actual executable in the +plugin's dependency settings; an explicit binding takes precedence. + +Host state lives outside this package. Uninstalling a version does not delete the vendor client, +this development checkout or saved host grants. + +## Usage + +A successful MCP handshake or tools listing does not prove that observation or actions work. +Before relying on the plugin, choose a disposable, authorized target: read its state, perform one +harmless action, then read and verify the result. Diagnose permission or caller-trust failures +through the vendor's and the system's supported flows. + +Native accessibility (AX) remains available for diagnostics and fallback where appropriate. An +uncertain write is observed before any further action; it is never replayed automatically +through AX. ## Requirements -Use a Computer MCP host with plugin support and a vendor-supported Computer Use -client. The client must provide its `mcp` entry point. The host checks its search -path, then locates the declared installed application by bundle identifier. -Supply the actual executable through the host's dependency binding when the -vendor installation is not discoverable by either method. An explicit binding -takes precedence. The vendor owns installation, updates and caller compatibility. - -The host starts new plugins disabled. Review the `native` MCP contribution, -explicitly choose its tool whitelist or all-tools exposure, and retain the -appropriate caller/profile permissions. Enabling the package does not override -those permissions. A successful MCP handshake or tools listing does not prove -that observation or actions work. - -For first-use verification, choose a disposable, authorized target: read its -state, perform one harmless action, then read and verify the result. Diagnose -permission or caller-trust failures through supported vendor/system flows. -Native AX remains available for diagnostics and appropriate fallback. An -uncertain write must be observed before any further action, never automatically -replayed through AX. - -## Package - -Run `Scripts/build-package.sh OUTPUT_DIRECTORY` to produce `computer-use.zip`. -The archive contains this manual, contributor and architecture documentation, -the license and third-party notices, the manifest and the skill directory. Build -output excludes Git metadata, local test evidence and external executables. -The build requires Python 3.11 or later's standard library; it installs nothing and -produces deterministic archive bytes from the same inputs. - -`.github/workflows/ci.yml` runs the package tests and retains the ZIP and -SHA-256 receipt on pull requests, `master` pushes and manual runs, and repeats -the tests and build on Python 3.11. This declaration-only -job runs independently of macOS and never starts the vendor client. Its workflow -artifacts are not a public plugin release or proof of GUI compatibility. - -Install the archive through the host's Plugins page or management CLI using its -declared ID/version and the SHA-256 emitted by the build. A local development -registration can point at this checkout instead. Local ownership or a bundled -source label is not a verified GitHub publisher or artifact signature. - -Host state belongs outside this package. Uninstalling an installed version must -not delete the vendor client, this development checkout, or saved host grants. - -The [repository documentation](Documentation/README.md) describes the package -architecture; the [contributor guide](CONTRIBUTING.md) covers validation. +- A Computer MCP host with plugin support, on macOS. +- A vendor-supported Computer Use client that provides its `mcp` entry point. +- Python 3.11 or newer (standard library only) to build the archive. + +## Documentation + +The archive contains this manual, the contributor and architecture documentation, the license +and third-party notices, the manifest and the skill directory; it excludes Git metadata, local +test evidence and external executables, and the same inputs produce the same bytes. The +[documentation index](docs/README.md) covers the package architecture and the +[release process](docs/Reference/Release.md). + +CI runs the package tests and keeps the archive and its SHA-256 receipt on pull requests, `master` +pushes and manual runs, and repeats the tests and build on Python 3.11. It runs without macOS and +never starts the vendor client, so its artifacts are neither a public plugin release nor proof of +GUI compatibility. Local ownership or a bundled source label is not a verified GitHub publisher or +artifact signature. + +## Contributing + +See the [contributor guide](CONTRIBUTING.md). ## License Computer MCP-owned code and resources use the -[Functional Source License 1.1, Apache 2.0 Future License](LICENSE) -(FSL-1.1-ALv2). See [third-party notices](THIRD_PARTY_NOTICES.md) for the -external vendor client's separate ownership and terms. +[Functional Source License 1.1, Apache 2.0 Future License](LICENSE) (FSL-1.1-ALv2). See the +[third-party notices](THIRD_PARTY_NOTICES.md) for the vendor client's separate ownership and +terms. diff --git a/Documentation/Architecture/Documentation.md b/docs/Architecture/Documentation.md similarity index 76% rename from Documentation/Architecture/Documentation.md rename to docs/Architecture/Documentation.md index a37950e..b6504e8 100644 --- a/Documentation/Architecture/Documentation.md +++ b/docs/Architecture/Documentation.md @@ -1,10 +1,10 @@ # Documentation structure -The root README is the public setup and usage manual. Documentation/README.md +The root README is the public setup and usage manual. docs/README.md indexes the architecture and reference material. AGENTS.md routes agent work; CONTRIBUTING.md describes contributor checks. Current package facts belong in -Documentation/Architecture/Package.md; commands, formats and operating detail -belong in Documentation/Reference/. +docs/Architecture/Package.md; commands, formats and operating detail +belong in docs/Reference/. Version rules belong in the [organization VERSIONING](https://github.com/computer-mcp/.github/blob/master/VERSIONING.md). diff --git a/Documentation/Architecture/Package.md b/docs/Architecture/Package.md similarity index 88% rename from Documentation/Architecture/Package.md rename to docs/Architecture/Package.md index 7c42a8d..56fed73 100644 --- a/Documentation/Architecture/Package.md +++ b/docs/Architecture/Package.md @@ -4,7 +4,7 @@ The manifest contributes the native Computer Use MCP entry and a portable observe–act–verify skill. The vendor owns the executable and caller compatibility; -the host owns registration, workspace/profile grants and dependency bindings. +the host owns registration, project and profile grants and dependency bindings. Skills are guidance resources and do not run during installation or reading. The dependency declares a command name and an application bundle locator. The @@ -17,8 +17,9 @@ directory. Discovery does not establish signing identity or GUI caller trust. The package builder validates regular files and bounded input, rejects links and special files, and emits deterministic ZIP bytes. The shipped contents are the -manifest, manuals, license and notices, architecture documentation and skill tree. Agent execution -state and source-level test helpers stay with the checkout. +manifest, manuals, license and notices, architecture documentation, skill tree +and the README logo images in `.github/assets/logo/`. Agent execution state +and source-level test helpers stay with the checkout. The MCP connection goes directly to the vendor. Native AX remains a diagnostic and fallback facility; uncertain actions require observation before another diff --git a/Documentation/Architecture/README.md b/docs/Architecture/README.md similarity index 100% rename from Documentation/Architecture/README.md rename to docs/Architecture/README.md diff --git a/Documentation/README.md b/docs/README.md similarity index 100% rename from Documentation/README.md rename to docs/README.md diff --git a/Documentation/Reference/Release.md b/docs/Reference/Release.md similarity index 89% rename from Documentation/Reference/Release.md rename to docs/Reference/Release.md index 4436838..c573876 100644 --- a/Documentation/Reference/Release.md +++ b/docs/Reference/Release.md @@ -9,7 +9,7 @@ The organization [VERSIONING](https://github.com/computer-mcp/.github/blob/maste owns version rules, including tags, version choice and the changelog. This reference records this repository's version sources, compatibility, release procedure and checks. The shared -[release workflow guide](https://github.com/computer-mcp/.github/blob/master/Documentation/Reference/PluginRelease.md) +[release workflow guide](https://github.com/computer-mcp/github-workflows/blob/master/docs/plugin-release.md) describes workflow verification and recovery. ## Components and Version Sources @@ -20,8 +20,9 @@ describes workflow verification and recovery. | Manifest and host runtime contract | Plugin API version | `plugin_api` in [`computer-mcp-plugin.toml`](../../computer-mcp-plugin.toml) | release receipt and host compatibility evaluation | Package tests check the declaration; `release.yml` verifies it | The archive contains the README, contribution guide, manifest, license, notices, -`skills/` and `Documentation/`. Tests, build scripts and workflows are excluded -from the archive. +`skills/`, `docs/` and the README images `.github/assets/logo/logo-light.png` +and `.github/assets/logo/logo-dark.png`. Tests, build scripts and workflows +are excluded from the archive. ## Compatibility Surface @@ -47,9 +48,10 @@ acceptance identify the combination actually observed. ## Derived Metadata and Drift Checks -`Scripts/build_package.py` verifies the plugin ID, skips hidden files and Python -caches, rejects links and special files, and produces the same archive bytes -from the same source. `Tests/test_build_package.py` checks the plugin API +`scripts/build_package.py` verifies the plugin ID, explicitly includes the README +logo images, skips hidden files and Python caches when traversing content trees, +rejects links and special files, and produces the same archive bytes from the +same source. `tests/test_build_package.py` checks the plugin API declaration and pins the exact archive file set. The builder receipt prints the ID, version and SHA-256. @@ -65,12 +67,12 @@ The shared workflow checks that setting before writing a Release. `ci.yml`, including its `computer-use-plugin-` artifact. 2. Dispatch `release.yml` with `ref` set to that full commit SHA and `publish` set to `false` (the default). The workflow rebuilds with - `Scripts/build-package.sh` and compares the archive bytes with the exact CI + `scripts/build-package.sh` and compares the archive bytes with the exact CI artifact. Inspect the `plugin-release--` Actions artifact: `assets/` contains `computer-use.zip`, `release-receipt.json` and `SHA256SUMS`. An untagged dry run reports the signed annotated tag still required for publication. -3. Rebuild the same commit with `Scripts/build-package.sh ` +3. Rebuild the same commit with `scripts/build-package.sh ` and confirm it matches the dry-run archive. Validate and install that exact archive with the selected Computer MCP host, and confirm native catalog discovery with the installed vendor client. GUI checks use a disposable, @@ -117,9 +119,9 @@ The job checks this repository's immutable ID, does not check out package code, and grants its own token no repository permissions. Missing or rejected authority fails the run visibly. -The website's notification action is pinned to a reviewed full commit. Publish -that website commit first, then update the pin here when adopting a change to -the notification contract. +The shared notification action is pinned to a reviewed full commit of +`computer-mcp/github-workflows`. Publish that commit first, then update the pin +here when adopting a change to the notification contract. ## Evidence Reuse and Invalidation @@ -141,8 +143,8 @@ acceptance record. | Operation | Existing command or explicit manual procedure | Required access | | --- | --- | --- | -| Version update and check | Edit `computer-mcp-plugin.toml`; `python3 -m unittest discover -s Tests -p 'test_*.py'` | Local checkout | -| Candidate validation and build | `ci.yml`; locally `Scripts/build-package.sh ` | CI or local checkout | +| Version update and check | Edit `computer-mcp-plugin.toml`; `python3 -m unittest discover -s tests -p 'test_*.py'` | Local checkout | +| Candidate validation and build | `ci.yml`; locally `scripts/build-package.sh ` | CI or local checkout | | Native and host acceptance | Validate and install the exact archive with the selected host; verify native catalog discovery and authorized GUI behavior | Selected host, vendor client and authorized disposable target | | Acceptance and publication | Manual checks above; dispatch `release.yml`, dry run first | Repository Actions; publication also needs a signing key and release write access | | Status and interrupted-run recovery | Inspect CI or `release.yml`; follow [Retry](#retry) | Repository Actions | diff --git a/Scripts/build-package.sh b/scripts/build-package.sh similarity index 56% rename from Scripts/build-package.sh rename to scripts/build-package.sh index eee3c05..cf20f26 100755 --- a/Scripts/build-package.sh +++ b/scripts/build-package.sh @@ -1,4 +1,4 @@ #!/bin/sh set -eu plugin_directory=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -exec python3 "$plugin_directory/Scripts/build_package.py" "$@" +exec python3 "$plugin_directory/scripts/build_package.py" "$@" diff --git a/Scripts/build_package.py b/scripts/build_package.py similarity index 94% rename from Scripts/build_package.py rename to scripts/build_package.py index b58dfcf..413758a 100644 --- a/Scripts/build_package.py +++ b/scripts/build_package.py @@ -15,8 +15,9 @@ def package_bytes(root): paths = [root / name for name in ( "README.md", "CONTRIBUTING.md", "computer-mcp-plugin.toml", "LICENSE", - "THIRD_PARTY_NOTICES.md", "ThirdPartyNotices.txt")] - pending = [root / "skills", root / "Documentation"] + "THIRD_PARTY_NOTICES.md", "ThirdPartyNotices.txt", + ".github/assets/logo/logo-light.png", ".github/assets/logo/logo-dark.png")] + pending = [root / "skills", root / "docs"] entries = 0 while pending: path = pending.pop() diff --git a/Tests/test_build_package.py b/tests/test_build_package.py similarity index 81% rename from Tests/test_build_package.py rename to tests/test_build_package.py index afe7372..f1294c7 100644 --- a/Tests/test_build_package.py +++ b/tests/test_build_package.py @@ -9,7 +9,7 @@ import zipfile ROOT = Path(__file__).resolve().parent.parent -SPEC = importlib.util.spec_from_file_location("build_package", ROOT / "Scripts/build_package.py") +SPEC = importlib.util.spec_from_file_location("build_package", ROOT / "scripts/build_package.py") BUILDER = importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(BUILDER) @@ -26,18 +26,20 @@ def test_reproducible_owned_contents(self): with zipfile.ZipFile(io.BytesIO(first)) as archive: self.assertEqual(set(archive.namelist()), { "README.md", "computer-mcp-plugin.toml", + ".github/assets/logo/logo-light.png", + ".github/assets/logo/logo-dark.png", "LICENSE", "THIRD_PARTY_NOTICES.md", "ThirdPartyNotices.txt", - "CONTRIBUTING.md", "Documentation/README.md", - "Documentation/Architecture/README.md", - "Documentation/Architecture/Package.md", - "Documentation/Architecture/Documentation.md", - "Documentation/Brand/header-dark.png", - "Documentation/Brand/header-light.png", - "Documentation/Reference/Release.md", + "CONTRIBUTING.md", "docs/README.md", + "docs/Architecture/README.md", + "docs/Architecture/Package.md", + "docs/Architecture/Documentation.md", + "docs/Reference/Release.md", "skills/observe-act-verify/SKILL.md", "skills/observe-act-verify/agents/openai.yaml", }) - for name in ("LICENSE", "THIRD_PARTY_NOTICES.md", "ThirdPartyNotices.txt"): + for name in ("LICENSE", "THIRD_PARTY_NOTICES.md", "ThirdPartyNotices.txt", + ".github/assets/logo/logo-light.png", + ".github/assets/logo/logo-dark.png"): self.assertEqual(archive.read(name), (ROOT / name).read_bytes()) for item in archive.infolist(): self.assertEqual(item.date_time, (1980, 1, 1, 0, 0, 0)) @@ -51,8 +53,9 @@ def test_rejects_links_oversize_and_special_files(self): for name in ("README.md", "CONTRIBUTING.md", "computer-mcp-plugin.toml", "LICENSE", "THIRD_PARTY_NOTICES.md", "ThirdPartyNotices.txt"): shutil.copyfile(ROOT / name, root / name) + shutil.copytree(ROOT / ".github/assets/logo", root / ".github/assets/logo") shutil.copytree(ROOT / "skills", root / "skills") - shutil.copytree(ROOT / "Documentation", root / "Documentation") + shutil.copytree(ROOT / "docs", root / "docs") target = root / "skills" / "unsafe" if kind == "file-link": target.symlink_to(root / "README.md") @@ -60,7 +63,7 @@ def test_rejects_links_oversize_and_special_files(self): (root / "skills").rename(root / "guidance") (root / "skills").symlink_to(root / "guidance", target_is_directory=True) elif kind == "documentation-link": - (root / "Documentation" / "unsafe").symlink_to(root / "README.md") + (root / "docs" / "unsafe").symlink_to(root / "README.md") elif kind == "oversize": target.write_bytes(b"x" * 1_048_577) else: @@ -74,15 +77,17 @@ def test_skips_hidden_files_and_python_caches(self): with tempfile.TemporaryDirectory() as temp: root = Path(temp) / "source" shutil.copytree(ROOT, root, ignore=shutil.ignore_patterns(".git", "__pycache__")) - (root / "Documentation" / ".DS_Store").write_bytes(b"finder") + (root / "docs" / ".DS_Store").write_bytes(b"finder") (root / "skills" / "__pycache__").mkdir() (root / "skills" / "__pycache__" / "cached.pyc").write_bytes(b"cache") (root / "skills" / "stale.pyc").write_bytes(b"cache") actual, _ = BUILDER.package_bytes(root) self.assertEqual(actual, expected) - def test_requires_license_and_notices(self): - for name in ("LICENSE", "THIRD_PARTY_NOTICES.md", "ThirdPartyNotices.txt"): + def test_requires_license_notices_and_logos(self): + for name in ("LICENSE", "THIRD_PARTY_NOTICES.md", "ThirdPartyNotices.txt", + ".github/assets/logo/logo-light.png", + ".github/assets/logo/logo-dark.png"): with self.subTest(name=name), tempfile.TemporaryDirectory() as temp: root = Path(temp) / "source" shutil.copytree(ROOT, root, ignore=shutil.ignore_patterns(".git", "__pycache__")) @@ -92,7 +97,7 @@ def test_requires_license_and_notices(self): def test_command_is_idempotent_but_never_overwrites_different_output(self): with tempfile.TemporaryDirectory() as temp: - command = [sys.executable, str(ROOT / "Scripts/build_package.py"), temp] + command = [sys.executable, str(ROOT / "scripts/build_package.py"), temp] first = subprocess.run(command, capture_output=True, timeout=10) second = subprocess.run(command, capture_output=True, timeout=10) self.assertEqual(first.returncode, 0, first.stderr)