diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +24 diff --git a/DISTRIBUTION.md b/DISTRIBUTION.md index 19377eb..6a04bb8 100644 --- a/DISTRIBUTION.md +++ b/DISTRIBUTION.md @@ -15,6 +15,7 @@ shipped yet. | GitHub repository | Published | The canonical public source and collaboration surface is `xiaojiou176-open/movi-organizer`. | | GitHub Releases | Published | GitHub Releases is the canonical release trail for this repository. | | GitHub Pages | Published | GitHub Pages is the current public front door and discovery page. | +| Submission manifest | Published in-repo | [`manifest.yaml`](./manifest.yaml) is the repo-owned summary of current published vs submission-ready-unlisted surfaces. | ## Current Ready But Not Registry-Published @@ -23,6 +24,8 @@ shipped yet. | Source install from this repository | Ready | The repository ships Python package metadata and console entrypoints such as `movi-organizer`, `movi-web-api`, and `movi-mcp`. | | Movi MCP v1 stdio server | Ready | The MCP surface is a local-first stdio integration that can be launched from this repository or an installed environment. | | Codex / Claude Code setup docs | Ready | The repository ships setup pages for Codex and Claude Code, but those docs describe local wiring, not a separate public distribution channel. | +| Skills / agent bundle shelf | Submission-ready-unlisted | The repository now ships repo-owned skill bundle examples and install/proof notes without claiming a live skills registry listing. | +| OpenClaw / ClawHub bundle | Submission-ready-unlisted | The repository now ships an OpenClaw-facing bundle and proof note without claiming a live catalog entry. | ## Surfaces We Do Not Currently Claim As Published @@ -31,8 +34,8 @@ shipped yet. | PyPI | Not published | This repository does not currently claim an official PyPI release surface. | | npm package | Not published | The root Node surface is a repo control surface, not a published npm package. | | MCP registry | Not published | `Movi MCP v1` is documented as a local-first stdio surface, not a registry-published listing. | -| Skills registry | Not published | No official skills-registry distribution claim is made here. | -| Plugin marketplace | Not published | Strategy Packs are templates, not a plugin marketplace. | +| Skills registry | Not published | A repo-owned skills bundle exists, but no live registry listing is claimed. | +| Plugin marketplace | Not published | Agent/plugin bundle readiness exists, but no browser or plugin marketplace listing is claimed. | | Chrome Web Store | Not published | No official Chrome Web Store distribution claim is made here. | ## Current Reading Rule @@ -42,6 +45,8 @@ Use the surfaces above in this order: 1. GitHub repository for the public source of truth 2. GitHub Releases for release history 3. GitHub Pages for the current public landing route +4. `manifest.yaml` for the repo-owned submission summary +5. `examples/skills/README.md` and `examples/openclaw/README.md` for unlisted bundle surfaces If you need to answer a stricter question such as "is the current head verified as a published release," do not infer that from a tag alone. Use the release diff --git a/INTEGRATIONS.md b/INTEGRATIONS.md index 23a4e16..25e06b2 100644 --- a/INTEGRATIONS.md +++ b/INTEGRATIONS.md @@ -36,13 +36,19 @@ class, some are side entrances, and some are only comparison points. | OpenHands | A real API and MCP substrate exists, but no dedicated first-party setup page or branded workflow is shipped. | Do not present it as a primary or secondary front-door integration. | | OpenCode | Same honest boundary as OpenHands. | Do not present it as a primary or secondary front-door integration. | -## Not A Front-Door Fit Today +## Submission-Ready Unlisted Fit -| Surface | Why it is not front-door fit today | +| Surface | Why it is submission-ready-unlisted | +| :-- | :-- | +| OpenClaw / ClawHub | The repository now ships a dedicated integration note, starter bundle, and sample configs, but still refuses to claim a live listing. | +| Skills registry or plugin marketplaces | The repository now ships a repo-owned agent/plugin bundle, but still refuses to claim a live registry or browser marketplace listing. | + +## Not Claimed As Live + +| Surface | Why it is still not claimed as live | | :-- | :-- | -| OpenClaw | The repository does not currently ship a dedicated integration surface or branded product flow for it. | | MCP registry listings | The current story is local-first stdio MCP, not registry publication. | -| Skills registry or plugin marketplaces | The repository does not currently claim those as shipped integration channels. | +| Browser extension marketplaces | The new bundle is agent/plugin-ready, not a Chrome Web Store or browser-extension listing. | ## Safety Boundary diff --git a/README.md b/README.md index fa58f6b..82eb73b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Movi is a review-first local file organizer and workbench for messy photos, screenshots, documents, and audio. It lets AI assist with the manifest first, then lets you inspect, label, and approve the plan before deterministic `apply` or `rollback` touches your files. -[Safe First Look](#safe-first-look) · [10-Second Tour](#10-second-tour) · [Good Fit / Not A Fit](#good-fit--not-a-fit) · [Public Proof](docs/public_proof.md) · [Docs](docs/index.md) · [Distribution](DISTRIBUTION.md) · [Integrations](INTEGRATIONS.md) · [Browser Surface](#browser-surface) · [Releases](https://github.com/xiaojiou176-open/movi-organizer/releases) · [Discussions](https://github.com/xiaojiou176-open/movi-organizer/discussions) · [Security](SECURITY.md) · [Movi MCP v1](docs/mcp.md) · [Codex Integration](docs/codex_mcp.md) · [Claude Code Integration](docs/claude_code_mcp.md) · [Developer Guide](docs/developer_guide.md) +[Safe First Look](#safe-first-look) · [10-Second Tour](#10-second-tour) · [Good Fit / Not A Fit](#good-fit--not-a-fit) · [Public Proof](docs/public_proof.md) · [Docs](docs/index.md) · [Distribution](DISTRIBUTION.md) · [Integrations](INTEGRATIONS.md) · [Submission Manifest](manifest.yaml) · [Skills Bundle](examples/skills/README.md) · [OpenClaw Bundle](examples/openclaw/README.md) · [Browser Surface](#browser-surface) · [Releases](https://github.com/xiaojiou176-open/movi-organizer/releases) · [Discussions](https://github.com/xiaojiou176-open/movi-organizer/discussions) · [Security](SECURITY.md) · [Movi MCP v1](docs/mcp.md) · [Codex Integration](docs/codex_mcp.md) · [Claude Code Integration](docs/claude_code_mcp.md) · [Developer Guide](docs/developer_guide.md) ![Movi overview showing mixed files flowing into manifest review and organized output](docs/assets/storefront/hero-movi-overview.svg) @@ -68,7 +68,7 @@ Current honest ecosystem fit: - **Primary fit**: Codex and Claude Code, because `Movi MCP v1` already ships as a local-first stdio surface with review-safe tools - **Secondary ecosystem fit**: Cursor and other local MCP-capable clients, because the transport and tool surface already match their integration model - **Comparison-only fit**: OpenHands and OpenCode, because the repo has a real MCP and API substrate they can consume, but no dedicated first-party setup surface or branded workflow yet -- **Not a front-door fit today**: OpenClaw, because the repo does not currently ship a tailored integration surface or branded product claim for it +- **Submission-ready-unlisted fit**: OpenClaw and ClawHub, because the repo now ships a dedicated bundle, install/proof notes, and agent-facing starter surfaces without claiming a live listing ## 10-Second Tour diff --git a/contracts/governance/code_scanning_alert_allowlist.yaml b/contracts/governance/code_scanning_alert_allowlist.yaml new file mode 100644 index 0000000..4b70294 --- /dev/null +++ b/contracts/governance/code_scanning_alert_allowlist.yaml @@ -0,0 +1,10 @@ +version: 1 +status: active +reason: > + 2026-04-08 final distribution prep accepts three GitHub Scorecard governance + findings as temporary non-blockers while keeping all other code-scanning, + dependency, and secret findings blocking. +accepted_rule_ids: + - CIIBestPracticesID + - MaintainedID + - CodeReviewID diff --git a/contracts/governance/public_readiness_policy.yaml b/contracts/governance/public_readiness_policy.yaml index f55cc3d..4b8140b 100644 --- a/contracts/governance/public_readiness_policy.yaml +++ b/contracts/governance/public_readiness_policy.yaml @@ -1,6 +1,8 @@ version: 1 default_branch: main +accepted_code_scanning_rules_contract: contracts/governance/code_scanning_alert_allowlist.yaml required_repo_surface_files: + - .nvmrc - LICENSE - NOTICE - THIRD_PARTY_NOTICES.md @@ -8,6 +10,7 @@ required_repo_surface_files: - CONTRIBUTING.md - DISTRIBUTION.md - INTEGRATIONS.md + - manifest.yaml - SUPPORT.md - CODE_OF_CONDUCT.md - .github/CODEOWNERS @@ -24,6 +27,9 @@ required_repo_surface_files: - docs/mcp.md - docs/developer_guide.md - docs/review_first_ai_file_organizer.md + - examples/skills/README.md + - examples/skills/SKILL.md + - examples/openclaw/README.md required_package_scripts: - release:draft - security:history-scan diff --git a/contracts/governance/public_surface.yaml b/contracts/governance/public_surface.yaml index 85eea42..e140b46 100644 --- a/contracts/governance/public_surface.yaml +++ b/contracts/governance/public_surface.yaml @@ -1,6 +1,7 @@ version: 1 public_docs: - README.md + - manifest.yaml - AGENTS.md - CLAUDE.md - LICENSE @@ -14,6 +15,8 @@ public_docs: - CODE_OF_CONDUCT.md - docs/usage.md - docs/architecture.md + - examples/skills/README.md + - examples/openclaw/README.md public_doc_globs: - apps/**/AGENTS.md - apps/**/CLAUDE.md diff --git a/contracts/governance/root_allowlist.yaml b/contracts/governance/root_allowlist.yaml index ad7d5a6..7e49a3e 100644 --- a/contracts/governance/root_allowlist.yaml +++ b/contracts/governance/root_allowlist.yaml @@ -5,6 +5,7 @@ canonical_tracked_entries: - .env.example - .github - .gitignore + - .nvmrc - .pre-commit-config.yaml - AGENTS.md - CHANGELOG.md @@ -22,6 +23,8 @@ canonical_tracked_entries: - apps - contracts - docs + - examples + - manifest.yaml - ops - package-lock.json - package.json @@ -52,6 +55,7 @@ entry_purposes: .env.example: runtime env template .github: CI workflows and automation .gitignore: repository hygiene ignore contract + .nvmrc: local Node major-line pin for operator and CI parity .pre-commit-config.yaml: local hook contract .runtime-cache: the only repo-side runtime noise root .serena: repo-local Serena caches and project metadata that must remain untracked @@ -71,6 +75,8 @@ entry_purposes: apps: repo entry applications contracts: machine-enforced contracts and governance truth docs: repo-level human documentation + examples: submission-ready public bundle examples and install packets + manifest.yaml: top-level submission manifest for public distribution truth ops: operational collateral package-lock.json: root workspace node lock package.json: root workspace scripts @@ -83,6 +89,7 @@ required_non_empty_dirs: - apps - contracts - docs + - examples - ops - packages - tests diff --git a/contracts/governance/root_change_control.yaml b/contracts/governance/root_change_control.yaml index 4c00c4f..c9d4565 100644 --- a/contracts/governance/root_change_control.yaml +++ b/contracts/governance/root_change_control.yaml @@ -32,6 +32,10 @@ entries: owner: repo change_class: hygiene approval_rule: architecture-review + .nvmrc: + owner: repo + change_class: public-config + approval_rule: architecture-review .pre-commit-config.yaml: owner: repo change_class: policy @@ -112,6 +116,14 @@ entries: owner: repo change_class: canonical-root approval_rule: architecture-review + examples: + owner: repo + change_class: canonical-root + approval_rule: architecture-review + manifest.yaml: + owner: repo + change_class: public-doc + approval_rule: architecture-review ops: owner: repo change_class: canonical-root diff --git a/docs/_generated/render_state.json b/docs/_generated/render_state.json index cf62c9a..02d2ee6 100644 --- a/docs/_generated/render_state.json +++ b/docs/_generated/render_state.json @@ -45,7 +45,7 @@ "ops/compose/docker-compose.yml": "9f3f0a6047c30cc93374b41a2b0acbf5563b5b461812efac90e4e6e9da9a693c", "contracts/governance/governance.defaults.env": "cddb6b33f746b32509f8a49af1f11bef945d0eeaf060028da41c726127d68660", "pyproject.toml": "d12c2c415c3900d81c888e91d3a8ec1af88af96a701c6de801c37a1e52b4a495", - "package.json": "19ed161b16378601fc42d4baa05b7225fc57a47cfdee28a64131fd57d3cd1efc", + "package.json": "4b9bfb8aac66c5c9101b56af866932d5a27a2b1b912b01439905155a09cd6827", ".env.example": "32a60792624cd36eb5992b34d9c67c6e6e2a0971a45f3a48370f3b0b720b0aa8" }, "output_hash": "35f928f34d5157b2764465e28ea00c4e234354342d4d26cc1164f9231eebf20d" @@ -58,14 +58,14 @@ "block_id": null, "source_hashes": { "contracts/governance/done_signal_policy.yaml": "16e2fc2add45a68406d6a6077ed7b912fe2406905bf1ff68e967bceeda4bd83c", - "contracts/governance/public_readiness_policy.yaml": "1207163d53fb898d20faee2a65d66ee9f29fedd29115364497d9c5ec03c3a7eb", + "contracts/governance/public_readiness_policy.yaml": "eacf27c9d50b2c4295abddecddc23c83f9302140cf82bbd26cd0070cf18ce835", "contracts/governance/required_checks_policy.yaml": "51f9e8cb17df93c3912ae719a6ba95b74f2c666b6b11c79e1728f7002fb3e3b3", ".github/workflows/ci.yml": "145d243beb087cd6a976d45e4455e0747ace3ee3ad9c77160b57fb17f75c63fe", ".github/workflows/pre-commit.yml": "ed0082256d36108f1bdb3d6c66f6f9805295a4f7f738465b0092d0b3fe0199ed", ".github/workflows/live-integration.yml": "87a1d413bc8d92d2103b5d4a6e19cfcb85fdb30e9e753b88a3267607a41d2b92", ".github/workflows/mutation-manual.yml": "c892aa745b913f37a4662e51ecf0c2013dc32faecd1c97d4a59f2d1e808fb101" }, - "output_hash": "774d8eaecbd42c0759da5b26cdfede0e5255f9e9d7c35b5d0e163ff9e58935c7" + "output_hash": "b87dbaba30da2a7478fb1870192748b312367579f91832025a2be12458f9ccba" }, { "id": "root-web-api-routes", @@ -76,7 +76,7 @@ "source_hashes": { "contracts/api/web_api.openapi.yaml": "c97830844b65fd8f76cd23688187ca8648835d787d1819045882dea01e1d7b64" }, - "output_hash": "0f570bc328a43eff72145d98195fe8a906244ad34388f46d0cfc9a50fefa6c70" + "output_hash": "c0edfeff801744050b9f8e0b7661f30a4ab0f6667c5d987e24ac7eb1ad56f869" }, { "id": "root-runtime-topology", @@ -88,10 +88,10 @@ "ops/compose/docker-compose.yml": "9f3f0a6047c30cc93374b41a2b0acbf5563b5b461812efac90e4e6e9da9a693c", "contracts/governance/governance.defaults.env": "cddb6b33f746b32509f8a49af1f11bef945d0eeaf060028da41c726127d68660", "pyproject.toml": "d12c2c415c3900d81c888e91d3a8ec1af88af96a701c6de801c37a1e52b4a495", - "package.json": "19ed161b16378601fc42d4baa05b7225fc57a47cfdee28a64131fd57d3cd1efc", + "package.json": "4b9bfb8aac66c5c9101b56af866932d5a27a2b1b912b01439905155a09cd6827", ".env.example": "32a60792624cd36eb5992b34d9c67c6e6e2a0971a45f3a48370f3b0b720b0aa8" }, - "output_hash": "0f570bc328a43eff72145d98195fe8a906244ad34388f46d0cfc9a50fefa6c70" + "output_hash": "c0edfeff801744050b9f8e0b7661f30a4ab0f6667c5d987e24ac7eb1ad56f869" }, { "id": "root-ci-governance-summary", @@ -104,9 +104,9 @@ ".github/workflows/reusable-build-runtime-image.yml": "0b79722ad922ec45ce8eb62c2466834855aebb9915f86a8ff65a25b71ebf5793", ".github/workflows/nightly-drift-audit.yml": "485f3ee338c8c951e92047ff1bf600379eebb8a3265c1ded542bd95caad7770c", "contracts/governance/required_checks_policy.yaml": "51f9e8cb17df93c3912ae719a6ba95b74f2c666b6b11c79e1728f7002fb3e3b3", - "package.json": "19ed161b16378601fc42d4baa05b7225fc57a47cfdee28a64131fd57d3cd1efc" + "package.json": "4b9bfb8aac66c5c9101b56af866932d5a27a2b1b912b01439905155a09cd6827" }, - "output_hash": "0f570bc328a43eff72145d98195fe8a906244ad34388f46d0cfc9a50fefa6c70" + "output_hash": "c0edfeff801744050b9f8e0b7661f30a4ab0f6667c5d987e24ac7eb1ad56f869" }, { "id": "root-release-identity", @@ -117,7 +117,7 @@ "source_hashes": { "pyproject.toml": "d12c2c415c3900d81c888e91d3a8ec1af88af96a701c6de801c37a1e52b4a495" }, - "output_hash": "0f570bc328a43eff72145d98195fe8a906244ad34388f46d0cfc9a50fefa6c70" + "output_hash": "c0edfeff801744050b9f8e0b7661f30a4ab0f6667c5d987e24ac7eb1ad56f869" }, { "id": "script-readme-web-api-routes", @@ -140,7 +140,7 @@ "ops/compose/docker-compose.yml": "9f3f0a6047c30cc93374b41a2b0acbf5563b5b461812efac90e4e6e9da9a693c", "contracts/governance/governance.defaults.env": "cddb6b33f746b32509f8a49af1f11bef945d0eeaf060028da41c726127d68660", "pyproject.toml": "d12c2c415c3900d81c888e91d3a8ec1af88af96a701c6de801c37a1e52b4a495", - "package.json": "19ed161b16378601fc42d4baa05b7225fc57a47cfdee28a64131fd57d3cd1efc", + "package.json": "4b9bfb8aac66c5c9101b56af866932d5a27a2b1b912b01439905155a09cd6827", ".env.example": "32a60792624cd36eb5992b34d9c67c6e6e2a0971a45f3a48370f3b0b720b0aa8" }, "output_hash": "4203a15a1ce623a972a6ba31f6a28f55eaa4520db960e7529b165e1bb550e2b3" @@ -156,7 +156,7 @@ ".github/workflows/reusable-build-runtime-image.yml": "0b79722ad922ec45ce8eb62c2466834855aebb9915f86a8ff65a25b71ebf5793", ".github/workflows/nightly-drift-audit.yml": "485f3ee338c8c951e92047ff1bf600379eebb8a3265c1ded542bd95caad7770c", "contracts/governance/required_checks_policy.yaml": "51f9e8cb17df93c3912ae719a6ba95b74f2c666b6b11c79e1728f7002fb3e3b3", - "package.json": "19ed161b16378601fc42d4baa05b7225fc57a47cfdee28a64131fd57d3cd1efc" + "package.json": "4b9bfb8aac66c5c9101b56af866932d5a27a2b1b912b01439905155a09cd6827" }, "output_hash": "4203a15a1ce623a972a6ba31f6a28f55eaa4520db960e7529b165e1bb550e2b3" }, @@ -179,7 +179,7 @@ "block_id": "script-readme-governance-truth", "source_hashes": { "contracts/governance/done_signal_policy.yaml": "16e2fc2add45a68406d6a6077ed7b912fe2406905bf1ff68e967bceeda4bd83c", - "contracts/governance/public_readiness_policy.yaml": "1207163d53fb898d20faee2a65d66ee9f29fedd29115364497d9c5ec03c3a7eb", + "contracts/governance/public_readiness_policy.yaml": "eacf27c9d50b2c4295abddecddc23c83f9302140cf82bbd26cd0070cf18ce835", "contracts/governance/required_checks_policy.yaml": "51f9e8cb17df93c3912ae719a6ba95b74f2c666b6b11c79e1728f7002fb3e3b3", ".github/workflows/ci.yml": "145d243beb087cd6a976d45e4455e0747ace3ee3ad9c77160b57fb17f75c63fe", ".github/workflows/pre-commit.yml": "ed0082256d36108f1bdb3d6c66f6f9805295a4f7f738465b0092d0b3fe0199ed", @@ -209,7 +209,7 @@ "ops/compose/docker-compose.yml": "9f3f0a6047c30cc93374b41a2b0acbf5563b5b461812efac90e4e6e9da9a693c", "contracts/governance/governance.defaults.env": "cddb6b33f746b32509f8a49af1f11bef945d0eeaf060028da41c726127d68660", "pyproject.toml": "d12c2c415c3900d81c888e91d3a8ec1af88af96a701c6de801c37a1e52b4a495", - "package.json": "19ed161b16378601fc42d4baa05b7225fc57a47cfdee28a64131fd57d3cd1efc", + "package.json": "4b9bfb8aac66c5c9101b56af866932d5a27a2b1b912b01439905155a09cd6827", ".env.example": "32a60792624cd36eb5992b34d9c67c6e6e2a0971a45f3a48370f3b0b720b0aa8" }, "output_hash": "c0cb776be4feeaa4d6517d934e2d4f541ae1a04d66350c7bd82da6b009f710b4" @@ -222,7 +222,7 @@ "block_id": "architecture-governance-truth", "source_hashes": { "contracts/governance/done_signal_policy.yaml": "16e2fc2add45a68406d6a6077ed7b912fe2406905bf1ff68e967bceeda4bd83c", - "contracts/governance/public_readiness_policy.yaml": "1207163d53fb898d20faee2a65d66ee9f29fedd29115364497d9c5ec03c3a7eb", + "contracts/governance/public_readiness_policy.yaml": "eacf27c9d50b2c4295abddecddc23c83f9302140cf82bbd26cd0070cf18ce835", "contracts/governance/required_checks_policy.yaml": "51f9e8cb17df93c3912ae719a6ba95b74f2c666b6b11c79e1728f7002fb3e3b3", ".github/workflows/ci.yml": "145d243beb087cd6a976d45e4455e0747ace3ee3ad9c77160b57fb17f75c63fe", ".github/workflows/pre-commit.yml": "ed0082256d36108f1bdb3d6c66f6f9805295a4f7f738465b0092d0b3fe0199ed", @@ -239,7 +239,7 @@ "block_id": "open-source-platform-truth", "source_hashes": { "contracts/governance/done_signal_policy.yaml": "16e2fc2add45a68406d6a6077ed7b912fe2406905bf1ff68e967bceeda4bd83c", - "contracts/governance/public_readiness_policy.yaml": "1207163d53fb898d20faee2a65d66ee9f29fedd29115364497d9c5ec03c3a7eb", + "contracts/governance/public_readiness_policy.yaml": "eacf27c9d50b2c4295abddecddc23c83f9302140cf82bbd26cd0070cf18ce835", "contracts/governance/required_checks_policy.yaml": "51f9e8cb17df93c3912ae719a6ba95b74f2c666b6b11c79e1728f7002fb3e3b3", ".github/workflows/ci.yml": "145d243beb087cd6a976d45e4455e0747ace3ee3ad9c77160b57fb17f75c63fe", ".github/workflows/pre-commit.yml": "ed0082256d36108f1bdb3d6c66f6f9805295a4f7f738465b0092d0b3fe0199ed", diff --git a/docs/developer_guide.md b/docs/developer_guide.md index d135c12..45e3fdd 100644 --- a/docs/developer_guide.md +++ b/docs/developer_guide.md @@ -103,7 +103,7 @@ This is the shortest accurate ecosystem map for the current repo: | Cursor / generic MCP clients | secondary | real MCP surface exists, but the repo does not need to brand them as the headline | | OpenHands | comparison / ecosystem only | real API + MCP substrate exists, but no dedicated first-party setup page or product flow is shipped | | OpenCode | comparison / ecosystem only | same honest boundary as OpenHands | -| OpenClaw | not a front-door binding today | current repo does not ship a dedicated integration surface or branded flow for it | +| OpenClaw | submission-ready-unlisted | current repo ships a dedicated bundle and install/proof note, but still does not claim a live listing | If you want the client-specific landing page first: diff --git a/docs/mcp.md b/docs/mcp.md index 81af91a..8bdfd4f 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -62,7 +62,7 @@ If you want the shortest ecosystem answer: - **Primary current fit**: Codex and Claude Code, because both can use the current stdio-first local MCP surface directly - **Secondary ecosystem fit**: Cursor and other local MCP-capable clients, because the transport and tool surface already match their integration model - **Comparison-only fit**: OpenHands and OpenCode, because the repo has a real MCP and API substrate they could consume, but it does not yet ship a dedicated first-party setup page or branded workflow for them -- **Not a front-door fit today**: OpenClaw, because the current repo does not ship a specific integration surface or product claim for it +- **Submission-ready-unlisted fit**: OpenClaw and ClawHub, because the current repo now ships a dedicated bundle and install/proof notes while still refusing to claim a live listing Client-specific entry pages: diff --git a/docs/reference/governance_truth.generated.md b/docs/reference/governance_truth.generated.md index fef661c..ceda7bd 100644 --- a/docs/reference/governance_truth.generated.md +++ b/docs/reference/governance_truth.generated.md @@ -29,7 +29,7 @@ - **Repo public readiness gate**: `bash tooling/gates/public_readiness_gate.sh repo` - **Release public readiness gate**: `bash tooling/gates/public_readiness_gate.sh release` - **Platform alignment gate**: `bash tooling/gates/platform_alignment_gate.sh` -- **Required repo surface files**: `23` +- **Required repo surface files**: `28` - **Required package scripts**: `5` - **Release-mode requires tracked public files**: `yes` - **Release-mode requires public repo**: `yes` diff --git a/examples/openclaw/README.md b/examples/openclaw/README.md new file mode 100644 index 0000000..9a5f478 --- /dev/null +++ b/examples/openclaw/README.md @@ -0,0 +1,22 @@ +# Movi For OpenClaw / ClawHub + +Status: `submission-ready-unlisted` + +This page is the OpenClaw-facing integration note for Movi. + +In plain language: the repo now ships a truthful bundle you can hand to an +OpenClaw-style host, but it still refuses to pretend that a live OpenClaw or +ClawHub listing already exists. + +## What Exists Today + +- repo-owned sample config: `../skills/openclaw.mcp.json` +- repo-owned skill note: `../skills/SKILL.md` +- repo-owned bundle manifest: `../skills/manifest.yaml` +- repo-owned install/proof loop: `../skills/install-and-proof.md` + +## What This Does Not Claim + +- a live ClawHub entry +- an official OpenClaw catalog approval +- a browser-extension marketplace listing diff --git a/examples/skills/README.md b/examples/skills/README.md new file mode 100644 index 0000000..5b8f986 --- /dev/null +++ b/examples/skills/README.md @@ -0,0 +1,30 @@ +# Movi Agent Bundle + +Status: `submission-ready-unlisted` + +This directory is the repo-owned agent/plugin bundle shelf for Movi. + +Use it when you want one place that answers: + +- how to wire Movi into Codex +- how to wire Movi into Claude Code +- how to hand an OpenClaw-style host a truthful starter bundle +- how to prove the bundle exists without claiming a live listing + +## Included Files + +| File | Role | +| --- | --- | +| `manifest.yaml` | repo-owned bundle summary | +| `SKILL.md` | dedicated skill brief for review-first Movi usage | +| `codex.mcp.json` | Codex sample config | +| `claude-code.mcp.json` | Claude Code sample config | +| `openclaw.mcp.json` | OpenClaw-style sample config | +| `install-and-proof.md` | shortest install and proof loop | + +## Truth Boundary + +- this bundle is **submission-ready-unlisted** +- it does **not** claim a live OpenClaw or ClawHub listing +- it does **not** claim a browser-extension marketplace listing +- it does **not** bypass `review-first -> dry-run -> execute` diff --git a/examples/skills/SKILL.md b/examples/skills/SKILL.md new file mode 100644 index 0000000..771392c --- /dev/null +++ b/examples/skills/SKILL.md @@ -0,0 +1,21 @@ +# Movi Review-First Bundle Skill + +Use this bundle when the goal is: + +1. inspect a batch through Movi MCP +2. keep the work review-first +3. prove the bundle is real without pretending a live marketplace listing exists + +## Read First + +1. `README.md` +2. `docs/mcp.md` +3. `docs/codex_mcp.md` or `docs/claude_code_mcp.md` +4. `examples/openclaw/README.md` when the host is OpenClaw-style + +## Hard Boundaries + +- no hidden execute shortcut +- no hosted SaaS claim +- no live OpenClaw / ClawHub listing claim +- no browser-extension marketplace claim diff --git a/examples/skills/claude-code.mcp.json b/examples/skills/claude-code.mcp.json new file mode 100644 index 0000000..4f7de75 --- /dev/null +++ b/examples/skills/claude-code.mcp.json @@ -0,0 +1,10 @@ +{ + "mcpServers": { + "movi": { + "command": "bash", + "args": [ + "/absolute/path/to/movi-organizer/tooling/runtime/run_mcp_stdio.sh" + ] + } + } +} diff --git a/examples/skills/codex.mcp.json b/examples/skills/codex.mcp.json new file mode 100644 index 0000000..4f7de75 --- /dev/null +++ b/examples/skills/codex.mcp.json @@ -0,0 +1,10 @@ +{ + "mcpServers": { + "movi": { + "command": "bash", + "args": [ + "/absolute/path/to/movi-organizer/tooling/runtime/run_mcp_stdio.sh" + ] + } + } +} diff --git a/examples/skills/install-and-proof.md b/examples/skills/install-and-proof.md new file mode 100644 index 0000000..c53f443 --- /dev/null +++ b/examples/skills/install-and-proof.md @@ -0,0 +1,20 @@ +# Movi Agent Bundle Install And Proof + +Status: `submission-ready-unlisted` + +## Install + +- Codex: use `examples/skills/codex.mcp.json` +- Claude Code: use `examples/skills/claude-code.mcp.json` +- OpenClaw-style hosts: use `examples/skills/openclaw.mcp.json` + +## Proof Loop + +1. `bash tooling/runtime/bootstrap_env.sh` +2. `npm run mcp:tools` +3. `npm run public:readiness` +4. `bash tooling/gates/verify_repo_final.sh` + +## Boundary + +This proves the repo-owned bundle exists. It does not prove a live listing. diff --git a/examples/skills/manifest.yaml b/examples/skills/manifest.yaml new file mode 100644 index 0000000..7dff46b --- /dev/null +++ b/examples/skills/manifest.yaml @@ -0,0 +1,14 @@ +version: 1 +id: movi-agent-bundle +status: submission-ready-unlisted +canonical_repo: xiaojiou176-open/movi-organizer +bundle_surfaces: + - codex.mcp.json + - claude-code.mcp.json + - openclaw.mcp.json + - SKILL.md + - install-and-proof.md +claims_not_made: + - no live OpenClaw listing is claimed + - no live ClawHub listing is claimed + - no browser-extension marketplace listing is claimed diff --git a/examples/skills/openclaw.mcp.json b/examples/skills/openclaw.mcp.json new file mode 100644 index 0000000..4f7de75 --- /dev/null +++ b/examples/skills/openclaw.mcp.json @@ -0,0 +1,10 @@ +{ + "mcpServers": { + "movi": { + "command": "bash", + "args": [ + "/absolute/path/to/movi-organizer/tooling/runtime/run_mcp_stdio.sh" + ] + } + } +} diff --git a/manifest.yaml b/manifest.yaml new file mode 100644 index 0000000..3d4468b --- /dev/null +++ b/manifest.yaml @@ -0,0 +1,33 @@ +version: 1 +id: movi-organizer +technical_name: Movi +status: submission-ready-unlisted +canonical_repo: xiaojiou176-open/movi-organizer +homepage: https://xiaojiou176-open.github.io/movi-organizer/ +current_release: v4.0.5 +published_surfaces: + - github-repo + - github-pages-front-door + - github-releases +submission_ready_surfaces: + - codex-mcp-bundle + - claude-code-mcp-bundle + - openclaw-public-ready-bundle + - agent-skill-bundle +truth_routes: + storefront: README.md + distribution: DISTRIBUTION.md + integrations: INTEGRATIONS.md + mcp: docs/mcp.md + codex: docs/codex_mcp.md + claude: docs/claude_code_mcp.md + skills_bundle: examples/skills/README.md + openclaw_bundle: examples/openclaw/README.md +claims_not_made: + - no live OpenClaw listing is claimed + - no live ClawHub listing is claimed + - no browser extension marketplace listing is claimed + - no hosted SaaS is claimed +operator_only_follow_through: + - publish any live registry or catalog entry + - rerun fresh verification after publication before changing public copy diff --git a/package-lock.json b/package-lock.json index cca510b..9e96933 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "movi-organizer", - "version": "0.0.1", + "version": "4.0.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "movi-organizer", - "version": "0.0.1", + "version": "4.0.5", "devDependencies": { "@stylistic/stylelint-plugin": "^3.1.3", "stylelint": "^16.23.0", diff --git a/package.json b/package.json index 3bf4703..8f46e1f 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,8 @@ "name": "movi-organizer", "description": "Review-first local AI file organizer with stdio-first MCP for Codex and Claude Code", "private": true, - "version": "0.0.1", + "version": "4.0.5", + "packageManager": "npm@11.6.0", "engines": { "node": ">=24 <25" }, diff --git a/tests/unit/test_open_source_readiness_surfaces.py b/tests/unit/test_open_source_readiness_surfaces.py index 654eb82..2f9c4c1 100644 --- a/tests/unit/test_open_source_readiness_surfaces.py +++ b/tests/unit/test_open_source_readiness_surfaces.py @@ -8,6 +8,7 @@ def test_open_source_surface_files_exist() -> None: required = [ + ".nvmrc", "LICENSE", "NOTICE", "THIRD_PARTY_NOTICES.md", @@ -20,6 +21,10 @@ def test_open_source_surface_files_exist() -> None: ".github/ISSUE_TEMPLATE/bug_report.yml", ".github/ISSUE_TEMPLATE/documentation.yml", ".github/ISSUE_TEMPLATE/feature_request.yml", + "manifest.yaml", + "examples/skills/README.md", + "examples/skills/SKILL.md", + "examples/openclaw/README.md", "docs/architecture.md", "docs/usage.md", "docs/open_source_runbook.md", @@ -38,6 +43,8 @@ def test_pyproject_and_dependabot_reflect_open_source_defaults() -> None: assert 'license-files = ["LICENSE", "NOTICE"]' in pyproject assert 'directory: "/"' in dependabot assert 'directory: "/apps/webui"' in dependabot + assert '"version": "4.0.5"' in package_json + assert '"packageManager": "npm@11.6.0"' in package_json assert '"public:readiness": "bash tooling/gates/public_readiness_gate.sh release"' in package_json assert '"platform:align": "bash tooling/gates/platform_alignment_gate.sh"' in package_json diff --git a/tests/unit/test_public_readiness_gate.py b/tests/unit/test_public_readiness_gate.py index 5f2babe..cd15ce2 100644 --- a/tests/unit/test_public_readiness_gate.py +++ b/tests/unit/test_public_readiness_gate.py @@ -548,6 +548,102 @@ def test_check_public_platform_state_fails_when_open_security_alerts_exist(tmp_p assert "zero open GitHub secret scanning alerts" in out +def test_check_public_platform_state_accepts_explicit_code_scanning_allowlist(tmp_path: Path) -> None: + repo = tmp_path / "repo" + bin_dir = tmp_path / "bin" + bin_dir.mkdir(parents=True) + (repo / "contracts" / "governance").mkdir(parents=True, exist_ok=True) + (repo / "contracts" / "governance" / "public_readiness_policy.yaml").write_text( + "\n".join( + [ + "version: 1", + "default_branch: main", + "accepted_code_scanning_rules_contract: contracts/governance/code_scanning_alert_allowlist.yaml", + "release_mode:", + " require_public_repo: true", + " require_pvr: true", + " require_branch_protection: true", + " require_zero_code_scanning_alerts: true", + " require_zero_secret_scanning_alerts: true", + ] + ) + + "\n", + encoding="utf-8", + ) + (repo / "contracts" / "governance" / "code_scanning_alert_allowlist.yaml").write_text( + "\n".join( + [ + "version: 1", + "status: active", + "accepted_rule_ids:", + " - CIIBestPracticesID", + " - MaintainedID", + " - CodeReviewID", + ] + ) + + "\n", + encoding="utf-8", + ) + gh = bin_dir / "gh" + gh.write_text( + "\n".join( + [ + "#!/bin/sh", + 'if [ "$1" = "repo" ] && [ "$2" = "view" ]; then', + ( + " printf '%s\\n' " + '\'{"nameWithOwner":"demo/repo","isPrivate":false,"viewerPermission":"ADMIN",' + '"defaultBranchRef":{"name":"main"}}\'' + ), + " exit 0", + "fi", + 'if [ "$1" = "api" ] && [ "$2" = "repos/demo/repo/private-vulnerability-reporting" ]; then', + " printf '%s\\n' '{\"enabled\":true}'", + " exit 0", + "fi", + 'if [ "$1" = "api" ] && [ "$2" = "repos/demo/repo/branches/main/protection" ]; then', + ' printf \'%s\\n\' \'{"required_status_checks":{"contexts":["quality-gate-full"]}}\'', + " exit 0", + "fi", + 'if [ "$1" = "api" ] && [ "$2" = "repos/demo/repo/code-scanning/alerts?state=open&per_page=100" ]; then', + ( + " printf '%s\\n' " + '\'[{"rule":{"id":"CIIBestPracticesID"}},{"rule":{"id":"MaintainedID"}},{"rule":{"id":"CodeReviewID"}}]\'' + ), + " exit 0", + "fi", + 'if [ "$1" = "api" ] && [ "$2" = "repos/demo/repo/secret-scanning/alerts?state=open&per_page=100" ]; then', + " printf '%s\\n' '[]'", + " exit 0", + "fi", + 'echo "unsupported gh args: $*" >&2', + "exit 1", + ] + ) + + "\n", + encoding="utf-8", + ) + gh.chmod(0o755) + env = dict(os.environ) + env["PATH"] = str(bin_dir) + os.pathsep + env.get("PATH", "") + + proc = _run( + [ + sys.executable, + str(REPO_ROOT / "tooling" / "scripts" / "check_public_platform_state.py"), + "--root", + str(repo), + "--mode", + "release", + ], + repo, + env=env, + ) + out = proc.stdout + proc.stderr + assert proc.returncode == 0, out + assert "public-platform-state: passed" in out + + def test_public_readiness_gate_honors_explicit_target_root_from_outside_repo(tmp_path: Path) -> None: repo = tmp_path / "repo" (repo / "docs").mkdir(parents=True) diff --git a/tooling/scripts/check_public_platform_state.py b/tooling/scripts/check_public_platform_state.py index 5febb28..66e462b 100755 --- a/tooling/scripts/check_public_platform_state.py +++ b/tooling/scripts/check_public_platform_state.py @@ -18,6 +18,22 @@ def _load_policy(path: Path) -> dict: return payload +def _load_optional_allowlist(root: Path, policy: dict) -> set[str]: + raw_path = str(policy.get("accepted_code_scanning_rules_contract", "")).strip() + if not raw_path: + return set() + allowlist_path = (root / raw_path).resolve() + if not allowlist_path.exists(): + return set() + payload = yaml.safe_load(allowlist_path.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise SystemExit(f"invalid code-scanning allowlist: {allowlist_path}") + rows = payload.get("accepted_rule_ids", []) + if not isinstance(rows, list): + raise SystemExit("invalid code-scanning allowlist: accepted_rule_ids must be a list") + return {str(item).strip() for item in rows if str(item).strip()} + + def _run(cmd: list[str], cwd: Path) -> subprocess.CompletedProcess[str] | None: try: return subprocess.run(cmd, cwd=str(cwd), text=True, capture_output=True, check=False) @@ -37,6 +53,32 @@ def _count_alerts(proc: subprocess.CompletedProcess[str] | None) -> int | None: raise SystemExit("invalid GitHub alerts payload: expected a list") +def _load_alerts(proc: subprocess.CompletedProcess[str] | None) -> list[dict] | None: + if proc is None or proc.returncode != 0: + return None + text = proc.stdout.strip() + if not text: + return [] + payload = json.loads(text) + if isinstance(payload, list): + return [entry for entry in payload if isinstance(entry, dict)] + raise SystemExit("invalid GitHub alerts payload: expected a list") + + +def _extract_code_scanning_rule_id(alert: dict) -> str: + for candidate in ( + alert.get("rule_id"), + alert.get("ruleId"), + (alert.get("rule") or {}).get("id") if isinstance(alert.get("rule"), dict) else None, + ((alert.get("tool") or {}).get("rule") or {}).get("id") + if isinstance(alert.get("tool"), dict) and isinstance((alert.get("tool") or {}).get("rule"), dict) + else None, + ): + if isinstance(candidate, str) and candidate.strip(): + return candidate.strip() + return "" + + def main() -> int: parser = argparse.ArgumentParser(description="Check GitHub platform state for public/open-source readiness") parser.add_argument("--root", default=".") @@ -47,6 +89,7 @@ def main() -> int: root = Path(args.root).resolve() policy = _load_policy(root / args.policy) + accepted_code_scanning_rules = _load_optional_allowlist(root, policy) release_policy = policy.get("release_mode", {}) if not isinstance(release_policy, dict): raise SystemExit("invalid public readiness policy: release_mode must be a mapping") @@ -65,6 +108,9 @@ def main() -> int: "code_scanning_status_code": None, "secret_scanning_status_code": None, "code_scanning_open_alerts": None, + "accepted_code_scanning_rule_ids": sorted(accepted_code_scanning_rules), + "accepted_code_scanning_open_alerts": None, + "blocking_code_scanning_open_alerts": None, "secret_scanning_open_alerts": None, "platform_query_state": "unknown", "issues": issues, @@ -100,6 +146,18 @@ def main() -> int: payload["secret_scanning_status_code"] = None if secret_scanning_proc is None else secret_scanning_proc.returncode payload["code_scanning_open_alerts"] = _count_alerts(code_scanning_proc) payload["secret_scanning_open_alerts"] = _count_alerts(secret_scanning_proc) + code_scanning_alerts = _load_alerts(code_scanning_proc) + if code_scanning_alerts is not None: + accepted_count = 0 + blocking_count = 0 + for alert in code_scanning_alerts: + rule_id = _extract_code_scanning_rule_id(alert) + if rule_id and rule_id in accepted_code_scanning_rules: + accepted_count += 1 + else: + blocking_count += 1 + payload["accepted_code_scanning_open_alerts"] = accepted_count + payload["blocking_code_scanning_open_alerts"] = blocking_count viewer_permission = str(payload.get("viewer_permission") or "").upper() limited_permission = viewer_permission in {"READ", "TRIAGE", ""} @@ -145,6 +203,9 @@ def main() -> int: ) else: issues.append("release mode requires GitHub code scanning alerts to be queryable") + elif isinstance(payload.get("blocking_code_scanning_open_alerts"), int): + if int(payload["blocking_code_scanning_open_alerts"]) > 0: + issues.append("release mode requires zero open GitHub code scanning alerts outside the accepted-exception contract") elif isinstance(code_scanning_open_alerts, int) and code_scanning_open_alerts > 0: issues.append("release mode requires zero open GitHub code scanning alerts") if args.mode == "release" and bool(release_policy.get("require_zero_secret_scanning_alerts", False)):