From 187ccf8441fae808de037132d95405f5e7b10901 Mon Sep 17 00:00:00 2001 From: Gale W Date: Thu, 20 Aug 2026 21:46:44 -0400 Subject: [PATCH 1/4] cleanup: centralize plugin maintenance state Why: Socket-owned plugins were recreating redundant environments and caches, while historical nested docs and the retired API documentation skill remained published. Breaking: Permanently removes the maintain-project-api skill and its maintain-project-docs dispatch route. Verification: uv run python -B scripts/validate_socket.py --profile full Post-validation plugin-local generated-state inventory: zero --- CONTRIBUTING.md | 15 +- README.md | 5 +- ROADMAP.md | 8 +- docs/maintainers/automation-suitability.md | 1 - plugins/agent-engineering-skills/AGENTS.md | 8 +- .../docs/maintainers/codex-hooks-guidance.md | 107 --- .../maintainers/codex-subagent-guidance.md | 125 --- .../agent-engineering-skills/pyproject.toml | 12 - plugins/agent-engineering-skills/uv.lock | 138 --- plugins/agent-portability-skills/.gitignore | 8 - plugins/agent-portability-skills/AGENTS.md | 15 +- .../codex-subagent-skill-guidance.md | 67 -- .../agent-portability-skills/pyproject.toml | 21 - .../bootstrap-skills-plugin-repo/SKILL.md | 4 +- .../codex-subagent-skill-guidance.md | 62 ++ plugins/agent-portability-skills/uv.lock | 362 -------- plugins/agentdeck/docs/thread-title-hooks.md | 204 ----- .../.github/scripts/validate_repo_docs.sh | 16 +- plugins/apple-dev-skills/.gitignore | 5 - plugins/apple-dev-skills/AGENTS.md | 8 +- plugins/apple-dev-skills/CONTRIBUTING.md | 5 +- plugins/apple-dev-skills/README.md | 5 +- plugins/apple-dev-skills/ROADMAP.md | 3 +- .../app-extension-workflows-plan.md | 30 - .../execution-split-and-inference-plan.md | 153 ---- .../docs/maintainers/reality-audit.md | 12 +- plugins/apple-dev-skills/pyproject.toml | 18 - .../icon-composer-app-icon-workflow/SKILL.md | 4 +- plugins/apple-dev-skills/uv.lock | 224 ----- plugins/professional-skills/AGENTS.md | 7 +- plugins/professional-skills/pyproject.toml | 12 - plugins/professional-skills/uv.lock | 138 --- plugins/python-skills/.gitignore | 5 - plugins/python-skills/AGENTS.md | 15 +- plugins/python-skills/pyproject.toml | 21 - plugins/python-skills/uv.lock | 373 -------- .../skills/maintain-project-api/SKILL.md | 89 -- .../maintain-project-api/agents/openai.yaml | 4 - .../assets/API.template.md | 83 -- .../config/api-customization.template.yaml | 154 ---- .../references/api-config-schema.md | 28 - .../references/api-customization.md | 19 - .../references/fix-policies.md | 8 - .../references/output-contract.md | 16 - ...ject-api-maintenance-automation-prompts.md | 18 - .../references/section-schema.md | 52 -- .../references/style-rules.md | 10 - .../scripts/maintain_project_api.py | 823 ------------------ .../skills/maintain-project-docs/SKILL.md | 13 +- .../maintain-project-docs/agents/openai.yaml | 2 +- .../references/document-boundaries.md | 2 - ...ect-docs-maintenance-automation-prompts.md | 6 +- .../scripts/maintain_project_docs.py | 120 ++- .../repository-operations-workflow/SKILL.md | 2 +- scripts/export_hermes_skills.py | 1 - scripts/validate_socket.py | 113 ++- skills.sh.json | 1 - skills/bootstrap-skills-plugin-repo/SKILL.md | 4 +- .../codex-subagent-skill-guidance.md | 62 ++ skills/maintain-project-api/SKILL.md | 89 -- .../maintain-project-api/agents/openai.yaml | 4 - .../assets/API.template.md | 83 -- .../config/api-customization.template.yaml | 154 ---- .../references/api-config-schema.md | 28 - .../references/api-customization.md | 19 - .../references/fix-policies.md | 8 - .../references/output-contract.md | 16 - ...ject-api-maintenance-automation-prompts.md | 18 - .../references/section-schema.md | 52 -- .../references/style-rules.md | 10 - .../scripts/maintain_project_api.py | 823 ------------------ skills/maintain-project-docs/SKILL.md | 13 +- .../maintain-project-docs/agents/openai.yaml | 2 +- .../references/document-boundaries.md | 2 - ...ect-docs-maintenance-automation-prompts.md | 6 +- .../scripts/maintain_project_docs.py | 120 ++- .../repository-operations-workflow/SKILL.md | 2 +- tests/test_repository_docs_workflow.py | 40 +- tests/test_validate_socket.py | 40 +- 79 files changed, 549 insertions(+), 4826 deletions(-) delete mode 100644 plugins/agent-engineering-skills/docs/maintainers/codex-hooks-guidance.md delete mode 100644 plugins/agent-engineering-skills/docs/maintainers/codex-subagent-guidance.md delete mode 100644 plugins/agent-engineering-skills/pyproject.toml delete mode 100644 plugins/agent-engineering-skills/uv.lock delete mode 100644 plugins/agent-portability-skills/.gitignore delete mode 100644 plugins/agent-portability-skills/docs/maintainers/codex-subagent-skill-guidance.md delete mode 100644 plugins/agent-portability-skills/pyproject.toml create mode 100644 plugins/agent-portability-skills/skills/bootstrap-skills-plugin-repo/references/codex-subagent-skill-guidance.md delete mode 100644 plugins/agent-portability-skills/uv.lock delete mode 100644 plugins/agentdeck/docs/thread-title-hooks.md delete mode 100644 plugins/apple-dev-skills/.gitignore delete mode 100644 plugins/apple-dev-skills/docs/maintainers/app-extension-workflows-plan.md delete mode 100644 plugins/apple-dev-skills/docs/maintainers/execution-split-and-inference-plan.md delete mode 100644 plugins/apple-dev-skills/pyproject.toml delete mode 100644 plugins/apple-dev-skills/uv.lock delete mode 100644 plugins/professional-skills/pyproject.toml delete mode 100644 plugins/professional-skills/uv.lock delete mode 100644 plugins/python-skills/.gitignore delete mode 100644 plugins/python-skills/pyproject.toml delete mode 100644 plugins/python-skills/uv.lock delete mode 100644 plugins/repository-skills/skills/maintain-project-api/SKILL.md delete mode 100644 plugins/repository-skills/skills/maintain-project-api/agents/openai.yaml delete mode 100644 plugins/repository-skills/skills/maintain-project-api/assets/API.template.md delete mode 100644 plugins/repository-skills/skills/maintain-project-api/config/api-customization.template.yaml delete mode 100644 plugins/repository-skills/skills/maintain-project-api/references/api-config-schema.md delete mode 100644 plugins/repository-skills/skills/maintain-project-api/references/api-customization.md delete mode 100644 plugins/repository-skills/skills/maintain-project-api/references/fix-policies.md delete mode 100644 plugins/repository-skills/skills/maintain-project-api/references/output-contract.md delete mode 100644 plugins/repository-skills/skills/maintain-project-api/references/project-api-maintenance-automation-prompts.md delete mode 100644 plugins/repository-skills/skills/maintain-project-api/references/section-schema.md delete mode 100644 plugins/repository-skills/skills/maintain-project-api/references/style-rules.md delete mode 100644 plugins/repository-skills/skills/maintain-project-api/scripts/maintain_project_api.py create mode 100644 skills/bootstrap-skills-plugin-repo/references/codex-subagent-skill-guidance.md delete mode 100644 skills/maintain-project-api/SKILL.md delete mode 100644 skills/maintain-project-api/agents/openai.yaml delete mode 100644 skills/maintain-project-api/assets/API.template.md delete mode 100644 skills/maintain-project-api/config/api-customization.template.yaml delete mode 100644 skills/maintain-project-api/references/api-config-schema.md delete mode 100644 skills/maintain-project-api/references/api-customization.md delete mode 100644 skills/maintain-project-api/references/fix-policies.md delete mode 100644 skills/maintain-project-api/references/output-contract.md delete mode 100644 skills/maintain-project-api/references/project-api-maintenance-automation-prompts.md delete mode 100644 skills/maintain-project-api/references/section-schema.md delete mode 100644 skills/maintain-project-api/references/style-rules.md delete mode 100644 skills/maintain-project-api/scripts/maintain_project_api.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e0bd7980b..9d0b297be 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,13 +65,19 @@ A root change is ready for review when: ### Runtime Config -The root superproject uses a small `uv` environment for maintainer tooling: +The root superproject uses one `uv` environment for root and monorepo-owned +child maintainer tooling: ```bash uv sync --dev ``` -If a root or child-repo workflow depends on Python quality tooling, declare that tooling in the relevant repo's `pyproject.toml` dev dependencies rather than assuming a machine-global install. Treat `pytest`, `ruff`, and `mypy` as the normal Python maintainer baseline when the repo's shipped validation surface uses them. +Declare Python quality tooling in the root `pyproject.toml` dev dependencies +rather than creating child environments or assuming a machine-global install. +Treat `pytest`, `ruff`, and `mypy` as the normal Python maintainer baseline when +the shipped validation surface uses them. Root validation redirects tool caches +to `.codex/.cache/` and disables bytecode writes so checks do not recreate +generated state inside plugin payloads. At the `socket` root, run `uv run mypy` without a path argument. The root mypy config intentionally checks root maintainer scripts, root tests, and package-shaped child maintainer code instead of crawling every standalone skill `scripts/` directory as one Python module namespace. @@ -249,7 +255,10 @@ and final marketplace refresh. See for the complete contract. Do not invoke the internal Python modules or create a direct-main shortcut. -If the changed surface also introduces or expands Python-backed repo checks, add the required tools to the repo-local `uv` dev group and document the corresponding `uv run pytest`, `uv run ruff check .`, and `uv run mypy` commands where that repo's contributors will actually look. +If the changed surface also introduces or expands Python-backed repo checks, +add the required tools to the root `uv` dev group and add the focused child +command to `scripts/validate_socket.py` instead of creating a child +`pyproject.toml`, lockfile, environment, or cache root. When editing docs, also review the rendered Markdown structure and cross-links for the files you changed. diff --git a/README.md b/README.md index 4372b485f..67281eb91 100644 --- a/README.md +++ b/README.md @@ -169,7 +169,10 @@ Current Socket catalog shape: - `network-protocol-skills`: modern networking and application-protocol workflows for transport selection, HTTP/3 and QUIC planning, Media over QUIC draft-aware guidance, WebRTC signaling/media/data-channel work, and protocol diagnostics with stack-plugin handoffs - `professional-skills`: career and professional workflow guidance, starting with Dice job search and its bundled read-only remote MCP configuration - `python-skills`: Python runtime and tooling workflows, including local-first agent services, FastAPI and FastMCP service maintenance, and pytest-based testing; see the [Python skills expansion plan](./docs/maintainers/python-skills-plugin-plan.md) for maintainer details -- `repository-skills`: routed Git and GitHub collaboration, README, CONTRIBUTING, AGENTS, API, ROADMAP, repository settings, protected-main release, and Codex GUI worktree workflow guidance; its portable repository and documentation workflows are available from the Hermes skill tap +- `repository-skills`: routed Git and GitHub collaboration, README, + CONTRIBUTING, AGENTS, ROADMAP, repository settings, protected-main release, + and Codex GUI worktree workflow guidance; its portable repository and + documentation workflows are available from the Hermes skill tap - `reverse-engineering-skills`: artifact triage, preservation, exact-build comparison, decompiler review, Apple Mach-O/runtime/signing/Apple Silicon/dyld/dynamic/kernel research, Cutter/Rizin, Malimite, Ghidra, Hopper, .NET, Unity and IL2CPP, and reproducible security evidence workflows - `server-side-jvm`: server-side JVM, Java, Scala, Google ADK agent-service, Gradle, Maven, SBT, and testing workflow guidance, with future Clojure support planned - `server-side-swift`: canonical-workspace service components, Vapor, Leaf-rendered websites and HTML email, Hummingbird, persistence, OpenAPI/RPC, SwiftNIO, observability, auth, app sync, Soto-first AWS integration, native macOS/Homebrew local development, and GitHub-only OCI/Fly.io deployment guidance plus the source-bundled `server-swift-steward` custom-agent definition diff --git a/ROADMAP.md b/ROADMAP.md index 3ce4c24e7..1447dc54e 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1190,7 +1190,9 @@ and test/production deployments for GitHub Actions. - [x] Add `game-dev-skills:game-porting-toolkit-workflow` to choose stable GPTK 3 evaluation/shader-conversion work or beta-sensitive GPTK 4 source-port work, then load Apple’s upstream GPTK 4 skills instead of duplicating their corpus. - [x] Add `game-dev-skills:metalfx-game-rendering-workflow`, `metal-asset-streaming-workflow`, and `metal-neural-rendering-workflow` for native MetalFX integration, GPU asset residency/streaming, and carefully gated experimental neural rendering. - [x] Overhaul `agent-portability-skills` so its docs, tests, generated bootstrap content, and sync audit logic target Codex/OpenAI plus the open `.agents/skills` discovery mirror only. Remove stale expectations for retired child maintainer docs such as reality-audit and install-surface docs, and keep the wording away from unsupported non-Codex or generic multi-agent surfaces. -- [x] Consolidate README, CONTRIBUTING, AGENTS, API, coordinated docs, and ROADMAP maintenance workflows into `repository-skills` so repository operations and repository documentation have one owner. +- [x] Consolidate README, CONTRIBUTING, AGENTS, coordinated docs, and ROADMAP + maintenance workflows into `repository-skills` so repository operations and + repository documentation have one owner. - [x] Add a first `agent-engineering-skills:design-agent-automation-workflow` planning skill for agent and automation design. It chooses between Codex app automations, `codex exec`, Codex subagents, OpenAI Agents SDK services, LangGraph graphs, Hermes-specific workflows, or no automation yet while delegating stack-specific implementation to the owning plugin. - [x] Added `agent-engineering-skills:design-agent-eval-workflow` for agent, skill, prompt, and automation eval planning, and skewed automation guidance toward safe full automation with exact escalation gates instead of broad human review. - [x] Replaced the legacy Apple app guidance and sync routes with the unified `bootstrap-xcode-workspace` workflow and its managed `just align` contract. @@ -1286,7 +1288,9 @@ and test/production deployments for GitHub Actions. - Loosened coordinated Codex subagent guidance so skills preserve OpenAI's explicit-trigger model while allowing narrower workflow guidance, such as Codex Security repository-wide scans, to ask for and use subagents when the task depends on parallel file-pass review. - Added coordinated Codex subagent guidance across `agent-portability-skills` and `agent-engineering-skills`, grounding skill wording in OpenAI's current explicit-trigger `subagents` model while keeping the root docs clear about why the pass belongs in `socket`. - Added `repository-skills:codex-gui-worktree-workflow` as the general Codex GUI worktree-first planning surface, while keeping SwiftPM, Xcode, Vapor, Hummingbird, and server-side Swift local environment templates inside their stack-specific plugins. -- Prepared the `v6.1.0` minor release by adding the `maintain-project-api` productivity skill and keeping the monorepo-owned child docs, tests, and shared version surfaces aligned. +- Prepared the `v6.1.0` minor release by expanding repository documentation + maintenance and keeping the monorepo-owned child docs, tests, and shared + version surfaces aligned. - Added explicit `standard` and `subtrees` release-mode guidance, including the pull-only `SpeakSwiftlyServer` rule for `socket` subtree sync. - Published `apple-dev-skills` `v6.0.11` after adding direct regression coverage for SwiftPM-generated `.swiftpm/xcode/package.xcworkspace` classification and synced the released child state back into `socket`. - Prepared the shared `v6.0.11` patch release after fixing `repository-skills:maintain-project-repo` release-helper regressions for initial PR check discovery and approval-only review handling. diff --git a/docs/maintainers/automation-suitability.md b/docs/maintainers/automation-suitability.md index 34aaf3543..35cfaf2f3 100644 --- a/docs/maintainers/automation-suitability.md +++ b/docs/maintainers/automation-suitability.md @@ -152,7 +152,6 @@ as the default Socket maintainer automation runtime. | --- | --- | --- | | `explain-code-slice` | Codex app automation, subagent read phase, or code-owned read phase | It is read-only and explanation-focused. Good for batch reports, but it should not mutate repos. | | `maintain-project-agents` | `codex exec` for one repo; code-owned service for policy rollout | It changes durable agent instructions, so apply mode should be branch-and-PR gated. App automation is good for drift reports. | -| `maintain-project-api` | `codex exec` for one repo; subagents for read-heavy evidence; code-owned service for API-doc inventory | It must avoid invented endpoints and credentials. Best run with repo-grounded evidence and PR review. | | `maintain-project-contributing` | `codex exec` for one repo; code-owned service for standardization campaigns | It owns contributor workflow. Good candidate for automated apply after check-only evidence. | | `maintain-project-readme` | `codex exec` for one repo; code-owned service for product-doc sweep | It keeps README product-focused and hands contributor details to `CONTRIBUTING.md`, which makes it safer for repeated repo-wide application. | | `maintain-project-repo` | `codex exec` first; code-owned service for coordinated rollout | It installs managed scripts and CI wrappers. It is deterministic, but the write surface is broad enough that every repo should get a PR. | diff --git a/plugins/agent-engineering-skills/AGENTS.md b/plugins/agent-engineering-skills/AGENTS.md index 4c697fae0..6f3870cb8 100644 --- a/plugins/agent-engineering-skills/AGENTS.md +++ b/plugins/agent-engineering-skills/AGENTS.md @@ -14,6 +14,12 @@ documentation rules. ## Validation +Run from the Socket repository root so the shared maintainer environment and +cache policy apply: + ```bash -uv run pytest +uv run python -B -m pytest \ + plugins/agent-engineering-skills/skills/design-agent-automation-workflow/tests \ + plugins/agent-engineering-skills/skills/design-agent-eval-workflow/tests \ + -o cache_dir=.codex/.cache/pytest ``` diff --git a/plugins/agent-engineering-skills/docs/maintainers/codex-hooks-guidance.md b/plugins/agent-engineering-skills/docs/maintainers/codex-hooks-guidance.md deleted file mode 100644 index c229b8bb2..000000000 --- a/plugins/agent-engineering-skills/docs/maintainers/codex-hooks-guidance.md +++ /dev/null @@ -1,107 +0,0 @@ -# Codex Hooks Guidance - -Use this document when an Agent Engineering skill needs to explain or audit -OpenAI Codex Hooks without turning hooks into a default requirement for every -repository. - -This is a maintainer-focused translation of the official Codex Hooks docs for repository guidance, AGENTS maintenance, and local maintainer-tooling work. - -## Official Model - -Codex Hooks are a Codex extensibility framework. They run scripts at specific Codex lifecycle events so a repository, user, or managed environment can add checks, context, logging, or guardrails around a Codex session. - -Hooks are enabled by default. To disable them in `config.toml`, use the -canonical feature key: - -```toml -[features] -hooks = false -``` - -`codex_hooks` still works as a deprecated alias, but skill and repo guidance -should use `features.hooks`. Do not use older plugin-gating keys such as -`features.plugin_hooks`; current Codex releases expose plugin support through -the stable plugin feature and hook support through `features.hooks`. - -Codex discovers hooks next to active config layers in either `hooks.json` or inline `[hooks]` tables in `config.toml`. The most common locations are: - -- `~/.codex/hooks.json` -- `~/.codex/config.toml` -- `/.codex/hooks.json` -- `/.codex/config.toml` - -Project-local hooks load only when the project `.codex/` layer is trusted. - -## Events To Name Carefully - -The current official Codex Hooks guide documents these main events: - -- `SessionStart`: adds context when a session starts, resumes, or clears. -- `PreToolUse`: can inspect supported tool calls before they run. -- `PermissionRequest`: can allow, deny, or decline to decide on approval requests. -- `PostToolUse`: can inspect supported tool results after they run. -- `PreCompact`: can inspect or stop a compaction before it runs. -- `PostCompact`: can inspect or stop after a compaction completes. -- `UserPromptSubmit`: can inspect the user prompt before it is sent. -- `SubagentStart`: can add context when a subagent starts. -- `SubagentStop`: can inspect subagent completion and ask Codex to continue - the subagent flow. -- `Stop`: can tell Codex to continue after a turn stops. - -Do not describe every event as a hard enforcement boundary. `PreToolUse` and `PostToolUse` are useful guardrails, but the docs state that they do not intercept every possible tool path today. - -## Good Fits - -Hooks guidance is useful when a repository needs to document or audit Codex runtime behavior such as: - -- repo-local policy checks before selected shell, edit, or MCP tool calls -- approval-request policy for sensitive tools or permission escalations -- turn-end validation reminders or continuation prompts -- session-start context that points Codex at repo-local conventions -- prompt checks that prevent accidental secrets or sensitive data in user prompts -- logging or analytics that the repository or organization intentionally owns - -## Poor Fits - -Avoid recommending hooks when ordinary repo guidance, skills, or maintainer scripts are enough. - -Poor fits include: - -- replacing `AGENTS.md` with hook-generated instructions -- treating hooks as the only enforcement layer for destructive operations -- adding project-local hooks to untrusted or public repos without a clear review story -- duplicating git pre-commit behavior without explaining why Codex lifecycle timing matters -- using relative hook script paths that break when Codex starts from a subdirectory - -## Skill Wording Pattern - -Use wording like this in applicable skills: - -```markdown -## Codex Hooks Fit - -When a repository documents Codex Hooks, keep the guidance explicit: hooks are enabled by default, may be disabled with `features.hooks = false`, project-local hooks load only from trusted `.codex/` layers, and hooks may live in `hooks.json` or inline `[hooks]` config. - -Good hook guidance names the lifecycle event, the matcher scope, the script location, and the user-visible effect. Do not present hooks as a replacement for AGENTS.md, normal approval policy, tests, or git hooks. -``` - -## Review Checklist - -When auditing hooks guidance, flag wording that: - -- uses deprecated `features.codex_hooks` wording instead of canonical `features.hooks` -- refers to removed or legacy plugin-hook gates such as `features.plugin_hooks` -- implies hooks are disabled by default -- implies project-local hooks load in untrusted projects -- says higher-precedence config layers replace lower-precedence hooks -- treats `PreToolUse` or `PostToolUse` as complete enforcement for all tool paths -- confuses Codex Hooks with git pre-commit hooks or repo-maintenance hook scripts -- uses relative repo-local script paths instead of resolving from the git root or another stable absolute path -- leaves hook behavior vague instead of naming the event, matcher, and expected effect -- omits the trust-review requirement for non-managed command hooks, including - plugin-bundled hooks - -## Official References - -- [OpenAI Codex Hooks](https://developers.openai.com/codex/hooks) -- [OpenAI Codex Configuration Reference](https://developers.openai.com/codex/config-reference) diff --git a/plugins/agent-engineering-skills/docs/maintainers/codex-subagent-guidance.md b/plugins/agent-engineering-skills/docs/maintainers/codex-subagent-guidance.md deleted file mode 100644 index f21bfb364..000000000 --- a/plugins/agent-engineering-skills/docs/maintainers/codex-subagent-guidance.md +++ /dev/null @@ -1,125 +0,0 @@ -# Codex Subagent Guidance - -Use this document for the Codex-specific portion of the broader Agent -Engineering contract. The portable contract in -`orchestrate-agent-work` applies to Codex, ChatGPT Work, Hermes Agent, and -external workers: a coordinator assigns bounded work, workers report terminal -state back to that coordinator, and no worker silently disappears. - -This is a maintainer-focused translation of the official Codex subagent docs -for this repository's documentation, maintenance, and explanation skills. Do -not use it to imply that Codex-only mechanisms exist on other hosts. - -## Official Model - -OpenAI's Codex docs use the term `subagent` for a delegated agent Codex starts -to handle a specific task. A `subagent workflow` is a workflow where Codex runs -parallel agents and combines their results. - -Current Codex releases enable subagent workflows by default. Codex only spawns -subagents when there is an explicit trigger: the user asks for subagents or -parallel agent work, or a narrower skill/plugin workflow tells the agent to ask -first and the user grants explicit permission. Do not write skill guidance that -implies subagents start automatically just because a skill is active. - -Codex ships built-in `default`, `worker`, and `explorer` agents. Custom agents -live in `~/.codex/agents/` for personal agents or `.codex/agents/` for -project-scoped agents, with global limits under `[agents]` in Codex config. A -standalone custom agent file currently requires `name`, `description`, and -`developer_instructions`, and can include supported `config.toml` keys such as -model, sandbox, MCP server, and skill configuration. - -## Model Selection - -Do not put a blanket subagent model rule in generated `AGENTS.md` files or in -global user guidance. Prefer the current Codex default unless a custom role has -a concrete reason to pin a model. - -For bounded read-heavy custom agents, such as repo-maintenance explorers that -scan docs, search code, inspect tests, or prepare draft patch plans for later -review, `gpt-5.4-mini` is a good role-local default. OpenAI's model docs -describe it as a faster, more efficient model for high-volume workloads and -explicitly include subagents in its intended use. Escalate to a stronger model, -or omit the model field and let the parent session choose, when the worker owns -hard architecture synthesis, ambiguous debugging, security reasoning, or -write-plan decisions that need deeper judgment. - -Keep the model choice separate from write ownership. A smaller read-heavy worker -can gather evidence quickly, but the main thread should still review findings, -choose edits, apply patches, and run validation unless the user explicitly -approves a disjoint parallel implementation pass. - -Subagents inherit the parent run's sandbox policy. In interactive sessions, -approval requests can surface from inactive agent threads. In non-interactive -flows, or when a fresh approval cannot be surfaced, an action that needs new -approval fails and Codex returns that failure to the parent workflow. - -Treat subagents as in-run parallel workers. They are not a replacement for -Codex app automations, `codex exec`, repo-local validation, or a durable -external scheduler. - -## Good Fits - -Subagents are useful when the work can be split into bounded, mostly independent read-heavy tasks and returned as concise summaries. - -Every launch must state the task, scope, authority, acceptance criteria, -escalation conditions, and required report. Every terminal report must state -completion, block, cancellation, or failure; outcome; evidence; validation; -uncertainty; and recommended next action. Codex can use its native subagent -tools for that exchange; cross-thread and cross-host work needs a durable -handoff surface instead. - -Good fits in this repository include: - -- documentation source gathering before a maintainer skill edits one target file -- broad repo scans where each worker owns a separate document family or directory -- code-slice exploration where one worker maps call sites while another reads tests or docs -- maintenance audits where one worker checks commands, another checks docs structure, and another checks package or plugin metadata -- review triage where workers return findings with file references instead of raw logs -- CSV-style fan-out where each row is a similar read-heavy audit target and each worker returns one structured result - -## Poor Fits - -Avoid recommending subagents when the work is small, sequential, or primarily one write target. - -Poor fits include: - -- single-file formatting or template normalization -- one focused README, AGENTS, ROADMAP, CONTRIBUTING, ACCESSIBILITY, or API apply pass -- write-heavy parallel edits to the same files or same tightly coupled document set -- tasks where the main agent needs each result immediately before it can choose the next step -- prompts where neither the user nor applicable workflow guidance explicitly calls for subagents, delegation, or parallel agent work -- background scheduling, retries, cross-run state, or queue ownership - -## Skill Wording Pattern - -Use wording like this in applicable skills: - -```markdown -## Codex Subagent Fit - -When the user explicitly requests subagents, or applicable workflow guidance tells the agent to ask and the user grants explicit permission, this skill can split read-heavy discovery into bounded subagent tasks and keep the main thread focused on decisions and final edits. - -Good subagent jobs for this skill: - -- inspect source docs and return concise evidence with links -- audit separate document families or directories -- compare current guidance against one upstream source per worker - -Keep apply-mode edits in the main thread unless the user explicitly asks for parallel implementation and each worker has a disjoint write scope. -``` - -The important parts are: - -- name the explicit trigger for delegation, whether it came directly from the user or from narrower workflow guidance that tells the agent to ask and receive permission before use -- limit subagents to bounded jobs -- prefer read-heavy discovery, triage, tests, and summarization -- ask workers for summaries, findings, and file references instead of raw intermediate output -- keep write ownership clear when parallel edits are intentionally requested -- name sandbox and approval behavior when a skill suggests non-interactive subagent use - -## Official References - -- [OpenAI Codex Subagents](https://developers.openai.com/codex/subagents) -- [OpenAI Codex Subagent concepts](https://developers.openai.com/codex/concepts/subagents) -- [OpenAI Codex Configuration Reference](https://developers.openai.com/codex/config-reference) diff --git a/plugins/agent-engineering-skills/pyproject.toml b/plugins/agent-engineering-skills/pyproject.toml deleted file mode 100644 index 80dd18555..000000000 --- a/plugins/agent-engineering-skills/pyproject.toml +++ /dev/null @@ -1,12 +0,0 @@ -[project] -name = "agent-engineering-skills-maintenance" -version = "10.0.0" -description = "Maintainer-only Python tooling baseline for agent-engineering-skills." -requires-python = ">=3.11" -dependencies = [] - -[dependency-groups] -dev = ["pytest>=9.0.0", "PyYAML>=6.0.0"] - -[tool.pytest.ini_options] -testpaths = ["skills/design-agent-automation-workflow/tests", "skills/design-agent-eval-workflow/tests"] diff --git a/plugins/agent-engineering-skills/uv.lock b/plugins/agent-engineering-skills/uv.lock deleted file mode 100644 index af97bd3f6..000000000 --- a/plugins/agent-engineering-skills/uv.lock +++ /dev/null @@ -1,138 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.11" - -[[package]] -name = "agent-engineering-skills-maintenance" -version = "10.0.0" -source = { virtual = "." } - -[package.dev-dependencies] -dev = [ - { name = "pytest" }, - { name = "pyyaml" }, -] - -[package.metadata] - -[package.metadata.requires-dev] -dev = [ - { name = "pytest", specifier = ">=9.0.0" }, - { name = "pyyaml", specifier = ">=6.0.0" }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "packaging" -version = "26.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "pygments" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, -] - -[[package]] -name = "pytest" -version = "9.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, - { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, - { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, - { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, - { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, - { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, - { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, - { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, - { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] diff --git a/plugins/agent-portability-skills/.gitignore b/plugins/agent-portability-skills/.gitignore deleted file mode 100644 index 0f6adba9e..000000000 --- a/plugins/agent-portability-skills/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -.venv/ -__pycache__/ -.pytest_cache/ -*.pyc - -# Agent plugin repo local runtime state -.codex/plugins/ -.codex/plugins/** diff --git a/plugins/agent-portability-skills/AGENTS.md b/plugins/agent-portability-skills/AGENTS.md index f76ae9341..ecdfa5186 100644 --- a/plugins/agent-portability-skills/AGENTS.md +++ b/plugins/agent-portability-skills/AGENTS.md @@ -23,8 +23,17 @@ This file is the Agent Portability Skills child-repo override for work done from ## Validation +Run from the Socket repository root so the shared maintainer environment and +cache policy apply: + ```bash -uv run pytest -uv run ruff check . -uv run mypy . +uv run python -B -m pytest \ + plugins/agent-portability-skills/tests \ + plugins/agent-portability-skills/skills/bootstrap-skills-plugin-repo/tests \ + plugins/agent-portability-skills/skills/sync-skills-repo-guidance/tests \ + -o cache_dir=.codex/.cache/pytest +uv run ruff check --cache-dir .codex/.cache/ruff/agent-portability-skills \ + plugins/agent-portability-skills +uv run mypy --cache-dir .codex/.cache/mypy/agent-portability-skills \ + plugins/agent-portability-skills ``` diff --git a/plugins/agent-portability-skills/docs/maintainers/codex-subagent-skill-guidance.md b/plugins/agent-portability-skills/docs/maintainers/codex-subagent-skill-guidance.md deleted file mode 100644 index 89b3dfd7c..000000000 --- a/plugins/agent-portability-skills/docs/maintainers/codex-subagent-skill-guidance.md +++ /dev/null @@ -1,67 +0,0 @@ -# Codex Subagent Skill Guidance - -Use this document when `agent-portability-skills` needs to bootstrap or audit guidance about Codex subagents in skills-export and plugin-export repositories. - -This document does not replace OpenAI's Codex docs. It gives these maintainer skills a narrow house pattern for teaching optional subagent use without overstating what Codex does automatically. - -Date checked: 2026-07-19. - -## Official Model - -OpenAI's Codex docs call this feature `subagents`. - -- A `subagent` is a delegated agent that Codex starts to handle a specific task. -- A `subagent workflow` is a workflow where Codex runs parallel agents and combines their results. -- Current Codex releases enable subagent workflows by default, but Codex only spawns subagents when there is an explicit trigger: the user asks for subagents or parallel agent work, or a narrower skill/plugin workflow instructs the agent to ask first and the user grants explicit permission. -- Built-in agents include `default`, `worker`, and `explorer`; project-scoped custom agents live under `.codex/agents/` when a repo intentionally owns that setup. -- Custom agents can set a role-local model. For bounded read-heavy discovery roles, `gpt-5.6-terra` is the current soft default because OpenAI positions it as the faster, lower-cost option for lighter subagent work. Use `gpt-5.6` for harder planning and reasoning, or leave the model unpinned when Codex should choose. -- Subagents are best for bounded read-heavy discovery, tests, triage, log analysis, and summarization. -- Write-heavy parallel work needs clear ownership because multiple agents editing code or docs at once can create conflicts and coordination overhead. - -## What Skills Should Say - -Only add subagent guidance to skills where the workflow has real parallelizable support work. - -Good candidates: - -- docs lookup and source verification skills -- repo-maintenance, metadata, or packaging audits -- broad codebase exploration or explanation skills -- test, log, or CI triage skills -- migration checks where each worker can own one independent surface - -Weak candidates: - -- narrow single-file apply skills -- skills whose primary job is one sequential command -- workflows where one output directly determines the next input -- skills that already have a tight write target with no independent discovery phase - -## Required Wording Points - -When a skill includes a `Codex Subagent Fit` section, it should say: - -- subagents require an explicit trigger, either from the user's request or from narrower workflow guidance that tells the agent to request and receive permission before delegation -- the subagent jobs must be bounded and independently useful -- read-heavy discovery, triage, tests, docs pulling, and summarization are the default fit -- read-heavy custom-agent roles may pin `model = "gpt-5.6-terra"` locally, but generated repo guidance should not turn that into a global rule for every subagent -- workers should return concise evidence, findings, links, or file references instead of raw command logs -- apply-mode or implementation edits should stay in the main thread unless the user asks for parallel implementation and each worker has a disjoint write scope -- plugin-specific guidance can be stricter; for example, Codex Security repository-wide scans may require asking for subagent use because the scan quality depends on parallel file-pass review - -## Review Checklist - -When auditing a skills repo, flag guidance that: - -- says or implies Codex will spawn subagents automatically -- tells agents to delegate merely because a task is complex or lengthy, without a concrete workflow reason -- recommends parallel writes without separate file ownership -- hides token, latency, or coordination costs -- asks subagents to dump raw exploratory output instead of distilled results -- uses older generic `multi-agent` wording where current Codex docs use `subagent` - -## Official References - -- [OpenAI Codex Subagents](https://developers.openai.com/codex/subagents) -- [OpenAI Codex Subagent concepts](https://developers.openai.com/codex/concepts/subagents) -- [OpenAI Codex subagent model guidance](https://learn.chatgpt.com/docs/agent-configuration/subagents#choosing-models-and-reasoning) diff --git a/plugins/agent-portability-skills/pyproject.toml b/plugins/agent-portability-skills/pyproject.toml deleted file mode 100644 index 879d59e4f..000000000 --- a/plugins/agent-portability-skills/pyproject.toml +++ /dev/null @@ -1,21 +0,0 @@ -[project] -name = "agent-portability-skills-maintenance" -version = "10.0.0" -description = "Maintainer-only Python tooling baseline for Agent Portability Skills." -requires-python = ">=3.11" -dependencies = [] - -[dependency-groups] -dev = [ - "mypy>=1.20.1", - "pytest>=9.0.0", - "PyYAML>=6.0.0", - "ruff>=0.14.0", -] - -[tool.pytest.ini_options] -testpaths = [ - "tests", - "skills/bootstrap-skills-plugin-repo/tests", - "skills/sync-skills-repo-guidance/tests", -] diff --git a/plugins/agent-portability-skills/skills/bootstrap-skills-plugin-repo/SKILL.md b/plugins/agent-portability-skills/skills/bootstrap-skills-plugin-repo/SKILL.md index 85a0a583e..abcbd55ca 100644 --- a/plugins/agent-portability-skills/skills/bootstrap-skills-plugin-repo/SKILL.md +++ b/plugins/agent-portability-skills/skills/bootstrap-skills-plugin-repo/SKILL.md @@ -52,7 +52,9 @@ When creating or aligning skills that can benefit from parallel support work, ad - Apply-mode or implementation edits should stay in the main thread unless the user explicitly asks for parallel implementation and each worker has a disjoint write scope. - Plugin-specific guidance can be stricter. For example, Codex Security repository-wide scans may require asking for subagent use because the scan quality depends on parallel file-pass review. -Do not add subagent guidance to every skill by default. Use `docs/maintainers/codex-subagent-skill-guidance.md` to decide whether the target skill has real parallelizable support work. +Do not add subagent guidance to every skill by default. Use +`references/codex-subagent-skill-guidance.md` to decide whether the target skill +has real parallelizable support work. ## Codex Install Guidance diff --git a/plugins/agent-portability-skills/skills/bootstrap-skills-plugin-repo/references/codex-subagent-skill-guidance.md b/plugins/agent-portability-skills/skills/bootstrap-skills-plugin-repo/references/codex-subagent-skill-guidance.md new file mode 100644 index 000000000..1ead0f734 --- /dev/null +++ b/plugins/agent-portability-skills/skills/bootstrap-skills-plugin-repo/references/codex-subagent-skill-guidance.md @@ -0,0 +1,62 @@ +# Codex Subagent Skill Guidance + +Use this reference when bootstrapping or auditing guidance about Codex subagents +in skills-export and plugin-export repositories. It does not replace OpenAI's +Codex documentation; it records the narrow Socket house pattern for optional +subagent guidance. + +Date checked: 2026-07-19. + +## Official Model + +- Codex calls delegated agents `subagents` and their coordinated use a + `subagent workflow`. +- Subagent workflows require an explicit trigger: the user asks for subagents + or parallel work, or narrower workflow guidance asks first and the user grants + permission. +- Built-in roles include `default`, `worker`, and `explorer`. Project-scoped + custom roles belong under `.codex/agents/` only when the repository + intentionally owns that configuration. +- Bounded read-heavy discovery, tests, triage, log analysis, documentation + lookup, and summarization are the normal fit. +- Parallel writes require disjoint ownership because shared edits create merge + conflicts and coordination overhead. + +## What Skills Should Say + +Add a `Codex Subagent Fit` section only when the workflow has independently +useful support work. Good candidates include documentation verification, +metadata or packaging audits, broad codebase exploration, test or CI triage, +and migration checks with separate evidence surfaces. + +Avoid subagent guidance for narrow single-file changes, one sequential command, +workflows where each output determines the next input, or tightly bounded write +targets with no independent discovery phase. + +When subagent guidance is present, require: + +- bounded, independently useful jobs; +- concise findings, evidence, links, or file references instead of raw logs; +- main-thread ownership of apply-mode edits unless the user explicitly requests + parallel implementation with disjoint write scopes; +- local model choices only when a repository intentionally owns them, without + turning one role's choice into a global rule; and +- stricter plugin-specific policy when the owning workflow requires it. + +## Review Checklist + +Flag guidance that: + +- implies Codex delegates automatically; +- recommends delegation merely because work is long or complex; +- recommends parallel writes without separate ownership; +- hides token, latency, or coordination costs; +- requests raw exploratory dumps instead of distilled findings; or +- uses vague `multi-agent` wording where current Codex documentation uses + `subagent`. + +## Official References + +- [OpenAI Codex Subagents](https://developers.openai.com/codex/subagents) +- [OpenAI Codex Subagent concepts](https://developers.openai.com/codex/concepts/subagents) +- [OpenAI Codex subagent model guidance](https://learn.chatgpt.com/docs/agent-configuration/subagents#choosing-models-and-reasoning) diff --git a/plugins/agent-portability-skills/uv.lock b/plugins/agent-portability-skills/uv.lock deleted file mode 100644 index 08d0d8cd8..000000000 --- a/plugins/agent-portability-skills/uv.lock +++ /dev/null @@ -1,362 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.11" -resolution-markers = [ - "python_full_version >= '3.15'", - "python_full_version < '3.15'", -] - -[[package]] -name = "agent-portability-skills-maintenance" -version = "10.0.0" -source = { virtual = "." } - -[package.dev-dependencies] -dev = [ - { name = "mypy" }, - { name = "pytest" }, - { name = "pyyaml" }, - { name = "ruff" }, -] - -[package.metadata] - -[package.metadata.requires-dev] -dev = [ - { name = "mypy", specifier = ">=1.20.1" }, - { name = "pytest", specifier = ">=9.0.0" }, - { name = "pyyaml", specifier = ">=6.0.0" }, - { name = "ruff", specifier = ">=0.14.0" }, -] - -[[package]] -name = "ast-serialize" -version = "0.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/9d/09e27731bd5864a9ce04e3244074e674bb8936bf62b45e0357248717adac/ast_serialize-0.5.0.tar.gz", hash = "sha256:5880091bfe6f4f986f22866375c2e884843e7a0b6343ae41aeea659613d879b6", size = 61157, upload-time = "2026-05-17T17:48:29.429Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/9a/13dde51ba9e15f8b97957ab7cb0120d0e381524d651c6bd630b9c359227f/ast_serialize-0.5.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8f5c14f169eb0972c0c21bada5358b23d6047c76583b005234f865b11f1fa00a", size = 1183520, upload-time = "2026-05-17T17:47:30.831Z" }, - { url = "https://files.pythonhosted.org/packages/37/de/5a7f0a9fe68944f536632a5af84676739c7d2582be42deb082634bf3a754/ast_serialize-0.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7d1a2de9de5be04652f0ed60738356ef94f66db37924a9499fffe98dc491aa0b", size = 1175779, upload-time = "2026-05-17T17:47:32.551Z" }, - { url = "https://files.pythonhosted.org/packages/9c/81/0bb853e76e4f6e9a1855d569003c59e19ffac45f7079d91505d1bb212f92/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be5173fb66f9b49026d9d5a2ff0fc7c7009077107c0eb285b2d60fdf1fe10bd1", size = 1233750, upload-time = "2026-05-17T17:47:34.731Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d3/4cf705beeccc08754d0bbda99aefff26110e209b9a07ac8a6b60eec48531/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8015cd071ac1339924ee2b8098c93e00e155f30a16f40ec9816fcf84f4753f6", size = 1235942, upload-time = "2026-05-17T17:47:36.287Z" }, - { url = "https://files.pythonhosted.org/packages/26/c8/ee097e437ea27dd2b8b227865c875492b585650a5802a22d82b304c8201b/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5499e8797edff2a9186aa313ed382c6b422e798e9332d9953badcee6e69a88f2", size = 1442517, upload-time = "2026-05-17T17:47:38.17Z" }, - { url = "https://files.pythonhosted.org/packages/ff/bd/68063442838f1ba68ec72b5436430bc75b3bb17a1a3c3063f09b0c05ae2b/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6848f2a093fb5548751a9a09bff8fcd229e2bbeb0e3331f391b6ae6d26cd9903", size = 1254081, upload-time = "2026-05-17T17:47:39.826Z" }, - { url = "https://files.pythonhosted.org/packages/50/e2/1e520793bc6a4e4524a6ab022391e827825eaa0c3811828bfdc6852eca26/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:832d4c998e0b091fd60a6d6bceee535483c4d490de9ba85003af835225719261", size = 1259910, upload-time = "2026-05-17T17:47:41.369Z" }, - { url = "https://files.pythonhosted.org/packages/4e/e1/49b60f467979979cfe6913b43948ff25bca971ad0591d181812f163a988e/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:16db7c62ec0b8efe1d7afd283a388d8f74f2605d56032e5a37747d2de8dba027", size = 1250678, upload-time = "2026-05-17T17:47:43.702Z" }, - { url = "https://files.pythonhosted.org/packages/74/ba/66ab9555de6275677566f6574e5ef6c29cb185ea866f643bc06f8280a8ee/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:baf5eb061eb5bccade4128ad42da33787d72f6013809cd1b590376ece8b3c937", size = 1301603, upload-time = "2026-05-17T17:47:46.256Z" }, - { url = "https://files.pythonhosted.org/packages/66/42/6aca9b9abc710014b2be9059689e5dd1679339e78f567ffb4d255a9e2050/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:104e4a35bd7c124173c41760ef9aaea17ddb3f86c65cb643671d59afbe3ee94c", size = 1410332, upload-time = "2026-05-17T17:47:47.899Z" }, - { url = "https://files.pythonhosted.org/packages/47/68/2f76594432a22581ecf878b5e75a9b8601c24b2241cf0bbeb1e21fcf370c/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:36be371028fc1675acb38a331bde160dbab7ff907fdf00b67eb6911aa106951b", size = 1509979, upload-time = "2026-05-17T17:47:50.942Z" }, - { url = "https://files.pythonhosted.org/packages/40/ac/a93c9b58292653f6c595752f677a08e608f903b710594909e9231a389b3b/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:061ee58bdb52341c8201a6df41182a977736bae3b7ded87ca7176ca25a8a47ab", size = 1505002, upload-time = "2026-05-17T17:47:54.093Z" }, - { url = "https://files.pythonhosted.org/packages/14/2e/b278f68c497ee2f1d1576cbbef8db5281cd4a5f2db040537592ac9c8862e/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b15219e9cdc9f53f6f4cb51c009203507228226148c05c5e8fe451c28b435eb3", size = 1456231, upload-time = "2026-05-17T17:47:56.311Z" }, - { url = "https://files.pythonhosted.org/packages/0b/43/419be1c566a4c504cd8fd60ce2f84e790f295495c0f327cfaeadf3d51012/ast_serialize-0.5.0-cp314-cp314t-win32.whl", hash = "sha256:842d1c004bb466c7df036f95fabef789570541922b10976b12f5592a69cf0b38", size = 1058668, upload-time = "2026-05-17T17:47:58.305Z" }, - { url = "https://files.pythonhosted.org/packages/03/6f/c9d4d549295ed05111aeb8853232d1afd9d0a179fddb01eeffbb3a4a6842/ast_serialize-0.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b0c06d760909b095cc466356dfccd05a1c7233a6ca191c020dca2c6a6f16c24c", size = 1101075, upload-time = "2026-05-17T17:48:00.35Z" }, - { url = "https://files.pythonhosted.org/packages/d0/8e/d00c5ab30c58222e07d62956fca86c59d91b9ad32997e633c38b526623a3/ast_serialize-0.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:787baedb0262cc49e8ce37cc15c00ae818e46a165a3b36f5e21ed174998104cb", size = 1075347, upload-time = "2026-05-17T17:48:01.753Z" }, - { url = "https://files.pythonhosted.org/packages/e0/9e/dc2530acb3a60dc6e46d65abf27d1d9f86721694757906a148d90a6860de/ast_serialize-0.5.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0668aa9459cfa8c9c49ddd2163ebcf43088ba045ef7492af6fe22e0098303101", size = 1191380, upload-time = "2026-05-17T17:48:03.738Z" }, - { url = "https://files.pythonhosted.org/packages/26/0a/bd3d18a582f273d6c843d16bb9e22e9e16365ff7991e92f18f798e9f1224/ast_serialize-0.5.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bf683d6363edf2b39eed6b6d4fe22d34b6203867a67e27134d9e2a2680c4bc4a", size = 1183879, upload-time = "2026-05-17T17:48:05.463Z" }, - { url = "https://files.pythonhosted.org/packages/40/ae/1f919100f8620887af58fcc381c61a1f218cdf89c6e155f87b213e61010a/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc22cf0c9be65e71cf88fda130af60d61eb4a79370ad4cfe7900d48a4aa2211", size = 1244529, upload-time = "2026-05-17T17:48:07.008Z" }, - { url = "https://files.pythonhosted.org/packages/c6/ca/6376559dcce707cdbc1d0d9a13c8d3baaaa501e949ce0ebdc4230cd881aa/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f66173891548c9f2726bf27957b41cabce12fa679dc6da505ddbde4d4b3b31cf", size = 1240560, upload-time = "2026-05-17T17:48:08.46Z" }, - { url = "https://files.pythonhosted.org/packages/35/b2/a620e206b5aeb7efbf2710336df57d457cffbb3991076bbcc1147ef9abd4/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e42d729ef2be96a14efbad355093284739e3670ece3e534f82cc8832790911d9", size = 1451172, upload-time = "2026-05-17T17:48:09.922Z" }, - { url = "https://files.pythonhosted.org/packages/fa/e0/4ad5c04c24a40481b2935ce9a0ccdb6023dc8b667167d06ae530cc3512f2/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b725026bafa801dbd7310eb13a75f0a2e370e7e51b2cb225f9d21fcfadf919ee", size = 1265072, upload-time = "2026-05-17T17:48:11.469Z" }, - { url = "https://files.pythonhosted.org/packages/b2/71/4d1d479aa56d0101c40e17720c3d6ac2af7269ea0487a80b18e7bfd1a5b7/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b54f60c1d78767a53b67eaa663f0dfac3afe606aa07f1301572f588b73d64809", size = 1270488, upload-time = "2026-05-17T17:48:13.575Z" }, - { url = "https://files.pythonhosted.org/packages/6d/4f/0de1bbe06f6edef9fde4ed12ca8e7b3ec7e6e2bd4e672c5af487f7957665/ast_serialize-0.5.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:27d51654fc240a1e87e742d353d98eb45b75f62f129086b3596ab53df2ac2a43", size = 1260702, upload-time = "2026-05-17T17:48:15.141Z" }, - { url = "https://files.pythonhosted.org/packages/75/61/e00872439cfdddcc3c1b6cdaa6e5d904ba8e26a18807c67c4e14409d0ca8/ast_serialize-0.5.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c36237c46dd1674542f2109740ea5ea485a169bf1431939ada0434e17934", size = 1311182, upload-time = "2026-05-17T17:48:16.779Z" }, - { url = "https://files.pythonhosted.org/packages/76/8e/699a5b955f7926956c95e9e1d74132acad73c2fe7a426f94da89123c20aa/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1943db345233cc7194a470f13afa9c59772c0b123dea0c9414c4d4ca54369759", size = 1421410, upload-time = "2026-05-17T17:48:18.527Z" }, - { url = "https://files.pythonhosted.org/packages/a9/ae/d5b7626874478997adc7a29ab28accf21e596fb590c944290401dfd0b29e/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df1c00022cbbcb064bfaa505aa9c9295362443ce5dacb459d1331d3da353f887", size = 1516587, upload-time = "2026-05-17T17:48:20.133Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ce/b59e02a82d9c4244d64cde502e0b00e83e38816abe19155ceb5437402c7f/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:cae65289fc456fde04af979a2be09302ef5d8ab92ef23e596d6746dc267ada27", size = 1515171, upload-time = "2026-05-17T17:48:21.921Z" }, - { url = "https://files.pythonhosted.org/packages/8b/38/d8d90042747d05aa08d4efcf1c99035a5f670a6bf4c214d31644392afbca/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:239a4c354e8d676e9d94631d1d4a64edc6b266f86ff3a5a80aedd344f342c01d", size = 1464668, upload-time = "2026-05-17T17:48:23.544Z" }, - { url = "https://files.pythonhosted.org/packages/dd/51/5b840c4df7334104cecffa28f23904fe81ca89ca223d2450e288de39fd3c/ast_serialize-0.5.0-cp39-abi3-win32.whl", hash = "sha256:143a4ef63285a075871908fda3672dc21864b83a8ec3ee12304aa3e4c5387b9a", size = 1068311, upload-time = "2026-05-17T17:48:25.027Z" }, - { url = "https://files.pythonhosted.org/packages/41/11/ca5672c7d491825bc4cd6702dea106a6b60d928707712ec257c7833ae476/ast_serialize-0.5.0-cp39-abi3-win_amd64.whl", hash = "sha256:cf25572c526add400f26a4750dc6ce0c3bb93fc1f75e7ae0cad4ce4f2cd5c590", size = 1108931, upload-time = "2026-05-17T17:48:26.591Z" }, - { url = "https://files.pythonhosted.org/packages/45/19/cc8bd127d28a43da249aa955cfd164cf8fd534e79e42cea96c4854d72fd0/ast_serialize-0.5.0-cp39-abi3-win_arm64.whl", hash = "sha256:92a31c9c20d25a076edaeec76b128a3535d74a24f340b9a8a7e96c9b86dc9642", size = 1081181, upload-time = "2026-05-17T17:48:28.122Z" }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "librt" -version = "0.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/40/08/9e7f6b5d2b5bed6ad055cdd5925f192bb403a51280f86b56554d9d0699a2/librt-0.11.0.tar.gz", hash = "sha256:075dc3ef4458a278e0195cbf6ac9d38808d9b906c5a6c7f7f79c3888276a3fb1", size = 200139, upload-time = "2026-05-10T18:17:25.138Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/87/2bf31fe17587b29e3f93ec31421e2b1e1c3e349b8bf6c7c313dbad1d5340/librt-0.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:93d95bd45b7d58343d8b90d904450a545144eec19a002511163426f8ab1fae29", size = 141092, upload-time = "2026-05-10T18:15:34.795Z" }, - { url = "https://files.pythonhosted.org/packages/cf/08/5c5bf772920b7ebac6e32bc91a643e0ab3870199c0b542356d3baa83970a/librt-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ee278c769a713638cdacd4c0436d72156e75df3ebc0166ab2b9dc43acc386c9", size = 142035, upload-time = "2026-05-10T18:15:36.242Z" }, - { url = "https://files.pythonhosted.org/packages/06/20/662a03d254e5b000d838e8b345d83303ddb768c080fd488e40634c0fa66b/librt-0.11.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f230cb1cbc9faaa616f9a678f530ebcf186e414b6bcbd88b960e4ba1b92428d5", size = 475022, upload-time = "2026-05-10T18:15:37.56Z" }, - { url = "https://files.pythonhosted.org/packages/de/f3/aa81523e45184c6ec23dc7f63263362ec55f80a09d424c012359ecbe7e35/librt-0.11.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5d63c855d86938d9de93e265c9bd8c705b51ec494de5738340ee93767a686e4b", size = 467273, upload-time = "2026-05-10T18:15:39.182Z" }, - { url = "https://files.pythonhosted.org/packages/6b/6f/59c74b560ca8853834d5501d589c8a2519f4184f273a085ffd0f37a1cc47/librt-0.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:993f028be9e96a08d31df3479ac80d99be374d17f3b78e4796b3fd3c913d4e89", size = 497083, upload-time = "2026-05-10T18:15:40.634Z" }, - { url = "https://files.pythonhosted.org/packages/fe/7b/5aa4d2c9600a719401160bf7055417df0b2a47439b9d88286ce45e56b65f/librt-0.11.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:258d73a0aa66a055e65b2e4d1b8cdb23b9d132c5bb915d9547d804fcaed116cc", size = 489139, upload-time = "2026-05-10T18:15:41.934Z" }, - { url = "https://files.pythonhosted.org/packages/d6/31/9143803d7da6856a69153785768c4936864430eec0fd9461c3ea527d9922/librt-0.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0827efe7854718f04aaddf6496e96960a956e676fe1d0f04eb41511fd8ad06d5", size = 508442, upload-time = "2026-05-10T18:15:43.206Z" }, - { url = "https://files.pythonhosted.org/packages/2f/5a/bce08184488426bda4ccc2c4964ac048c8f68ae89bd7120082eef4233cfd/librt-0.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7753e57d6e12d019c0d8786f1c09c709f4c3fcc57c3887b24e36e6c06ec938b7", size = 514230, upload-time = "2026-05-10T18:15:44.761Z" }, - { url = "https://files.pythonhosted.org/packages/89/8c/bb5e213d254b7505a0e658da199d8ab719086632ce09eef311ab27976523/librt-0.11.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:11bd19822431cc21af9f27374e7ae2e58103c7d98bda823536a6c47f6bb2bb3d", size = 494231, upload-time = "2026-05-10T18:15:46.308Z" }, - { url = "https://files.pythonhosted.org/packages/9d/fb/541cdad5b1ab1300398c74c4c9a497b88e5074c21b1244c8f49731d3a284/librt-0.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:22bdf239b219d3993761a148ffa134b19e52e9989c84f845d5d7b71d70a17412", size = 537585, upload-time = "2026-05-10T18:15:47.629Z" }, - { url = "https://files.pythonhosted.org/packages/8f/f2/464bb69295c320cb06bddb4f14a4ec67934ee14b2bffb12b19fb7ab287ba/librt-0.11.0-cp311-cp311-win32.whl", hash = "sha256:46c60b61e308eb535fbd6fa622b1ee1bb2815691c1ad9c98bf7b84952ec3bc8d", size = 100509, upload-time = "2026-05-10T18:15:49.157Z" }, - { url = "https://files.pythonhosted.org/packages/6d/e7/a17ee1788f9e4fbf548c19f4afa07c92089b9e24fef6cb2410863781ef4c/librt-0.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:902e546ff044f579ff1c953ff5fce97b636fe9e3943996b2177710c6ef076f73", size = 118628, upload-time = "2026-05-10T18:15:50.345Z" }, - { url = "https://files.pythonhosted.org/packages/cc/c7/6c766214f9f9903bcfcfbef97d807af8d8f5aa3502d247858ab17582d212/librt-0.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:65ac3bc20f78aa0ee5ae84baa68917f89fef4af63e941084dd019a0d0e749f0c", size = 103122, upload-time = "2026-05-10T18:15:52.068Z" }, - { url = "https://files.pythonhosted.org/packages/8b/d0/07c77e067f0838949b43bd89232c29d72efebb9d2801a9750184eb706b71/librt-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b87504f1690a23b9a2cca841191a04f83895d4fc2dd04df91d82b1a04ca2ad46", size = 144147, upload-time = "2026-05-10T18:15:53.227Z" }, - { url = "https://files.pythonhosted.org/packages/7a/24/8493538fa4f62f982686398a5b8f68008138a75086abdea19ade64bf4255/librt-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40071fc5fe0ce8daa6de616702314a01e1250711682b0523d6ab8d4525910cb3", size = 143614, upload-time = "2026-05-10T18:15:54.657Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1e/f8bad050810d9171f34a1648ed910e56814c2ba61639f2bd53c6377ae24b/librt-0.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:137e79445c896a0ea7b265f52d23954e05b64222ee1af69e2cb34219067cbb67", size = 485538, upload-time = "2026-05-10T18:15:56.117Z" }, - { url = "https://files.pythonhosted.org/packages/c0/fe/3594ebfbaf03084ba4b120c9ba5c3183fd938a48725e9bbe6ff0a5159ad8/librt-0.11.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:cca6644054e78746d8d4ef238681f9c34ff8b584fe6b988ecebb8db3b15e622a", size = 479623, upload-time = "2026-05-10T18:15:57.544Z" }, - { url = "https://files.pythonhosted.org/packages/b0/da/5d1876984b3746c85dbd219dbfcb73c85f54ee263fd32e5b2a632ec14571/librt-0.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5b0eea49f5562861ee8d757a32ef7d559c1d35be2aaaa1ec28941d74c9ffc8a", size = 513082, upload-time = "2026-05-10T18:15:58.805Z" }, - { url = "https://files.pythonhosted.org/packages/19/6e/55bdf5d5ca00c3e18430690bf2c953d8d3ffd3c337418173d33dec985dc9/librt-0.11.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0d1029d7e1ae1a7e647ed6fb5df8c4ce2dffefb7a9f5fd1376a4554d96dac09f", size = 508105, upload-time = "2026-05-10T18:16:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/07/10/f1f23a7c595ee90ece4d35c851e5d104b1311a887ed1b4ac4c35bbd13da8/librt-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bc3ce6b33c5828d9e80592011a5c584cb2ce86edbc4088405f70da47dc1d1b3b", size = 522268, upload-time = "2026-05-10T18:16:01.708Z" }, - { url = "https://files.pythonhosted.org/packages/b6/02/5720f5697a7f54b78b3aefbe20df3a48cedcff1276618c4aa481177942ed/librt-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:936c5995f3514a42111f20099397d8177c79b4d7e70961e396c6f5a0a3566766", size = 527348, upload-time = "2026-05-10T18:16:03.496Z" }, - { url = "https://files.pythonhosted.org/packages/50/db/b4a47c6f91db4ff76348a0b3dd0cc65e090a078b765a810a62ff9434c3d3/librt-0.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9bc0ca6ad9381cbe8e4aa6e5726e4c80c78115a6e9723c599ed1d73e092bc49d", size = 516294, upload-time = "2026-05-10T18:16:05.173Z" }, - { url = "https://files.pythonhosted.org/packages/9e/58/9384b2f4eb1ed1d273d40948a7c5c4b2360213b402ef3be4641c06299f9c/librt-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:070aa8c26c0a74774317a72df8851facc7f0f012a5b406557ac56992d92e1ec8", size = 553608, upload-time = "2026-05-10T18:16:06.839Z" }, - { url = "https://files.pythonhosted.org/packages/21/7b/5aa8848a7c6a9278c79375146da1812e695754ceec5f005e6043461a7315/librt-0.11.0-cp312-cp312-win32.whl", hash = "sha256:6bf14feb84b05ae945277395451998c89c54d0def4070eb5c08de544930b245a", size = 101879, upload-time = "2026-05-10T18:16:08.103Z" }, - { url = "https://files.pythonhosted.org/packages/37/33/8a745436944947575b584231750a41417de1a38cf6a2e9251d1065651c09/librt-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:75672f0bc524ede266287d532d7923dbce94c7514ad07627bac3d0c6d92cc4d9", size = 119831, upload-time = "2026-05-10T18:16:09.174Z" }, - { url = "https://files.pythonhosted.org/packages/59/67/a6739ac96e28b7855808bdb0370e250606104a859750d209e5a0716fe7ab/librt-0.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f10cf143e4a9bb0f4f5af568a00df94a2d69ef41c2579584454bb0fe5cc642c", size = 103470, upload-time = "2026-05-10T18:16:10.369Z" }, - { url = "https://files.pythonhosted.org/packages/82/61/e59168d4d0bf2bf90f4f0caf7a001bfc60254c3af4586013b04dc3ef517b/librt-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:78dc31f7fdfe9c9d0eb0e8f42d139db230e826415bbcabd9f0e9faaaee909894", size = 144119, upload-time = "2026-05-10T18:16:11.771Z" }, - { url = "https://files.pythonhosted.org/packages/61/fd/caa1d60b12f7dd79ccea23054e06eeaebe266a5f52c40a6b651069200ce5/librt-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fa475675db22290c3158e1d42326d0f5a65f04f44a0e68c3630a25b53560fb9c", size = 143565, upload-time = "2026-05-10T18:16:13.334Z" }, - { url = "https://files.pythonhosted.org/packages/b8/a9/dc744f5c2b4978d48db970be29f22716d3413d28b14ad99740817315cf2c/librt-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:621db29691044bdeda22e789e482e1b0f3a985d90e3426c9c6d17606416205ea", size = 485395, upload-time = "2026-05-10T18:16:14.729Z" }, - { url = "https://files.pythonhosted.org/packages/8f/21/7f8e97a1e4dae952a5a95948f6f8507a173bc1e669f54340bba6ca1ca31b/librt-0.11.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a9010e2ed5b3a9e158c5fd966b3ab7e834bb3d3aacc8f66c91dd4b57a3799230", size = 479383, upload-time = "2026-05-10T18:16:16.321Z" }, - { url = "https://files.pythonhosted.org/packages/a6/6d/d8ee9c114bebf2c50e29ec2aa940826fccb62a645c3e4c18760987d0e16d/librt-0.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c39513d8b7477a2e1ed8c43fc21c524e8d5a0f8d4e8b7b074dbdbe7820a08e2", size = 513010, upload-time = "2026-05-10T18:16:17.647Z" }, - { url = "https://files.pythonhosted.org/packages/f0/43/0b5708af2bd30a46400e72ba6bdaa8f066f15fb9a688527e34220e8d6c06/librt-0.11.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7aef3cf1d5af86e770ab04bfd993dfc4ae8b8c17f66fb77dd4a7d50de7bbb1a3", size = 508433, upload-time = "2026-05-10T18:16:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/4a/50/356187247d09013490481033183b3532b58acf8028bcb34b2b56a375c9b2/librt-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:557183ddc36babe46b27dd60facbd5adb4492181a5be887587d57cda6e092f21", size = 522595, upload-time = "2026-05-10T18:16:20.642Z" }, - { url = "https://files.pythonhosted.org/packages/40/e7/c6ac4240899c7f3248079d5a9900debe0dadb3fdeaf856684c987105ba47/librt-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83d3e1f72bd42f6c5c0b7daec530c3f829bd02db42c70b8ddf0c2d90a2459930", size = 527255, upload-time = "2026-05-10T18:16:22.352Z" }, - { url = "https://files.pythonhosted.org/packages/eb/b5/a81322dbeedeeaf9c1ee6f001734d28a09d8383ac9e6779bc24bbd0743c6/librt-0.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4ce1f21fbe589bc1afd7872dece84fb0e1144f794a288e58a10d2c54a55c43be", size = 516847, upload-time = "2026-05-10T18:16:23.627Z" }, - { url = "https://files.pythonhosted.org/packages/ae/66/6e6323787d592b55204a42595ff1102da5115601b53a7e9ddebc889a6da5/librt-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b09f7044ea2b64c9da42fd3d335666518cfd1c6e8a182c95da73d0214b41e", size = 553920, upload-time = "2026-05-10T18:16:25.025Z" }, - { url = "https://files.pythonhosted.org/packages/9c/21/623f8ca230857102066d9ca8c6c1734995908c4d0d1bee7bb2ef0021cb33/librt-0.11.0-cp313-cp313-win32.whl", hash = "sha256:78fddc31cd4d3caa897ad5d31f856b1faadc9474021ad6cb182b9018793e254e", size = 101898, upload-time = "2026-05-10T18:16:26.649Z" }, - { url = "https://files.pythonhosted.org/packages/b3/1d/b4ebd44dd723f768469007515cb92251e0ae286c94c140f374801140fa74/librt-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ca8aa88751a775870b764e93bad5135385f563cb8dcee399abf034ea4d3cb47", size = 119812, upload-time = "2026-05-10T18:16:27.859Z" }, - { url = "https://files.pythonhosted.org/packages/3b/e4/b2f4ca7965ca373b491cdb4bc25cdb30c1649ca81a8782056a83850292a9/librt-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:96f044bb325fd9cf1a723015638c219e9143f0dfbc0ca54c565df2b7fc748b44", size = 103448, upload-time = "2026-05-10T18:16:29.066Z" }, - { url = "https://files.pythonhosted.org/packages/29/eb/dbce197da4e227779e56b5735f2decc3eb36e55a1cdbf1bd65d6639d76c1/librt-0.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4a017a95e5837dc15a8c5661d60e05daa96b90908b1aa6b7acdf443cd25c8ebd", size = 143345, upload-time = "2026-05-10T18:16:30.674Z" }, - { url = "https://files.pythonhosted.org/packages/76/a3/254bebd0c11c8ba684018efb8006ff22e466abce445215cca6c778e7d9de/librt-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b1ecbd9819deccc39b7542bf4d2a740d8a620694d39989e58661d3763458f8d4", size = 143131, upload-time = "2026-05-10T18:16:32.037Z" }, - { url = "https://files.pythonhosted.org/packages/f1/3f/f77d6122d21ac7bf6ae8a7dfced1bd2a7ac545d3273ebdcaf8042f6d619f/librt-0.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7da327dacd7be8f8ec36547373550744a3cc0e536d54665cd83f8bcd961200e8", size = 477024, upload-time = "2026-05-10T18:16:33.493Z" }, - { url = "https://files.pythonhosted.org/packages/ac/0a/2c996dadebaa7d9bbbd43ef2d4f3e66b6da545f838a41694ef6172cebec8/librt-0.11.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0dc56b1f8d06e60db362cc3fdae206681817f86ce4725d34511473487f12a34b", size = 474221, upload-time = "2026-05-10T18:16:34.864Z" }, - { url = "https://files.pythonhosted.org/packages/0a/7e/f5d92af8486b8272c23b3e686b46ff72d89c8169585eb61eef01a2ac7147/librt-0.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05fb8fb2ab90e21c8d12ea240d744ad514da9baf381ebfa70d91d20d21713175", size = 505174, upload-time = "2026-05-10T18:16:36.705Z" }, - { url = "https://files.pythonhosted.org/packages/af/1a/cb0734fe86398eb33193ab753b7326255c74cac5eb09e76b9b16536e7adb/librt-0.11.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cae74872be221df4374d10fec61f93ed1513b9546ea84f2c0bf73ab3e9bd0b03", size = 497216, upload-time = "2026-05-10T18:16:38.418Z" }, - { url = "https://files.pythonhosted.org/packages/18/06/094820f91558b66e29943c0ec41c9914f460f48dd51fc503c3101e10842d/librt-0.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32bcc918c0148eb7e3d57385125bac7e5f9e4359d05f07448b09f6f778c2f31c", size = 513921, upload-time = "2026-05-10T18:16:39.848Z" }, - { url = "https://files.pythonhosted.org/packages/0b/c2/00de9018871a282f530cacb457d5ec0428f6ac7e6fedde9aff7468d9fb04/librt-0.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f9743fc99135d5f78d2454435615f6dec0473ca507c26ce9d92b10b562a280d3", size = 520850, upload-time = "2026-05-10T18:16:41.471Z" }, - { url = "https://files.pythonhosted.org/packages/51/9d/64631832348fd1834fb3a61b996434edddaaf25a31d03b0a76273159d2cf/librt-0.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5ba067f4aadae8fda802d91d2124c90c42195ff32d9161d3549e6d05cfe26f96", size = 504237, upload-time = "2026-05-10T18:16:43.15Z" }, - { url = "https://files.pythonhosted.org/packages/a5/ec/ae5525eb16edc827a044e7bb8777a455ff95d4bca9379e7e6bddd7383647/librt-0.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:de3bf945454d032f9e390b85c4072e0a0570bf825421c8be0e71209fa65e1abe", size = 546261, upload-time = "2026-05-10T18:16:44.408Z" }, - { url = "https://files.pythonhosted.org/packages/5a/09/adce371f27ca039411da9659f7430fcc2ba6cd0c7b3e4467a0f091be7fa9/librt-0.11.0-cp314-cp314-win32.whl", hash = "sha256:d2277a05f6dcb9fd13db9566aac4fabd68c3ea1ea46ee5567d4eef8efa495a2f", size = 96965, upload-time = "2026-05-10T18:16:46.039Z" }, - { url = "https://files.pythonhosted.org/packages/d6/ee/8ac720d98548f173c7ce2e632a7ca94673f74cacd5c8162a84af5b35958a/librt-0.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:ab73e8db5e3f564d812c1f5c3a175930a5f9bc96ccb5e3b22a34d7858b401cf7", size = 115151, upload-time = "2026-05-10T18:16:47.133Z" }, - { url = "https://files.pythonhosted.org/packages/94/20/c900cf14efeb09b6bef2b2dff20779f73464b97fd58d1c6bccc379588ae3/librt-0.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:aea3caa317752e3a466fa8af45d91ee0ea8c7fdd96e42b0a8dd9b76a7931eba1", size = 98850, upload-time = "2026-05-10T18:16:48.597Z" }, - { url = "https://files.pythonhosted.org/packages/0c/71/944bfe4b64e12abffcd3c15e1cce07f72f3d55655083786285f4dedeb532/librt-0.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d1b36540d7aaf9b9101b3a6f376c8d8e9f7a9aec93ed05918f2c69d493ffef72", size = 151138, upload-time = "2026-05-10T18:16:49.839Z" }, - { url = "https://files.pythonhosted.org/packages/b6/10/99e64a5c86989357fda078c8143c533389585f6473b7439172dd8f3b3b2d/librt-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:efbb343ab2ce3540f4ecbe6315d677ed70f37cd9a72b1e58066c918ca83acbaa", size = 151976, upload-time = "2026-05-10T18:16:51.062Z" }, - { url = "https://files.pythonhosted.org/packages/21/31/5072ad880946d83e5ea4147d6d018c78eefce85b77819b19bdd0ee229435/librt-0.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0dd688aab3f7914d3e6e5e3554978e0383312fb8e771d84be008a35b9ee548", size = 557927, upload-time = "2026-05-10T18:16:52.632Z" }, - { url = "https://files.pythonhosted.org/packages/5e/8d/70b5fb7cfbab60edbe7381614ab985da58e144fbf465c86d44c95f43cdca/librt-0.11.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f5fb36b8c6c63fdcbb1d526d94c0d1331610d43f4118cc1beb4efef4f3faacb2", size = 539698, upload-time = "2026-05-10T18:16:53.934Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a3/ba3495a0b3edbd24a4cae0d1d3c64f39a9fc45d06e812101289b50c1a619/librt-0.11.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a9a237d13addb93715b6fee74023d5ee3469b53fce527626c0e088aa585805f", size = 577162, upload-time = "2026-05-10T18:16:55.589Z" }, - { url = "https://files.pythonhosted.org/packages/f7/db/36e25fb81f99937ff1b96612a1dc9fd66f039cb9cc3aee12c01fac31aab9/librt-0.11.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5ddd17bd87b2c56ddd60e546a7984a2e64c4e8eab92fb4cf3830a48ad5469d51", size = 566494, upload-time = "2026-05-10T18:16:56.975Z" }, - { url = "https://files.pythonhosted.org/packages/33/0d/3f622b47f0b013eeb9cf4cc07ae9bfe378d832a4eec998b2b209fe84244d/librt-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd43992b4473d42f12ff9e68326079f0696d9d4e6000e8f39a0238d482ba6ee2", size = 596858, upload-time = "2026-05-10T18:16:58.374Z" }, - { url = "https://files.pythonhosted.org/packages/a9/02/71b90bc93039c46a2000651f6ad60122b114c8f54c4ad306e0e96f5b75ad/librt-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f8e3e8056dd674e279741485e2e512d6e9a751c7455809d0114e6ebf8d781085", size = 590318, upload-time = "2026-05-10T18:16:59.676Z" }, - { url = "https://files.pythonhosted.org/packages/04/04/418cb3f75621e2b761fb1ab0f017f4d70a1a72a6e7c74ee4f7e8d198c2f3/librt-0.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c1f708d8ae9c56cf38a903c44297243d2ec83fd82b396b977e0144a3e76217e3", size = 575115, upload-time = "2026-05-10T18:17:01.007Z" }, - { url = "https://files.pythonhosted.org/packages/cc/2c/5a2183ac58dd911f26b5d7e7d7d8f1d87fcecdddd99d6c12169a258ff62c/librt-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0add982e0e7b9fc14cf4b33789d5f13f66581889b88c2f58099f6ce8f92617bd", size = 617918, upload-time = "2026-05-10T18:17:02.682Z" }, - { url = "https://files.pythonhosted.org/packages/15/1f/dc6771a52592a4451be6effa200cbfc9cec61e4393d3033d81a9d307961d/librt-0.11.0-cp314-cp314t-win32.whl", hash = "sha256:2b481d846ac894c4e8403c5fd0e87c5d11d6499e404b474602508a224ff531c8", size = 103562, upload-time = "2026-05-10T18:17:03.99Z" }, - { url = "https://files.pythonhosted.org/packages/62/4a/7d1415567027286a75ba1093ec4aca11f073e0f559c530cf3e0a757ad55c/librt-0.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:28edb433edde181112a908c78907af28f964eabc15f4dd16c9d66c834302677c", size = 124327, upload-time = "2026-05-10T18:17:05.465Z" }, - { url = "https://files.pythonhosted.org/packages/ce/62/b40b382fa0c66fee1478073eb8db352a4a6beda4a1adccf1df911d8c289c/librt-0.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dee008f20b542e3cd162ba338a7f9ec0f6d23d395f66fe8aeeec3c9d067ea253", size = 102572, upload-time = "2026-05-10T18:17:06.809Z" }, -] - -[[package]] -name = "mypy" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ast-serialize" }, - { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, - { name = "mypy-extensions" }, - { name = "pathspec" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/82/15/cca9d88503549ed6fedeaa1d448cdddd542ee8a490232d732e278036fbf2/mypy-2.1.0.tar.gz", hash = "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633", size = 3898359, upload-time = "2026-05-11T18:37:36.237Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/a1/639f3024794a2a15899cb90707fe02e044c4412794c39c5769fd3df2e2ef/mypy-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a683016b16fe2f572dc04c72be7ee0504ac1605a265d0200f5cea695fb788f41", size = 14691685, upload-time = "2026-05-11T18:33:27.973Z" }, - { url = "https://files.pythonhosted.org/packages/3b/08/9a585dea4325f20d8b80dc78623fa50d1fd2173b710f6237afd6ba6ab39b/mypy-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a293c534adb55271fef24a26da04b855540a8c13cc07bc5917b9fd2c394f2ca", size = 13555165, upload-time = "2026-05-11T18:32:16.107Z" }, - { url = "https://files.pythonhosted.org/packages/81/dc/7c42cc9c6cb01e8eb09961f1f738741d3e9c7e9d5c5b30ec69222625cd5f/mypy-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7406f4d048e71e576f5356d317e5b0a9e666dfd966bd99f9d14ca06e1a341538", size = 13994376, upload-time = "2026-05-11T18:32:39.256Z" }, - { url = "https://files.pythonhosted.org/packages/d4/fa/285946c33bce716e082c11dfeee9ee196eaf1f5042efb3581a31f9f205e4/mypy-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e0210d626fc8b31ccc90233754c7bc90e1f43205e85d96387f7db1285b55c398", size = 14864618, upload-time = "2026-05-11T18:34:49.765Z" }, - { url = "https://files.pythonhosted.org/packages/2b/83/82397f48af6c27e295d57979ded8490c9829040152cf7571b2f026aeb9a0/mypy-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3712c20deed54e814eaaa825603bada8ea1c390670a397c95b98405347acc563", size = 15102063, upload-time = "2026-05-11T18:34:05.855Z" }, - { url = "https://files.pythonhosted.org/packages/40/68/b02dec39057b88eb03dc0aa854732e26e8361f34f9d0e20c7614967d1eba/mypy-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fcaa0e479066e31f7cceb6a3bea39cb22b2ff51a6b2f24f193d19179ba17c389", size = 11060564, upload-time = "2026-05-11T18:35:36.494Z" }, - { url = "https://files.pythonhosted.org/packages/cf/a8/ea3dcbef31f99b634f2ee23bb0321cbc8c1b388b76a861eb849f13c347dc/mypy-2.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:0b1a5260c95aa443083f9ed3592662941951bca3d4ca224a5dc517c38b7cf666", size = 9966983, upload-time = "2026-05-11T18:37:14.139Z" }, - { url = "https://files.pythonhosted.org/packages/95/b1/55861beb5c339b44f9a2ba92df9e2cb1eeb4ae1eee674cdf7772c797778b/mypy-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:244358bf1c0da7722230bce60683d52e8e9fd030554926f15b747a84efb5b3af", size = 14874381, upload-time = "2026-05-11T18:37:31.784Z" }, - { url = "https://files.pythonhosted.org/packages/0b/b3/b7f770114b7d0ac92d0f76e8d93c2780844a70488a90e91821927850da86/mypy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ec7c57657493c7a75534df2751c8ae2cda383c16ecc55d2106c54476b1b16f6", size = 13665501, upload-time = "2026-05-11T18:34:23.063Z" }, - { url = "https://files.pythonhosted.org/packages/b6/f3/8ae2037967e2126689a0c11d99e2b707134a565191e92c60ca2572aec60a/mypy-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8161b6ff4392410023224f0969d17db93e1e154bc3e4ba62598e720723ae211", size = 14045750, upload-time = "2026-05-11T18:31:48.151Z" }, - { url = "https://files.pythonhosted.org/packages/a0/32/615eb5911859e43d054941b0d0a7d06cfa2870eba86529cf385b052b111c/mypy-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf03e12003084a67395184d3eb8cbd6a489dc3655b5664b28c210a9e2403ab0b", size = 15061630, upload-time = "2026-05-11T18:37:06.898Z" }, - { url = "https://files.pythonhosted.org/packages/d4/03/4eafbfff8bfab1b87082741eae6e6a624028c984e6708b73bce2a8570c9d/mypy-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:20509760fd791c51579d573153407d226385ec1f8bcce55d730b354f3336bc22", size = 15288831, upload-time = "2026-05-11T18:31:18.07Z" }, - { url = "https://files.pythonhosted.org/packages/99/ee/919661478e5891a3c96e549c036e467e64563ab85995b10c53c8358e16a3/mypy-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:6753d0c1fdd6b1a23b9e4f283ce80b2153b724adcb2653b20b85a8a28ac6436b", size = 11135228, upload-time = "2026-05-11T18:34:31.23Z" }, - { url = "https://files.pythonhosted.org/packages/24/0a/6a12b9782ca0831a553192f351679f4548abc9d19a7cc93bb7feb02084c7/mypy-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:98ebb6589bb3b6d0c6f0c459d53ca55b8091fbc13d277c4041c885392e8195e8", size = 10040684, upload-time = "2026-05-11T18:36:48.199Z" }, - { url = "https://files.pythonhosted.org/packages/6e/dd/c7191469c777f07689c032a8f7326e393ea34c92d6d76eb7ce5ba57ea66d/mypy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35aac3bb114e03888f535d5eb51b8bafbb3266586b599da1940f9b1be3ec5bd5", size = 14852174, upload-time = "2026-05-11T18:31:38.929Z" }, - { url = "https://files.pythonhosted.org/packages/55/8c/aed55408879043d72bb9135f4d0d19a02b886dd569631e113e3d2706cb8d/mypy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8de55a8c861f2a49331f807be98d90caeceeef520bde13d43a160207f8af613e", size = 13651542, upload-time = "2026-05-11T18:36:04.636Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8e/f371a824b1f1fa8ea6e3dbb8703d232977d572be2329554a3bc4d960302f/mypy-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fdf2941a07434af755837d9880f7d7d25f1dacb1af9dcd4b9b66f2220a3024e", size = 14033929, upload-time = "2026-05-11T18:35:55.742Z" }, - { url = "https://files.pythonhosted.org/packages/94/21/f54be870d6dd53a82c674407e0f8eed7174b05ec78d42e5abd7b42e84fd5/mypy-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e195b817c13f02352a9c124301f9f30f078405444679b6753c1b96b6eed37285", size = 15039200, upload-time = "2026-05-11T18:33:10.281Z" }, - { url = "https://files.pythonhosted.org/packages/17/99/bf21748626a40ce59fd29a39386ab46afec88b7bd2f0fa6c3a97c995523f/mypy-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5431d42af987ebd92ba2f71d45c85ed41d8e6ca9f5fd209a69f68f707d2469e5", size = 15272690, upload-time = "2026-05-11T18:32:07.205Z" }, - { url = "https://files.pythonhosted.org/packages/d6/d7/9e90d2cf47100bea550ed2bc7b0d4de3a62181d84d5e37da0003e8462637/mypy-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:767fe8c66dc3e01e19e1737d4c38ebefead16125e1b8e58ad421903b376f5c65", size = 11147435, upload-time = "2026-05-11T18:33:56.477Z" }, - { url = "https://files.pythonhosted.org/packages/ec/46/e5c449e858798e35ffc90946282a27c62a77be743fe17480e4977374eb91/mypy-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:ecfe70d43775ab99562ab128ce49854a362044c9f894961f68f898c23cb7429d", size = 10035052, upload-time = "2026-05-11T18:32:30.049Z" }, - { url = "https://files.pythonhosted.org/packages/b0/ca/b279a672e874aedd5498ae25f722dacc8aa86bbffb939b3f97cbb1cf6686/mypy-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7354c5a7f69d9345c3d6e69921d57088eea3ddeeb6b20d34c1b3855b02c36ec2", size = 14848422, upload-time = "2026-05-11T18:35:45.984Z" }, - { url = "https://files.pythonhosted.org/packages/27/e6/3efe56c631d959b9b4454e208b0ac4b7f4f58b404c89f8bec7b49efdfc21/mypy-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:49890d4f76ac9e06ec117f9e09f3174da70a620a0c300953d8595c926e80947f", size = 13677374, upload-time = "2026-05-11T18:36:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/84/7f/8107ea87a44fd1f1b59882442f033c9c3488c127201b1d1d15f1cbd6022e/mypy-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:761be68e023ef5d94678772396a8af1220030f80837a3afd8d0aef3b419666f4", size = 14055743, upload-time = "2026-05-11T18:35:18.361Z" }, - { url = "https://files.pythonhosted.org/packages/51/4d/b6d34db183133b83761b9199a82d31557cdbb70a380d8c3b3438e11882a3/mypy-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c90345fc182dc363b891350457ec69c35140858538f38b4540845afcc32b1aef", size = 15020937, upload-time = "2026-05-11T18:34:59.618Z" }, - { url = "https://files.pythonhosted.org/packages/ff/d7/f08360c691d758acb02f45022c34d98b92892f4ea756644e1000d4b9f3d8/mypy-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b84802e7b5a6daf1f5e15bc9fcd7ddae77be13981ffab037f1c67bb84d67d135", size = 15253371, upload-time = "2026-05-11T18:36:41.081Z" }, - { url = "https://files.pythonhosted.org/packages/67/1b/09460a13719530a19bce27bd3bc8449e83569dd2ba7faf51c9c3c30c0b61/mypy-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:022c771234936ceac541ebaf836fe9e2abeb3f5e09aff21588fe543ff006fe21", size = 11326429, upload-time = "2026-05-11T18:34:13.526Z" }, - { url = "https://files.pythonhosted.org/packages/40/62/75dbf0f82f7b6680340efc614af29dd0b3c17b8a4f1cd09b8bd2fd6bc814/mypy-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:498207db725cec88829a6a5c2fc771205fd043719ef98bc49aba8fb9fc4e6d57", size = 10218799, upload-time = "2026-05-11T18:32:23.491Z" }, - { url = "https://files.pythonhosted.org/packages/b2/66/caca04ed7d972fb6eb6dd1ccd6df1de5c38fae8c5b3dc1c4e8e0d85ee6b9/mypy-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d5e5cad0efeba72b93cd17490cc0d69c5ac9ca132994fe3fb0314808aeeb83e", size = 15923458, upload-time = "2026-05-11T18:35:28.64Z" }, - { url = "https://files.pythonhosted.org/packages/ed/52/2d90cbe49d014b13ed7ff337930c30bad35893fe38a1e4641e756bb62191/mypy-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ff715050c127d724fd260a2e666e7747fdd83511c0c47d449d98238970aef780", size = 14757697, upload-time = "2026-05-11T18:36:14.208Z" }, - { url = "https://files.pythonhosted.org/packages/ac/37/d98f4a14e081b238992d0ed96b6d39c7cc0148c9699eb71eaa68629665ea/mypy-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82208da9e09414d520e912d3e462d454854bed0810b71540bb016dcbca7308fd", size = 15405638, upload-time = "2026-05-11T18:33:48.249Z" }, - { url = "https://files.pythonhosted.org/packages/a3/c2/15c46613b24a84fad2aea1248bf9619b99c2767ae9071fe224c179a0b7d4/mypy-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e79ebc1b904b84f0310dff7469655a9c36c7a68bddb37bdd42b67a332df61d08", size = 16215852, upload-time = "2026-05-11T18:32:50.296Z" }, - { url = "https://files.pythonhosted.org/packages/5c/90/9c16a57f482c76d25f6379762b56bbf65c711d8158cf271fb2802cfb0640/mypy-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e583edc957cfb0deb142079162ae826f58449b116c1d442f2d91c69d9fced081", size = 16452695, upload-time = "2026-05-11T18:33:38.182Z" }, - { url = "https://files.pythonhosted.org/packages/0f/4c/215a4eeb63cacc5f17f516691ea7285d11e249802b942476bff15922a314/mypy-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b33b6cd332695bba180d55e717a79d3038e479a2c49cc5eb3d53603409b9a5d7", size = 12866622, upload-time = "2026-05-11T18:34:39.945Z" }, - { url = "https://files.pythonhosted.org/packages/4b/50/1043e1db5f455ffe4c9ab22747cd8ca2bc492b1e4f4e21b130a44ee2b217/mypy-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:4f910fe825376a7b66ef7ca8c98e5a149e8cd64c19ae71d84047a74ee060d4e6", size = 10610798, upload-time = "2026-05-11T18:36:31.444Z" }, - { url = "https://files.pythonhosted.org/packages/0d/2a/13ca1f292f6db1b98ff495ef3467736b331621c5917cad984b7043e7348d/mypy-2.1.0-py3-none-any.whl", hash = "sha256:a663814603a5c563fb87a4f96fb473eeb30d1f5a4885afcf44f9db000a366289", size = 2693302, upload-time = "2026-05-11T18:31:29.246Z" }, -] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, -] - -[[package]] -name = "packaging" -version = "26.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, -] - -[[package]] -name = "pathspec" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "pygments" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, -] - -[[package]] -name = "pytest" -version = "9.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/84/0e/b5858858d74958632c49b72cb25a3976ff9f632397626715be71c89d3971/pytest-9.1.0.tar.gz", hash = "sha256:41dd9148c08072446394cefd3d79701701335a9f4cae69ba92e39f6c7f5c061c", size = 1634181, upload-time = "2026-06-13T18:52:45.983Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/5a/ba30a81239b909821b3153e303e7def45178bf353da4f72380e6c5e8793b/pytest-9.1.0-py3-none-any.whl", hash = "sha256:8ebb0e7888bdf2bdfc602ec51f8f62d50200af37356c74e503c79a94f5c81f32", size = 386453, upload-time = "2026-06-13T18:52:44.045Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, - { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, - { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, - { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, - { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, - { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, - { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, - { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, - { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "ruff" -version = "0.15.17" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/a9/3abdf488f1bf3d24c699415e454ed554a6350d5d89ce183be1ee0a3361ac/ruff-0.15.17.tar.gz", hash = "sha256:2ec446937fd16c8c4de2674a209cc5af64d9c6f17d21fbf1151054fa0bcf5219", size = 4743346, upload-time = "2026-06-11T17:54:47.663Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/4d/e11259f5da07cb6afb2d074c31bf09da9671993f7329d4f15d2fdc458301/ruff-0.15.17-py3-none-linux_armv6l.whl", hash = "sha256:d9feddb927fc68bd295f5eebc587a7e42cfaf9b65f60ca4a2386febff575da8f", size = 10856677, upload-time = "2026-06-11T17:54:49.533Z" }, - { url = "https://files.pythonhosted.org/packages/29/3e/772d679e1a0dc058e58875bd2c0cb713a0530877b4a76fee3c7966df0d49/ruff-0.15.17-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:25805a226d741c47d274a35ad5c10a7dde175fcddfa511d7cf3da0a21eb3eab7", size = 11223443, upload-time = "2026-06-11T17:55:00.573Z" }, - { url = "https://files.pythonhosted.org/packages/68/58/bd41f7688b2fd5623012605130ed70e60aa7f2244baa3d5066bdd61530c8/ruff-0.15.17-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f6ad73b14c2d18a3bf8ad7cb6974294d7f613a7898604826058e6ac64918ef4d", size = 10566458, upload-time = "2026-06-11T17:55:07.52Z" }, - { url = "https://files.pythonhosted.org/packages/d8/5b/733371013fcf1ec339e477ece6ab42bfe10bdd9bba8ee88a9516aa56bfc0/ruff-0.15.17-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ba0c1e4f95bcb3869d0d30cbd5917071ef2e28665abfec970cdab0492c713ed", size = 10914483, upload-time = "2026-06-11T17:55:05.501Z" }, - { url = "https://files.pythonhosted.org/packages/bd/cc/6f24251cc0252f7239391ccb85833f320efad14ebe5b443943f37ced6332/ruff-0.15.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:81647960f10bff57d2e51cadd0c3950fe598400c852863a038720ef5b8cca91e", size = 10647497, upload-time = "2026-06-11T17:54:57.733Z" }, - { url = "https://files.pythonhosted.org/packages/68/dd/0d10c17ce1a1624d6fc3156309c3f834fdb5dfaad026ec90c85684f3990e/ruff-0.15.17-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e01a84ddbc8c16c23055ba3924476850f1bbc1917cebbb9376665a63e74260d", size = 11416967, upload-time = "2026-06-11T17:54:51.461Z" }, - { url = "https://files.pythonhosted.org/packages/2f/91/556bfb156f6144f355e831c23db00b2fc4120f86b3ce81cc5f7fd2df51f3/ruff-0.15.17-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fe9f653152f8f294f9f7e03bf3a453d8b4a27f7a59c78c8666167f2b17b96c", size = 12335770, upload-time = "2026-06-11T17:54:45.793Z" }, - { url = "https://files.pythonhosted.org/packages/88/82/8b5999aa13355e926f06d9f42a32dcca862f623bf0363785ff89d607dffd/ruff-0.15.17-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c0fe88a7676e7a05b73174d4d4a59cb2ac21ff8263583f87a81a6018475a978", size = 11575441, upload-time = "2026-06-11T17:54:32.661Z" }, - { url = "https://files.pythonhosted.org/packages/11/93/f10377bb04109ca0e8cbc483ff1982c54b6d418210041776f93e8cdc7fa9/ruff-0.15.17-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecfc3c7878fff94633ab0348524e093f9ce3243080416dd7d14f8ba400174719", size = 11557614, upload-time = "2026-06-11T17:54:34.698Z" }, - { url = "https://files.pythonhosted.org/packages/c7/a6/eeeae7f7d5493df41649ab3db92f086b2d0a30199e4efdf8e3dd7a033f24/ruff-0.15.17-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:b8461180b22420b1bdc289909410930761629fddf2a5aaf60fae1ab26cedc4c4", size = 11544450, upload-time = "2026-06-11T17:54:39.042Z" }, - { url = "https://files.pythonhosted.org/packages/32/88/5991ce565129a24dd4a00db1254b3b5db2e53018cbe4018ea5a89738e727/ruff-0.15.17-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6eccbe50a038b503e7140b441aa9c7fc8c1f36edf23ebef9f4165c2f28f568b7", size = 10892524, upload-time = "2026-06-11T17:55:09.432Z" }, - { url = "https://files.pythonhosted.org/packages/f5/1d/0fdd248313425f55223968af04b0a42125466a8d88d21c1d99c6af0a51e8/ruff-0.15.17-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:382fc0521025f5a8ad447d8bdd523545d0d7646adb718eb1c2dac5065ec27c0f", size = 10659573, upload-time = "2026-06-11T17:54:36.824Z" }, - { url = "https://files.pythonhosted.org/packages/9e/0e/072e8260deb9461062ce9311ced27a8e541229a6ffd483013dd37661e43e/ruff-0.15.17-py3-none-musllinux_1_2_i686.whl", hash = "sha256:456d41fcd1b2777ad63f09a6e7121d43f7b688bbc76a800c10f7f8fb1f912c3f", size = 11127818, upload-time = "2026-06-11T17:55:03.124Z" }, - { url = "https://files.pythonhosted.org/packages/ab/b4/55060a34163121498014696b5f656db5b8c6963768f227dbf0d76b311073/ruff-0.15.17-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b1a04bcc94ae6194e9db05d16ad31f298a7194bfbcb08258bbe589cee1d587b8", size = 11655901, upload-time = "2026-06-11T17:54:53.562Z" }, - { url = "https://files.pythonhosted.org/packages/49/71/9b29d6b87cef468d697f43c6a91e3fae4a80185779d7d5a4ef27d173439f/ruff-0.15.17-py3-none-win32.whl", hash = "sha256:596065960ab1ff593f744220c9fe6580eda00a95003cffa9f4048bb5b1bf0392", size = 10925574, upload-time = "2026-06-11T17:54:55.723Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b2/8fc77f3723228836fa5d12497eb71c808f83782e10d058d2b15cfa14640b/ruff-0.15.17-py3-none-win_amd64.whl", hash = "sha256:6769e5fa1710b179b92e0bfa5a51735b35baea9013dadb06d5f44cbcf9547084", size = 12058788, upload-time = "2026-06-11T17:54:41.042Z" }, - { url = "https://files.pythonhosted.org/packages/2d/c7/c53e8dbff9c9dc4b7928773421ae294a5d28fcb8dcda1a089579d3a7e510/ruff-0.15.17-py3-none-win_arm64.whl", hash = "sha256:f3be1fbb34bcdfd146240d8fb92a709d4c2c8191348580a3c044ec60fa0b4456", size = 11355275, upload-time = "2026-06-11T17:54:43.635Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] diff --git a/plugins/agentdeck/docs/thread-title-hooks.md b/plugins/agentdeck/docs/thread-title-hooks.md deleted file mode 100644 index 07ed4ee60..000000000 --- a/plugins/agentdeck/docs/thread-title-hooks.md +++ /dev/null @@ -1,204 +0,0 @@ -# Codex Thread Title Hook Notes - -AgentDeck provides `SessionStart` and `Stop` hooks for prefixing generated -thread titles with the project directory name, plus a `PostToolUse` diagnostic -hook for timing research. By default, a trusted and enabled hook renames project -threads; disable the hook in Codex GUI settings to turn the behavior off. - -## Name - -The plugin is named `agentdeck` because this work is about Codex runtime -behavior rather than a language, framework, app, or repository-maintenance -domain. It follows the Socket convention of hyphen-case plugin IDs with a -reader-facing display name in plugin metadata. - -## Current Hooks - -The current hooks listen for `SessionStart` with the `startup` matcher, `Stop`, -and `PostToolUse`. All three run: - -```sh -sh ${PLUGIN_ROOT}/hooks/run-thread-title-hook.sh -``` - -This command path depends on Codex's plugin-bundled hook command expansion. It -has been verified in the installed Socket plugin cache after restarting the -Codex GUI and trusting the plugin hooks. - -The script appends stdin to: - -```text -~/.codex/agentdeck/hooks/thread-title-payloads.jsonl -``` - -It also writes structured hook decisions to: - -```text -~/.codex/agentdeck/hooks/thread-title-decisions.jsonl -``` - -Per-thread rename state is written to: - -```text -~/.codex/agentdeck/hooks/thread-title-state.json -``` - -Post-tool-use summaries are written to: - -```text -~/.codex/agentdeck/hooks/tool-use-events.jsonl -``` - -Set `AGENTDECK_DATA_DIR` to redirect these paths during tests. - -## Thread Title Modes - -`AGENTDECK_THREAD_TITLE_MODE` controls the title behavior: - -- `rename` is the default. It uses the `Stop` hook to read the generated title - and then call App Server `thread/name/set` once per thread. -- `capture` records payloads and decisions without changing thread metadata. -- `dry-run` records the thread id candidate and proposed prefix without calling - `thread/name/set`. - -`SessionStart` never renames the thread because it runs before Codex creates the -generated title. By default, the first `Stop` for a thread also waits without -renaming because GUI tests showed Codex can generate or rewrite the title after -that hook fires. The second `Stop` reads the current generated title with App -Server `thread/read`, prefixes it, and records per-thread state so later turns -do not keep rewriting the title. - -`AGENTDECK_THREAD_TITLE_MIN_STOP_COUNT` controls the Stop count threshold. -The default is `2`. Set it to `1` only when deliberately testing first-turn -renames. - -The prefix is the last path component of `cwd`, truncated to -`AGENTDECK_THREAD_TITLE_MAX_PREFIX_LENGTH` characters. The default maximum -is `48`. The generated title is preserved after the prefix: - -```text -socket: Implement Stop hook title prefixing -``` - -Projectless Codex chat directories are treated specially. If `cwd` appears under -the default Codex chat root: - -```text -~/Documents/Codex/YYYY-MM-DD/ -``` - -the hook skips prefixing by default so projectless chats keep Codex's generated -title. Override the root with `AGENTDECK_PROJECTLESS_ROOT`. To opt into a -shared projectless prefix such as `Chat`, set -`AGENTDECK_PROJECTLESS_THREAD_PREFIX`. - -The thread id candidate is read from `thread_id`, `threadId`, `session_id`, then -`sessionId`. Current Codex hook docs describe `session_id`. - -A live projectless thread created after trusting the plugin hook produced this -`SessionStart` payload shape: - -```json -{ - "session_id": "019e9e4e-e0c5-7591-ac1d-51c09ef83faa", - "transcript_path": "~/.codex/sessions/2026/06/06/rollout-2026-06-06T15-00-30-019e9e4e-e0c5-7591-ac1d-51c09ef83faa.jsonl", - "cwd": "~/Documents/Codex/2026-06-06/agentdeck-projectless-hook-test", - "hook_event_name": "SessionStart", - "model": "gpt-5.5", - "permission_mode": "default", - "source": "startup" -} -``` - -The payload did not include an explicit saved-project or projectless marker, so -the projectless rule is path-based until Codex exposes richer thread metadata to -hooks. - -## Rename Transport - -The preferred next transport is Codex App Server `thread/name/set`. The -[App Server API overview](https://developers.openai.com/codex/app-server#api-overview) -describes it as setting or updating a thread's user-facing name for a loaded -thread or persisted rollout. The same API overview describes `command/exec` as -running a command without starting a thread or turn, which makes App Server -calls a better fit than launching `codex exec`. - -`codex exec` is a poorer fit for automatic thread naming because it starts a -new agent run. That makes a metadata-only update depend on another model turn -and risks making the naming utility visible as its own thread activity. - -The hook's rename mode starts a short-lived App Server JSONL stdio process: - -```sh -codex app-server -``` - -Override the command with `AGENTDECK_APP_SERVER_COMMAND`. The hook sends -`initialize`, `initialized`, `thread/read`, and `thread/name/set` over stdio. -This path works with the regular CLI install and does not require the managed -app-server daemon or the standalone Codex installer. - -`AGENTDECK_THREAD_TITLE_POLL_ATTEMPTS` and -`AGENTDECK_THREAD_TITLE_POLL_DELAY_MS` control the bounded wait for Codex's -generated title during the `Stop` hook. If the title is still missing when the -poll expires, the hook skips the rename and can try again on a later `Stop`. - -An MCP tool can still be useful as an operator-facing helper, but it should be -implemented as a thin path to App Server metadata operations rather than as an -agent loop. - -## Tool Timing Diagnostics - -The `PostToolUse` hook records compact summaries in `tool-use-events.jsonl` and -the raw payload in `thread-title-payloads.jsonl`. The compact summary includes -the timestamp, thread id candidate, turn id, cwd, best-effort tool name, tool id, -status, and sorted payload keys. - -This diagnostic hook is intentionally read-only. It exists to answer whether -Codex's title generation appears as a hook-visible tool event and to compare -that timing with `Stop` decisions. - -## Tested Alternate Route - -An adjacent thread tested a more model-mediated route for prefixing thread -names. A `SessionStart` hook injected developer context that asked the new -thread itself to call its rename tool. That successfully renamed a generated -title to: - -```text -heya-codex-i-d-like-to: Inspect thread naming controls -``` - -This route is worth keeping as a future refinement option because it preserves -Codex's generated title and only adds the project prefix around it. The tradeoff -is that it depends on model and tool compliance, and the instruction is more -visible inside the new thread than the direct App Server hook route. - -## GUI Test Notes - -Before testing, confirm the active Codex config uses the current feature keys: -`features.hooks = true` and the stable plugin feature. Removed keys such as -`features.plugin_hooks` are not required and should not appear in operator -guidance. - -An installed `agentdeck@socket` 6.16.0 probe initially did not expose the -plugin-bundled `SessionStart` hook until the Codex GUI was restarted. After the -restart, the hook appeared in settings, was trusted, and captured real -`SessionStart` payloads. - -1. Install or refresh the Socket marketplace plugin locally. -2. Confirm the hook source is visible in Codex hook settings or `/hooks`. -3. Trust the `agentdeck` `SessionStart`, `Stop`, and `PostToolUse` hooks - if they appear. -4. Start a new thread in a known working directory. -5. Confirm `thread-title-decisions.jsonl` records `SessionStart` and `Stop` - decisions for the new thread. -6. Confirm the first `Stop` records the wait-for-second-Stop decision. -7. Send a second turn, then confirm the generated title is prefixed after the - second `Stop` event. -8. Inspect `tool-use-events.jsonl` to see whether title generation appears as a - hook-visible tool event. -9. Use `AGENTDECK_THREAD_TITLE_MODE=capture` or `dry-run` only when - debugging the hook without mutating thread metadata. -10. Keep the model-mediated prefix route as a comparison path if direct App - Server renaming loses too much of Codex's generated-title quality. diff --git a/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh b/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh index d2e7c1a8c..1c24c9dc7 100644 --- a/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh +++ b/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh @@ -30,7 +30,6 @@ echo "Validating root docs presence..." [[ -f AGENTS.md ]] || fail "Missing AGENTS.md at repo root." [[ -f docs/maintainers/reality-audit.md ]] || fail "Missing docs/maintainers/reality-audit.md." [[ -f docs/maintainers/customization-consolidation-review.md ]] || fail "Missing docs/maintainers/customization-consolidation-review.md." -[[ -f docs/maintainers/execution-split-and-inference-plan.md ]] || fail "Missing docs/maintainers/execution-split-and-inference-plan.md." echo "Validating local discovery mirrors..." [[ -L ".agents/skills" ]] || fail "Expected .agents/skills to be a symlink to ../skills" @@ -46,7 +45,6 @@ require_contains "README.md" 'Treat root [`skills/`](./skills/) as the canonical require_contains "README.md" 'Keep shared reusable assets in [`shared/`](./shared/)' require_contains "README.md" 'Run the repository test suite for skill and metadata changes:' require_contains "README.md" 'Use [`CONTRIBUTING.md`](./CONTRIBUTING.md) for maintainer workflow details' -require_not_contains "README.md" 'plugins/apple-dev-skills/' require_not_contains "README.md" 'install-plugin-to-socket' echo "Validating CONTRIBUTING contract..." @@ -54,8 +52,8 @@ require_contains "CONTRIBUTING.md" 'Use this guide when preparing changes so the require_contains "CONTRIBUTING.md" '## Contribution Workflow' require_contains "CONTRIBUTING.md" '## Local Setup' require_contains "CONTRIBUTING.md" '## Development Expectations' -require_contains "CONTRIBUTING.md" 'bash .github/scripts/validate_repo_docs.sh' -require_contains "CONTRIBUTING.md" 'uv run pytest' +require_contains "CONTRIBUTING.md" 'bash plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh' +require_contains "CONTRIBUTING.md" 'uv run python -B -m pytest plugins/apple-dev-skills/tests' echo "Validating AGENTS contract..." require_contains "AGENTS.md" 'This repository is the canonical home for Gale'"'"'s Apple, Swift, and Xcode workflow skills.' @@ -65,7 +63,6 @@ require_contains "AGENTS.md" 'Root `skills/` is the canonical authored and expor require_contains "AGENTS.md" 'Keep shared reusable assets in [`shared/`](./shared/) and maintainer tests in [`tests/`](./tests/).' require_contains "AGENTS.md" 'require reading the relevant Apple documentation before proposing implementation changes.' require_contains "AGENTS.md" 'Keep `explore-apple-swift-docs` as the canonical docs-routing surface' -require_not_contains "AGENTS.md" 'plugins/apple-dev-skills/' echo "Validating reality audit guide..." audit_doc="docs/maintainers/reality-audit.md" @@ -77,7 +74,6 @@ require_contains "$audit_doc" '`repository-skills` owns the reusable `maintain-p require_contains "$audit_doc" 'standalone `apple-dev-skills` installs from installs that also include the `repository-skills` companion plugin' require_contains "$audit_doc" 'this repository owns only the Apple-specific profile selection and Xcode MCP registration contract' require_contains "$audit_doc" 'Historical milestone planning decisions that no longer need standalone docs should live in `ROADMAP.md`' -require_not_contains "$audit_doc" 'plugins/apple-dev-skills/' echo "Validating customization consolidation review..." customization_review_doc="docs/maintainers/customization-consolidation-review.md" @@ -89,14 +85,6 @@ require_contains "$customization_review_doc" "## Follow-Up Plan" require_contains "$customization_review_doc" "Milestone 20 concludes that the repo should shrink the customization surface rather than expand it." require_contains "$customization_review_doc" "## Sync Skill Simplification Decision" require_contains "$customization_review_doc" 'implemented replacement: `writeMode`' -execution_split_doc="docs/maintainers/execution-split-and-inference-plan.md" -require_contains "$execution_split_doc" "## Target Skill Matrix" -require_contains "$execution_split_doc" "## Guidance Preservation Contract" -require_contains "$execution_split_doc" "## AGENTS Expansion Strategy" -require_contains "$execution_split_doc" "## Repo-Maintenance Direction" -require_contains "$execution_split_doc" "## Implementation Plan" -require_contains "$execution_split_doc" '`repository-skills/maintain-project-repo` as the canonical shipped repo-maintenance surface' -require_contains "$execution_split_doc" 'Users who install only `apple-dev-skills` still get the Apple-only workflows' require_contains "ROADMAP.md" "Completed Milestones 22 and 23" require_contains "ROADMAP.md" 'See `docs/maintainers/customization-consolidation-review.md`.' require_contains "ROADMAP.md" "Completed Milestones 30 through 36" diff --git a/plugins/apple-dev-skills/.gitignore b/plugins/apple-dev-skills/.gitignore deleted file mode 100644 index c513792cb..000000000 --- a/plugins/apple-dev-skills/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -__pycache__/ -*.pyc - -# Agent plugin repo local runtime state -.codex/plugins/ diff --git a/plugins/apple-dev-skills/AGENTS.md b/plugins/apple-dev-skills/AGENTS.md index bf44f7990..b99a1a8f7 100644 --- a/plugins/apple-dev-skills/AGENTS.md +++ b/plugins/apple-dev-skills/AGENTS.md @@ -36,9 +36,13 @@ This file is the Apple Dev Skills child-repo override for work done from `socket ## Validation +Run from the Socket repository root so the shared maintainer environment and +cache policy apply: + ```bash -bash .github/scripts/validate_repo_docs.sh -uv run pytest +bash plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh +uv run python -B -m pytest plugins/apple-dev-skills/tests \ + -o cache_dir=.codex/.cache/pytest ``` Use the docs validator when README, AGENTS, ROADMAP, active skill inventory, or maintainer docs change. Use pytest when skill behavior, scripts, validation helpers, or tested contracts change. diff --git a/plugins/apple-dev-skills/CONTRIBUTING.md b/plugins/apple-dev-skills/CONTRIBUTING.md index 99d0ebb49..f3da2224d 100644 --- a/plugins/apple-dev-skills/CONTRIBUTING.md +++ b/plugins/apple-dev-skills/CONTRIBUTING.md @@ -67,8 +67,9 @@ This repository does not currently maintain a separate root `ACCESSIBILITY.md`. Use the grounded repo checks: ```bash -bash .github/scripts/validate_repo_docs.sh -uv run pytest +bash plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh +uv run python -B -m pytest plugins/apple-dev-skills/tests \ + -o cache_dir=.codex/.cache/pytest ``` Run additional targeted checks only when the changed surface has a narrower validation path worth calling out. diff --git a/plugins/apple-dev-skills/README.md b/plugins/apple-dev-skills/README.md index 9e12e642e..bd5372b32 100644 --- a/plugins/apple-dev-skills/README.md +++ b/plugins/apple-dev-skills/README.md @@ -124,8 +124,9 @@ Use [`CONTRIBUTING.md`](./CONTRIBUTING.md) for maintainer workflow details, and Run the repository test suite for skill and metadata changes: ```bash -bash .github/scripts/validate_repo_docs.sh -uv run pytest +bash plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh +uv run python -B -m pytest plugins/apple-dev-skills/tests \ + -o cache_dir=.codex/.cache/pytest ``` ## Repo Structure diff --git a/plugins/apple-dev-skills/ROADMAP.md b/plugins/apple-dev-skills/ROADMAP.md index 66b45ecfb..3f06d480c 100644 --- a/plugins/apple-dev-skills/ROADMAP.md +++ b/plugins/apple-dev-skills/ROADMAP.md @@ -1279,7 +1279,8 @@ Completed ### Tickets -- [x] Use [`app-extension-workflows-plan.md`](./docs/maintainers/app-extension-workflows-plan.md) as the durable ownership and delivery record. +- [x] Record the durable ownership split in `AGENTS.md` and the delivered + behavior in the three focused skill contracts and tests. - [x] Add the three skills with Apple documentation anchors, interface metadata, customization contracts, shared Xcode project references, and explicit Xcode/testing/distribution handoffs. - [x] Update Apple Dev Skills metadata, active inventory, README, AGENTS guidance, docs validator, customization review, and targeted tests. - [x] Export the portable instruction-only skills to the root Hermes tap and validate the generated inventory without representing the Codex plugin bundle as a Hermes plugin. diff --git a/plugins/apple-dev-skills/docs/maintainers/app-extension-workflows-plan.md b/plugins/apple-dev-skills/docs/maintainers/app-extension-workflows-plan.md deleted file mode 100644 index d6ed49810..000000000 --- a/plugins/apple-dev-skills/docs/maintainers/app-extension-workflows-plan.md +++ /dev/null @@ -1,30 +0,0 @@ -# App Extension Workflows Plan - -## Decision - -Apple Dev Skills owns reusable Apple app-extension mechanics plus the specific macOS MailKit and File Provider/Finder Sync workflows. This is a durable building-block change: it removes the gap between app-level architecture skills and extension-specific framework work without adding a generic extension framework or absorbing product policy. - -## Owned Workflows - -- `app-extension-architecture-workflow` routes extension points, targets, separate processes, lifecycle, activation, entitlement minimization, App Groups/shared containers, typed data flow, privacy, testing, signing, distribution, and focused handoffs. -- `mailkit-workflow` owns macOS MailKit content blockers, message actions, compose sessions, message security, capability declarations, privacy, and handler validation. -- `file-provider-and-finder-sync-workflow` owns remote storage synchronization through File Provider and constrains Finder Sync to monitored-folder badges, menus, selected-item context, and visibility. - -## Explicit Non-Goals - -- Do not turn this slice into a catch-all guide for every extension point. -- Do not place Messages/iMessage collaboration, communication-notification policy, VoIP, or Push to Talk behavior here; Messaging Collaboration Skills owns those product workflows. -- Do not make Finder Sync a sync engine. File Provider owns remote storage enumeration, materialization, document operations, remote change signaling, and recovery. -- Do not move server transport, storage protocol, or backend persistence ownership into Apple Dev Skills. - -## Documentation Evidence - -The shipped workflows cite Apple documentation for the separate-process app-extension model, MailKit handler capabilities, File Provider synchronization and working sets, Finder Sync controller UI, App Groups, and per-target signing/distribution boundaries. A current Xcode documentation search is required before future platform-behavior changes. - -## Delivery Checklist - -- [x] Add the three bounded skills with interface metadata, customization contract, shared Xcode policy reference, and authoritative Apple source links. -- [x] Add targeted tests and update the active inventory, plugin manifest, README, AGENTS guidance, docs validator, and customization review. -- [x] Export the portable instruction-only skills through the checked-in Hermes skill tap, group them, and validate the export. -- [x] Keep MailKit/File Provider/Finder Sync as product-specific handoffs from reusable extension mechanics. -- [ ] Validate a real app target for each framework only when a consumer app supplies the concrete project, signing identity, and service/backend context. diff --git a/plugins/apple-dev-skills/docs/maintainers/execution-split-and-inference-plan.md b/plugins/apple-dev-skills/docs/maintainers/execution-split-and-inference-plan.md deleted file mode 100644 index 0800b34fb..000000000 --- a/plugins/apple-dev-skills/docs/maintainers/execution-split-and-inference-plan.md +++ /dev/null @@ -1,153 +0,0 @@ -# Execution Split and Inference Plan - -Date: 2026-04-08 - -Status: Historical. Repository-shape inference and `sync-swift-package-guidance` -were retired by the unified Xcode workspace contract. All Swift product repositories -now use one workspace entry point with `Apps/`, `Packages/`, and `Services/` component -surfaces. - -## Purpose - -Record the planned split of the current execution skills into narrower build-run and testing skills, define the guidance-preservation contract for that split, and establish the companion plan for Swift and Xcode-oriented `maintain-project-repo` profiles selected by this shipped Apple plugin. - -## Current Problem - -The package side of the split is now underway, but the overall execution surface is still broader than it should be: - -- `xcode-app-project-workflow` -- `swift-package-build-run-workflow` -- `swift-package-testing-workflow` -- `swift-package-workflow` as a compatibility surface - -Those skills now contain good guidance, but they still force the agent to do too much of the routing work manually, especially on the Xcode side and in broad compatibility entry points: - -- classify build versus run versus testing intent before the runtime wrapper can help -- decide when testing-specific guidance, package-resource guidance, Metal handoff guidance, or Debug-vs-Release guidance matters -- bounce between neighboring skills even when the repo shape and request wording already make the likely path obvious - -The plugin-first install model reduces the old pressure to minimize skill count. That means the repo can favor narrower skill boundaries and stronger inference instead of preserving monolithic workflow skills for install simplicity. - -## Target Skill Matrix - -Planned execution skills: - -- `xcode-build-run-workflow` - - Xcode-aware build, run, launch, preview-adjacent execution, scheme and destination inference, configuration selection, artifact validation, and `.pbxproj` safety boundaries when execution work crosses project-integrity concerns. -- `xcode-testing-workflow` - - Swift Testing, XCTest, XCUITest, `.xctestplan`, destinations, filters, retries, diagnostics, and testing-specific fallback behavior for Xcode-managed work. -- `swift-package-build-run-workflow` - - SwiftPM-first build and run flows, target inference, manifest-adjacent execution, package resources, `Bundle.module`, Metal library distribution guidance, and Release-vs-Debug validation for package repos. -- `swift-package-testing-workflow` - - Swift Testing-first package testing, XCTest holdouts, async testing guidance, package-level Xcode test-plan execution when needed, and testing-specific fallback behavior for package repos. - -Skills that remain separate and keep their current high-level role: - -- `explore-apple-swift-docs` -- `bootstrap-swift-package` -- `bootstrap-xcode-workspace` for product-level alignment -- `format-swift-sources` -- `structure-swift-sources` - -## Guidance Preservation Contract - -No guidance currently carried by `xcode-app-project-workflow` or `swift-package-workflow` should be dropped during the split. - -The line-by-line preservation work from the split is now reflected directly in the shipped narrower skills, the synced and bootstrapped `AGENTS.md` assets, and the repo validator. Keep this document as the durable narrative for that split instead of reviving a separate guidance-preservation matrix file. - -The split must preserve every active guidance area in one of these forms: - -1. Retained directly inside the narrower replacement skill. -2. Moved into a shared reference that the narrower skills still require. -3. Promoted into the synced or bootstrapped `AGENTS.md` baseline when it is a durable repo policy rather than an execution-only concern. - -### Current guidance that must survive - -- Apple docs gate before implementation guidance. -- Simplicity-first Swift guidance. -- Structured concurrency guidance, including cancellation-aware async design and sendability boundaries. -- Swift Testing, XCTest, XCUITest, and `.xctestplan` guidance. -- Package resources, `Bundle.module`, `Resource.process(...)`, `Resource.copy(...)`, and `Resource.embedInCode(...)`. -- Metal library distribution guidance and the Xcode handoff when Apple-managed Metal toolchain behavior matters. -- Debug-vs-Release validation guidance, including tagged-release expectations. -- Xcode file-membership and target-membership verification after on-disk edits. -- `.pbxproj` warning boundary. -- Plugin-install and downstream-guidance-sync recommendations for this repo's own maintenance flow. - -## AGENTS Expansion Strategy - -The split is a good opportunity to move more durable policy into synced and bootstrapped `AGENTS.md` output rather than leaving it only in execution-skill prose. - -Good candidates for stronger `AGENTS.md` presence: - -- Swift Testing default preference and XCTest/XCUITest role boundaries. -- `.xctestplan` expectations when the repo uses test plans. -- Package-resource and `Bundle.module` expectations. -- Metal distribution and Xcode handoff guidance for packages. -- File-membership verification after script-driven on-disk edits in Xcode repos. -- Debug-vs-Release validation expectations and tagged-release behavior. -- Repo-maintenance sync expectations after substantial workflow or plugin updates. - -Keep execution-only mechanics out of `AGENTS.md` when they are too tied to tool behavior or a transient runtime path. - -## Repo-Maintenance Direction - -The repo treats `repository-skills/maintain-project-repo` as the canonical shipped repo-maintenance surface. `apple-dev-skills` owns the unified workspace and Apple guidance; it does not classify repositories or own a second toolkit implementation. - -Planned profile shape: - -- `generic` -- one unified Swift product profile for workspaces containing any combination of apps, packages, and services - -The `socket` marketplace lists `repository-skills` and `apple-dev-skills` together, so users can add one catalog and then install or enable both plugin entries from the Socket marketplace instead of wiring each plugin marketplace one by one. Users who install only `apple-dev-skills` still get the Apple-only workflows, but mutating bootstrap and guidance-sync workflows must explain that repo-maintenance install or refresh requires the `repository-skills` companion plugin. - -Current Apple-side integration status: - -- `maintain-project-repo` is profile-aware -- workspace bootstrap and alignment explicitly run `maintain-project-repo` with the unified Swift product profile -- installed repos now get `scripts/repo-maintenance/config/profile.env` as a concrete profile marker -- Apple-specific formatting assets are promoted into the `maintain-project-repo` Apple profile overlay - -## Implementation Plan - -### Phase 1: Inference-first runtime improvements - -- Teach the current execution wrappers to infer likely intent from request wording. -- Keep explicit override input available when inference would be risky. -- Return the inferred intent in the runtime payload so downstream skills and tests can assert it. - -### Phase 2: Execution split - -- Add the four narrower execution skills. -- Convert the current two execution skills into compatibility and migration surfaces for one release cycle. -- Update docs, tests, and install surfaces to point to the new skill matrix. - -Current status: - -- the Swift package side is landed -- the Xcode side is landed -- both original broad execution skills now act as compatibility-routing surfaces -- the remaining work is inference strengthening plus the toolkit follow-through - -### Phase 3: Guidance redistribution - -- Promote durable execution-adjacent policy into synced and bootstrapped `AGENTS.md` templates where appropriate. -- Keep volatile runtime behavior in skill-local references and wrappers. -- Re-audit the guidance-preservation contract after the split lands. - -### Phase 4: Toolkit consolidation - -- Keep Swift and Xcode-oriented repo-maintenance profile support canonical in `repository-skills/maintain-project-repo`. -- Keep Apple-specific plugin work focused on stack guidance and the permanent workspace/component model. - -## First Implementation Slice - -The first implementation slice did not wait for the full skill split. - -It: - -- added request-driven intent inference to the current `swift-package-workflow` and `xcode-app-project-workflow` runtime wrappers before the split -- documented the planned split and preservation contract -- updated roadmap and maintainer docs so the future work is tracked explicitly - -That inference work survived the later split and directly reduced current agent ceremony. diff --git a/plugins/apple-dev-skills/docs/maintainers/reality-audit.md b/plugins/apple-dev-skills/docs/maintainers/reality-audit.md index 7bfcb9ab5..1f1a746eb 100644 --- a/plugins/apple-dev-skills/docs/maintainers/reality-audit.md +++ b/plugins/apple-dev-skills/docs/maintainers/reality-audit.md @@ -28,7 +28,6 @@ Root `skills/` is the canonical workflow-authoring surface. - `AGENTS.md` - `ROADMAP.md` - `docs/maintainers/` - - especially `execution-split-and-inference-plan.md` when validating compatibility surfaces and guidance-preservation rules Deprecated compatibility skills that remain on disk do not count as part of the active public skill surface unless the validator and root docs explicitly say otherwise. @@ -46,7 +45,7 @@ Deprecated compatibility skills that remain on disk do not count as part of the - docs do not reintroduce a nested packaged plugin tree or any other second export surface under `plugins/` - docs do not tell maintainers to use removed installer or install-validator skills - docs describe top-level `skills/` as the active export surface today, with top-level `mcps/` or `apps/` only if those directories are added later -4. Run `bash .github/scripts/validate_repo_docs.sh` and treat failures as documentation-contract drift unless code assets prove otherwise. +4. From the Socket root, run `bash plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh` and treat failures as documentation-contract drift unless code assets prove otherwise. - for repo-maintenance drift inside this repo, compare Apple behavior against `repository-skills/skills/maintain-project-repo/` - when intentionally syncing ideas from another repo, reconcile them into `maintain-project-repo` first, then keep this repo limited to Apple guidance and profile selection - for Xcode MCP drift, compare the plugin metadata against Apple's documented `codex mcp add xcode -- xcrun mcpbridge` setup @@ -58,8 +57,8 @@ Deprecated compatibility skills that remain on disk do not count as part of the Use this flow when validating the current top-level export surface and local discovery mirrors instead of checking a nested packaged plugin tree. -1. Run `bash .github/scripts/validate_repo_docs.sh`. -2. Run `uv run --group dev pytest`. +1. From the Socket root, run `bash plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh`. +2. Run `uv run python -B -m pytest plugins/apple-dev-skills/tests -o cache_dir=.codex/.cache/pytest`. 3. Confirm `.agents/skills` still points at `../skills`. 4. Confirm root docs, skill docs, and the roadmap all describe top-level `skills/` as the active export surface and do not mention a nested packaged plugin tree or removed installer workflows. 5. If discovery or docs drift remains, update the docs to match the tested top-level export surface instead of preserving stale packaging language. @@ -76,13 +75,14 @@ Use this flow when validating the current top-level export surface and local dis - Historical notes may mention retired or deprecated skills only in migration context. - Maintainer docs must not imply that repo-root files are required when the canonical files live under `docs/maintainers/`. - Docs and skill guidance must not mention removed installer or install-validator skills as if they still exist. -- Maintainer Python tooling guidance should stay explicit about repo-local `pyproject.toml` dev dependencies and `uv run` commands for tools such as `pytest`, `ruff`, and `mypy`, rather than relying on machine-global installs. +- Maintainer Python tooling guidance should use Socket's root `pyproject.toml`, + root `uv` environment, and centralized `.codex/.cache/` paths rather than + child environments, child caches, or machine-global installs. ## Current Canonical Maintainer Docs - Audit procedure and source-of-truth order: `docs/maintainers/reality-audit.md` - Customization-surface decision and follow-up plan: `docs/maintainers/customization-consolidation-review.md` -- Execution-skill split, inference plan, and guidance-preservation contract: `docs/maintainers/execution-split-and-inference-plan.md` - Historical milestone planning decisions that no longer need standalone docs should live in `ROADMAP.md` under the milestone body or `History`. ## Reporting Shape diff --git a/plugins/apple-dev-skills/pyproject.toml b/plugins/apple-dev-skills/pyproject.toml deleted file mode 100644 index 0ae5c5af2..000000000 --- a/plugins/apple-dev-skills/pyproject.toml +++ /dev/null @@ -1,18 +0,0 @@ -[project] -name = "apple-dev-skills-maintainer" -version = "10.0.0" -description = "Maintainer tooling for the apple-dev-skills repository" -requires-python = ">=3.10" -dependencies = [] - -[dependency-groups] -dev = [ - "pytest>=9.0.3", - "PyYAML>=6.0.2,<7", -] - -[tool.pytest.ini_options] -testpaths = ["tests"] - -[tool.uv] -package = false diff --git a/plugins/apple-dev-skills/skills/icon-composer-app-icon-workflow/SKILL.md b/plugins/apple-dev-skills/skills/icon-composer-app-icon-workflow/SKILL.md index a7c14f763..d5f5f5e21 100644 --- a/plugins/apple-dev-skills/skills/icon-composer-app-icon-workflow/SKILL.md +++ b/plugins/apple-dev-skills/skills/icon-composer-app-icon-workflow/SKILL.md @@ -247,7 +247,9 @@ uv run scripts/validate_socket_metadata.py bash plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh ``` -From `plugins/apple-dev-skills`, run `uv run pytest` only when tests, validation helpers, or scripts changed. +From the Socket root, run the Apple Dev tests through +`uv run scripts/validate_socket.py --profile full` when tests, validation +helpers, or scripts changed. ## Handoffs diff --git a/plugins/apple-dev-skills/uv.lock b/plugins/apple-dev-skills/uv.lock deleted file mode 100644 index 7721dd7af..000000000 --- a/plugins/apple-dev-skills/uv.lock +++ /dev/null @@ -1,224 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.10" - -[[package]] -name = "apple-dev-skills-maintainer" -version = "10.0.0" -source = { virtual = "." } - -[package.dev-dependencies] -dev = [ - { name = "pytest" }, - { name = "pyyaml" }, -] - -[package.metadata] - -[package.metadata.requires-dev] -dev = [ - { name = "pytest", specifier = ">=9.0.3" }, - { name = "pyyaml", specifier = ">=6.0.2,<7" }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "exceptiongroup" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "packaging" -version = "26.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "pygments" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, -] - -[[package]] -name = "pytest" -version = "9.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/84/0e/b5858858d74958632c49b72cb25a3976ff9f632397626715be71c89d3971/pytest-9.1.0.tar.gz", hash = "sha256:41dd9148c08072446394cefd3d79701701335a9f4cae69ba92e39f6c7f5c061c", size = 1634181, upload-time = "2026-06-13T18:52:45.983Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/5a/ba30a81239b909821b3153e303e7def45178bf353da4f72380e6c5e8793b/pytest-9.1.0-py3-none-any.whl", hash = "sha256:8ebb0e7888bdf2bdfc602ec51f8f62d50200af37356c74e503c79a94f5c81f32", size = 386453, upload-time = "2026-06-13T18:52:44.045Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, - { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, - { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, - { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, - { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, - { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, - { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, - { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, - { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, - { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, - { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, - { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, - { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, - { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, - { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "tomli" -version = "2.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, - { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, - { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, - { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, - { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, - { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, - { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, - { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, - { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, - { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, - { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, - { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, - { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, - { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, - { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, - { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, - { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, - { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, - { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, - { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, - { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, - { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, - { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, - { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, - { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, - { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, - { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, - { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, - { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, - { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, - { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, - { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, - { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, - { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, - { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, - { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, - { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, - { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, - { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, - { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, - { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, - { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, - { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, - { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] diff --git a/plugins/professional-skills/AGENTS.md b/plugins/professional-skills/AGENTS.md index bda6403fc..a8db6219c 100644 --- a/plugins/professional-skills/AGENTS.md +++ b/plugins/professional-skills/AGENTS.md @@ -14,6 +14,11 @@ documentation rules. ## Validation +Run from the Socket repository root so the shared maintainer environment and +cache policy apply: + ```bash -uv run pytest +uv run python -B -m pytest \ + plugins/professional-skills/skills/dice-job-search-workflow/tests \ + -o cache_dir=.codex/.cache/pytest ``` diff --git a/plugins/professional-skills/pyproject.toml b/plugins/professional-skills/pyproject.toml deleted file mode 100644 index 29dea0aa3..000000000 --- a/plugins/professional-skills/pyproject.toml +++ /dev/null @@ -1,12 +0,0 @@ -[project] -name = "professional-skills-maintenance" -version = "10.0.0" -description = "Maintainer-only Python tooling baseline for professional-skills." -requires-python = ">=3.11" -dependencies = [] - -[dependency-groups] -dev = ["pytest>=9.0.0", "PyYAML>=6.0.0"] - -[tool.pytest.ini_options] -testpaths = ["skills/dice-job-search-workflow/tests"] diff --git a/plugins/professional-skills/uv.lock b/plugins/professional-skills/uv.lock deleted file mode 100644 index 6eed00f88..000000000 --- a/plugins/professional-skills/uv.lock +++ /dev/null @@ -1,138 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.11" - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "packaging" -version = "26.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "professional-skills-maintenance" -version = "10.0.0" -source = { virtual = "." } - -[package.dev-dependencies] -dev = [ - { name = "pytest" }, - { name = "pyyaml" }, -] - -[package.metadata] - -[package.metadata.requires-dev] -dev = [ - { name = "pytest", specifier = ">=9.0.0" }, - { name = "pyyaml", specifier = ">=6.0.0" }, -] - -[[package]] -name = "pygments" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, -] - -[[package]] -name = "pytest" -version = "9.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, - { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, - { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, - { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, - { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, - { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, - { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, - { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, - { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] diff --git a/plugins/python-skills/.gitignore b/plugins/python-skills/.gitignore deleted file mode 100644 index 4f957b94e..000000000 --- a/plugins/python-skills/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.codex/profiles/ -.worktrees/ - -# Agent plugin repo local runtime state -.codex/plugins/ diff --git a/plugins/python-skills/AGENTS.md b/plugins/python-skills/AGENTS.md index c5c39edd2..ce0cbea42 100644 --- a/plugins/python-skills/AGENTS.md +++ b/plugins/python-skills/AGENTS.md @@ -21,9 +21,16 @@ This file is the Python Skills child-repo override for work done from `socket`. ## Validation +Run from the Socket repository root so the shared maintainer environment and +cache policy apply: + ```bash -uv run scripts/validate_repo_metadata.py -uv run pytest -uv run ruff check . -uv run mypy . +(cd plugins/python-skills && \ + uv run --project ../.. python -B scripts/validate_repo_metadata.py) +uv run python -B -m pytest plugins/python-skills/tests \ + -o cache_dir=.codex/.cache/pytest +uv run ruff check --cache-dir .codex/.cache/ruff/python-skills \ + plugins/python-skills +uv run mypy --cache-dir .codex/.cache/mypy/python-skills \ + plugins/python-skills ``` diff --git a/plugins/python-skills/pyproject.toml b/plugins/python-skills/pyproject.toml deleted file mode 100644 index af38f6f8b..000000000 --- a/plugins/python-skills/pyproject.toml +++ /dev/null @@ -1,21 +0,0 @@ -[project] -name = "python-skills-maintainer" -version = "10.0.0" -description = "Maintainer tooling for the python-skills repository" -requires-python = ">=3.11" -dependencies = [] - -[dependency-groups] -dev = [ - "mypy>=1.20.1", - "pytest>=9.0.3", - "PyYAML>=6.0.2,<7", - "ruff>=0.14.0", - "types-PyYAML>=6.0.12.20250915", -] - -[tool.pytest.ini_options] -testpaths = ["tests"] - -[tool.uv] -package = false diff --git a/plugins/python-skills/uv.lock b/plugins/python-skills/uv.lock deleted file mode 100644 index 9f50f123a..000000000 --- a/plugins/python-skills/uv.lock +++ /dev/null @@ -1,373 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.11" -resolution-markers = [ - "python_full_version >= '3.15'", - "python_full_version < '3.15'", -] - -[[package]] -name = "ast-serialize" -version = "0.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/9d/09e27731bd5864a9ce04e3244074e674bb8936bf62b45e0357248717adac/ast_serialize-0.5.0.tar.gz", hash = "sha256:5880091bfe6f4f986f22866375c2e884843e7a0b6343ae41aeea659613d879b6", size = 61157, upload-time = "2026-05-17T17:48:29.429Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/9a/13dde51ba9e15f8b97957ab7cb0120d0e381524d651c6bd630b9c359227f/ast_serialize-0.5.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8f5c14f169eb0972c0c21bada5358b23d6047c76583b005234f865b11f1fa00a", size = 1183520, upload-time = "2026-05-17T17:47:30.831Z" }, - { url = "https://files.pythonhosted.org/packages/37/de/5a7f0a9fe68944f536632a5af84676739c7d2582be42deb082634bf3a754/ast_serialize-0.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7d1a2de9de5be04652f0ed60738356ef94f66db37924a9499fffe98dc491aa0b", size = 1175779, upload-time = "2026-05-17T17:47:32.551Z" }, - { url = "https://files.pythonhosted.org/packages/9c/81/0bb853e76e4f6e9a1855d569003c59e19ffac45f7079d91505d1bb212f92/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be5173fb66f9b49026d9d5a2ff0fc7c7009077107c0eb285b2d60fdf1fe10bd1", size = 1233750, upload-time = "2026-05-17T17:47:34.731Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d3/4cf705beeccc08754d0bbda99aefff26110e209b9a07ac8a6b60eec48531/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8015cd071ac1339924ee2b8098c93e00e155f30a16f40ec9816fcf84f4753f6", size = 1235942, upload-time = "2026-05-17T17:47:36.287Z" }, - { url = "https://files.pythonhosted.org/packages/26/c8/ee097e437ea27dd2b8b227865c875492b585650a5802a22d82b304c8201b/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5499e8797edff2a9186aa313ed382c6b422e798e9332d9953badcee6e69a88f2", size = 1442517, upload-time = "2026-05-17T17:47:38.17Z" }, - { url = "https://files.pythonhosted.org/packages/ff/bd/68063442838f1ba68ec72b5436430bc75b3bb17a1a3c3063f09b0c05ae2b/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6848f2a093fb5548751a9a09bff8fcd229e2bbeb0e3331f391b6ae6d26cd9903", size = 1254081, upload-time = "2026-05-17T17:47:39.826Z" }, - { url = "https://files.pythonhosted.org/packages/50/e2/1e520793bc6a4e4524a6ab022391e827825eaa0c3811828bfdc6852eca26/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:832d4c998e0b091fd60a6d6bceee535483c4d490de9ba85003af835225719261", size = 1259910, upload-time = "2026-05-17T17:47:41.369Z" }, - { url = "https://files.pythonhosted.org/packages/4e/e1/49b60f467979979cfe6913b43948ff25bca971ad0591d181812f163a988e/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:16db7c62ec0b8efe1d7afd283a388d8f74f2605d56032e5a37747d2de8dba027", size = 1250678, upload-time = "2026-05-17T17:47:43.702Z" }, - { url = "https://files.pythonhosted.org/packages/74/ba/66ab9555de6275677566f6574e5ef6c29cb185ea866f643bc06f8280a8ee/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:baf5eb061eb5bccade4128ad42da33787d72f6013809cd1b590376ece8b3c937", size = 1301603, upload-time = "2026-05-17T17:47:46.256Z" }, - { url = "https://files.pythonhosted.org/packages/66/42/6aca9b9abc710014b2be9059689e5dd1679339e78f567ffb4d255a9e2050/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:104e4a35bd7c124173c41760ef9aaea17ddb3f86c65cb643671d59afbe3ee94c", size = 1410332, upload-time = "2026-05-17T17:47:47.899Z" }, - { url = "https://files.pythonhosted.org/packages/47/68/2f76594432a22581ecf878b5e75a9b8601c24b2241cf0bbeb1e21fcf370c/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:36be371028fc1675acb38a331bde160dbab7ff907fdf00b67eb6911aa106951b", size = 1509979, upload-time = "2026-05-17T17:47:50.942Z" }, - { url = "https://files.pythonhosted.org/packages/40/ac/a93c9b58292653f6c595752f677a08e608f903b710594909e9231a389b3b/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:061ee58bdb52341c8201a6df41182a977736bae3b7ded87ca7176ca25a8a47ab", size = 1505002, upload-time = "2026-05-17T17:47:54.093Z" }, - { url = "https://files.pythonhosted.org/packages/14/2e/b278f68c497ee2f1d1576cbbef8db5281cd4a5f2db040537592ac9c8862e/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b15219e9cdc9f53f6f4cb51c009203507228226148c05c5e8fe451c28b435eb3", size = 1456231, upload-time = "2026-05-17T17:47:56.311Z" }, - { url = "https://files.pythonhosted.org/packages/0b/43/419be1c566a4c504cd8fd60ce2f84e790f295495c0f327cfaeadf3d51012/ast_serialize-0.5.0-cp314-cp314t-win32.whl", hash = "sha256:842d1c004bb466c7df036f95fabef789570541922b10976b12f5592a69cf0b38", size = 1058668, upload-time = "2026-05-17T17:47:58.305Z" }, - { url = "https://files.pythonhosted.org/packages/03/6f/c9d4d549295ed05111aeb8853232d1afd9d0a179fddb01eeffbb3a4a6842/ast_serialize-0.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b0c06d760909b095cc466356dfccd05a1c7233a6ca191c020dca2c6a6f16c24c", size = 1101075, upload-time = "2026-05-17T17:48:00.35Z" }, - { url = "https://files.pythonhosted.org/packages/d0/8e/d00c5ab30c58222e07d62956fca86c59d91b9ad32997e633c38b526623a3/ast_serialize-0.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:787baedb0262cc49e8ce37cc15c00ae818e46a165a3b36f5e21ed174998104cb", size = 1075347, upload-time = "2026-05-17T17:48:01.753Z" }, - { url = "https://files.pythonhosted.org/packages/e0/9e/dc2530acb3a60dc6e46d65abf27d1d9f86721694757906a148d90a6860de/ast_serialize-0.5.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0668aa9459cfa8c9c49ddd2163ebcf43088ba045ef7492af6fe22e0098303101", size = 1191380, upload-time = "2026-05-17T17:48:03.738Z" }, - { url = "https://files.pythonhosted.org/packages/26/0a/bd3d18a582f273d6c843d16bb9e22e9e16365ff7991e92f18f798e9f1224/ast_serialize-0.5.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bf683d6363edf2b39eed6b6d4fe22d34b6203867a67e27134d9e2a2680c4bc4a", size = 1183879, upload-time = "2026-05-17T17:48:05.463Z" }, - { url = "https://files.pythonhosted.org/packages/40/ae/1f919100f8620887af58fcc381c61a1f218cdf89c6e155f87b213e61010a/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc22cf0c9be65e71cf88fda130af60d61eb4a79370ad4cfe7900d48a4aa2211", size = 1244529, upload-time = "2026-05-17T17:48:07.008Z" }, - { url = "https://files.pythonhosted.org/packages/c6/ca/6376559dcce707cdbc1d0d9a13c8d3baaaa501e949ce0ebdc4230cd881aa/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f66173891548c9f2726bf27957b41cabce12fa679dc6da505ddbde4d4b3b31cf", size = 1240560, upload-time = "2026-05-17T17:48:08.46Z" }, - { url = "https://files.pythonhosted.org/packages/35/b2/a620e206b5aeb7efbf2710336df57d457cffbb3991076bbcc1147ef9abd4/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e42d729ef2be96a14efbad355093284739e3670ece3e534f82cc8832790911d9", size = 1451172, upload-time = "2026-05-17T17:48:09.922Z" }, - { url = "https://files.pythonhosted.org/packages/fa/e0/4ad5c04c24a40481b2935ce9a0ccdb6023dc8b667167d06ae530cc3512f2/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b725026bafa801dbd7310eb13a75f0a2e370e7e51b2cb225f9d21fcfadf919ee", size = 1265072, upload-time = "2026-05-17T17:48:11.469Z" }, - { url = "https://files.pythonhosted.org/packages/b2/71/4d1d479aa56d0101c40e17720c3d6ac2af7269ea0487a80b18e7bfd1a5b7/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b54f60c1d78767a53b67eaa663f0dfac3afe606aa07f1301572f588b73d64809", size = 1270488, upload-time = "2026-05-17T17:48:13.575Z" }, - { url = "https://files.pythonhosted.org/packages/6d/4f/0de1bbe06f6edef9fde4ed12ca8e7b3ec7e6e2bd4e672c5af487f7957665/ast_serialize-0.5.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:27d51654fc240a1e87e742d353d98eb45b75f62f129086b3596ab53df2ac2a43", size = 1260702, upload-time = "2026-05-17T17:48:15.141Z" }, - { url = "https://files.pythonhosted.org/packages/75/61/e00872439cfdddcc3c1b6cdaa6e5d904ba8e26a18807c67c4e14409d0ca8/ast_serialize-0.5.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c36237c46dd1674542f2109740ea5ea485a169bf1431939ada0434e17934", size = 1311182, upload-time = "2026-05-17T17:48:16.779Z" }, - { url = "https://files.pythonhosted.org/packages/76/8e/699a5b955f7926956c95e9e1d74132acad73c2fe7a426f94da89123c20aa/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1943db345233cc7194a470f13afa9c59772c0b123dea0c9414c4d4ca54369759", size = 1421410, upload-time = "2026-05-17T17:48:18.527Z" }, - { url = "https://files.pythonhosted.org/packages/a9/ae/d5b7626874478997adc7a29ab28accf21e596fb590c944290401dfd0b29e/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df1c00022cbbcb064bfaa505aa9c9295362443ce5dacb459d1331d3da353f887", size = 1516587, upload-time = "2026-05-17T17:48:20.133Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ce/b59e02a82d9c4244d64cde502e0b00e83e38816abe19155ceb5437402c7f/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:cae65289fc456fde04af979a2be09302ef5d8ab92ef23e596d6746dc267ada27", size = 1515171, upload-time = "2026-05-17T17:48:21.921Z" }, - { url = "https://files.pythonhosted.org/packages/8b/38/d8d90042747d05aa08d4efcf1c99035a5f670a6bf4c214d31644392afbca/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:239a4c354e8d676e9d94631d1d4a64edc6b266f86ff3a5a80aedd344f342c01d", size = 1464668, upload-time = "2026-05-17T17:48:23.544Z" }, - { url = "https://files.pythonhosted.org/packages/dd/51/5b840c4df7334104cecffa28f23904fe81ca89ca223d2450e288de39fd3c/ast_serialize-0.5.0-cp39-abi3-win32.whl", hash = "sha256:143a4ef63285a075871908fda3672dc21864b83a8ec3ee12304aa3e4c5387b9a", size = 1068311, upload-time = "2026-05-17T17:48:25.027Z" }, - { url = "https://files.pythonhosted.org/packages/41/11/ca5672c7d491825bc4cd6702dea106a6b60d928707712ec257c7833ae476/ast_serialize-0.5.0-cp39-abi3-win_amd64.whl", hash = "sha256:cf25572c526add400f26a4750dc6ce0c3bb93fc1f75e7ae0cad4ce4f2cd5c590", size = 1108931, upload-time = "2026-05-17T17:48:26.591Z" }, - { url = "https://files.pythonhosted.org/packages/45/19/cc8bd127d28a43da249aa955cfd164cf8fd534e79e42cea96c4854d72fd0/ast_serialize-0.5.0-cp39-abi3-win_arm64.whl", hash = "sha256:92a31c9c20d25a076edaeec76b128a3535d74a24f340b9a8a7e96c9b86dc9642", size = 1081181, upload-time = "2026-05-17T17:48:28.122Z" }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "librt" -version = "0.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/40/08/9e7f6b5d2b5bed6ad055cdd5925f192bb403a51280f86b56554d9d0699a2/librt-0.11.0.tar.gz", hash = "sha256:075dc3ef4458a278e0195cbf6ac9d38808d9b906c5a6c7f7f79c3888276a3fb1", size = 200139, upload-time = "2026-05-10T18:17:25.138Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/87/2bf31fe17587b29e3f93ec31421e2b1e1c3e349b8bf6c7c313dbad1d5340/librt-0.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:93d95bd45b7d58343d8b90d904450a545144eec19a002511163426f8ab1fae29", size = 141092, upload-time = "2026-05-10T18:15:34.795Z" }, - { url = "https://files.pythonhosted.org/packages/cf/08/5c5bf772920b7ebac6e32bc91a643e0ab3870199c0b542356d3baa83970a/librt-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ee278c769a713638cdacd4c0436d72156e75df3ebc0166ab2b9dc43acc386c9", size = 142035, upload-time = "2026-05-10T18:15:36.242Z" }, - { url = "https://files.pythonhosted.org/packages/06/20/662a03d254e5b000d838e8b345d83303ddb768c080fd488e40634c0fa66b/librt-0.11.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f230cb1cbc9faaa616f9a678f530ebcf186e414b6bcbd88b960e4ba1b92428d5", size = 475022, upload-time = "2026-05-10T18:15:37.56Z" }, - { url = "https://files.pythonhosted.org/packages/de/f3/aa81523e45184c6ec23dc7f63263362ec55f80a09d424c012359ecbe7e35/librt-0.11.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5d63c855d86938d9de93e265c9bd8c705b51ec494de5738340ee93767a686e4b", size = 467273, upload-time = "2026-05-10T18:15:39.182Z" }, - { url = "https://files.pythonhosted.org/packages/6b/6f/59c74b560ca8853834d5501d589c8a2519f4184f273a085ffd0f37a1cc47/librt-0.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:993f028be9e96a08d31df3479ac80d99be374d17f3b78e4796b3fd3c913d4e89", size = 497083, upload-time = "2026-05-10T18:15:40.634Z" }, - { url = "https://files.pythonhosted.org/packages/fe/7b/5aa4d2c9600a719401160bf7055417df0b2a47439b9d88286ce45e56b65f/librt-0.11.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:258d73a0aa66a055e65b2e4d1b8cdb23b9d132c5bb915d9547d804fcaed116cc", size = 489139, upload-time = "2026-05-10T18:15:41.934Z" }, - { url = "https://files.pythonhosted.org/packages/d6/31/9143803d7da6856a69153785768c4936864430eec0fd9461c3ea527d9922/librt-0.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0827efe7854718f04aaddf6496e96960a956e676fe1d0f04eb41511fd8ad06d5", size = 508442, upload-time = "2026-05-10T18:15:43.206Z" }, - { url = "https://files.pythonhosted.org/packages/2f/5a/bce08184488426bda4ccc2c4964ac048c8f68ae89bd7120082eef4233cfd/librt-0.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7753e57d6e12d019c0d8786f1c09c709f4c3fcc57c3887b24e36e6c06ec938b7", size = 514230, upload-time = "2026-05-10T18:15:44.761Z" }, - { url = "https://files.pythonhosted.org/packages/89/8c/bb5e213d254b7505a0e658da199d8ab719086632ce09eef311ab27976523/librt-0.11.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:11bd19822431cc21af9f27374e7ae2e58103c7d98bda823536a6c47f6bb2bb3d", size = 494231, upload-time = "2026-05-10T18:15:46.308Z" }, - { url = "https://files.pythonhosted.org/packages/9d/fb/541cdad5b1ab1300398c74c4c9a497b88e5074c21b1244c8f49731d3a284/librt-0.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:22bdf239b219d3993761a148ffa134b19e52e9989c84f845d5d7b71d70a17412", size = 537585, upload-time = "2026-05-10T18:15:47.629Z" }, - { url = "https://files.pythonhosted.org/packages/8f/f2/464bb69295c320cb06bddb4f14a4ec67934ee14b2bffb12b19fb7ab287ba/librt-0.11.0-cp311-cp311-win32.whl", hash = "sha256:46c60b61e308eb535fbd6fa622b1ee1bb2815691c1ad9c98bf7b84952ec3bc8d", size = 100509, upload-time = "2026-05-10T18:15:49.157Z" }, - { url = "https://files.pythonhosted.org/packages/6d/e7/a17ee1788f9e4fbf548c19f4afa07c92089b9e24fef6cb2410863781ef4c/librt-0.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:902e546ff044f579ff1c953ff5fce97b636fe9e3943996b2177710c6ef076f73", size = 118628, upload-time = "2026-05-10T18:15:50.345Z" }, - { url = "https://files.pythonhosted.org/packages/cc/c7/6c766214f9f9903bcfcfbef97d807af8d8f5aa3502d247858ab17582d212/librt-0.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:65ac3bc20f78aa0ee5ae84baa68917f89fef4af63e941084dd019a0d0e749f0c", size = 103122, upload-time = "2026-05-10T18:15:52.068Z" }, - { url = "https://files.pythonhosted.org/packages/8b/d0/07c77e067f0838949b43bd89232c29d72efebb9d2801a9750184eb706b71/librt-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b87504f1690a23b9a2cca841191a04f83895d4fc2dd04df91d82b1a04ca2ad46", size = 144147, upload-time = "2026-05-10T18:15:53.227Z" }, - { url = "https://files.pythonhosted.org/packages/7a/24/8493538fa4f62f982686398a5b8f68008138a75086abdea19ade64bf4255/librt-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40071fc5fe0ce8daa6de616702314a01e1250711682b0523d6ab8d4525910cb3", size = 143614, upload-time = "2026-05-10T18:15:54.657Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1e/f8bad050810d9171f34a1648ed910e56814c2ba61639f2bd53c6377ae24b/librt-0.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:137e79445c896a0ea7b265f52d23954e05b64222ee1af69e2cb34219067cbb67", size = 485538, upload-time = "2026-05-10T18:15:56.117Z" }, - { url = "https://files.pythonhosted.org/packages/c0/fe/3594ebfbaf03084ba4b120c9ba5c3183fd938a48725e9bbe6ff0a5159ad8/librt-0.11.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:cca6644054e78746d8d4ef238681f9c34ff8b584fe6b988ecebb8db3b15e622a", size = 479623, upload-time = "2026-05-10T18:15:57.544Z" }, - { url = "https://files.pythonhosted.org/packages/b0/da/5d1876984b3746c85dbd219dbfcb73c85f54ee263fd32e5b2a632ec14571/librt-0.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5b0eea49f5562861ee8d757a32ef7d559c1d35be2aaaa1ec28941d74c9ffc8a", size = 513082, upload-time = "2026-05-10T18:15:58.805Z" }, - { url = "https://files.pythonhosted.org/packages/19/6e/55bdf5d5ca00c3e18430690bf2c953d8d3ffd3c337418173d33dec985dc9/librt-0.11.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0d1029d7e1ae1a7e647ed6fb5df8c4ce2dffefb7a9f5fd1376a4554d96dac09f", size = 508105, upload-time = "2026-05-10T18:16:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/07/10/f1f23a7c595ee90ece4d35c851e5d104b1311a887ed1b4ac4c35bbd13da8/librt-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bc3ce6b33c5828d9e80592011a5c584cb2ce86edbc4088405f70da47dc1d1b3b", size = 522268, upload-time = "2026-05-10T18:16:01.708Z" }, - { url = "https://files.pythonhosted.org/packages/b6/02/5720f5697a7f54b78b3aefbe20df3a48cedcff1276618c4aa481177942ed/librt-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:936c5995f3514a42111f20099397d8177c79b4d7e70961e396c6f5a0a3566766", size = 527348, upload-time = "2026-05-10T18:16:03.496Z" }, - { url = "https://files.pythonhosted.org/packages/50/db/b4a47c6f91db4ff76348a0b3dd0cc65e090a078b765a810a62ff9434c3d3/librt-0.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9bc0ca6ad9381cbe8e4aa6e5726e4c80c78115a6e9723c599ed1d73e092bc49d", size = 516294, upload-time = "2026-05-10T18:16:05.173Z" }, - { url = "https://files.pythonhosted.org/packages/9e/58/9384b2f4eb1ed1d273d40948a7c5c4b2360213b402ef3be4641c06299f9c/librt-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:070aa8c26c0a74774317a72df8851facc7f0f012a5b406557ac56992d92e1ec8", size = 553608, upload-time = "2026-05-10T18:16:06.839Z" }, - { url = "https://files.pythonhosted.org/packages/21/7b/5aa8848a7c6a9278c79375146da1812e695754ceec5f005e6043461a7315/librt-0.11.0-cp312-cp312-win32.whl", hash = "sha256:6bf14feb84b05ae945277395451998c89c54d0def4070eb5c08de544930b245a", size = 101879, upload-time = "2026-05-10T18:16:08.103Z" }, - { url = "https://files.pythonhosted.org/packages/37/33/8a745436944947575b584231750a41417de1a38cf6a2e9251d1065651c09/librt-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:75672f0bc524ede266287d532d7923dbce94c7514ad07627bac3d0c6d92cc4d9", size = 119831, upload-time = "2026-05-10T18:16:09.174Z" }, - { url = "https://files.pythonhosted.org/packages/59/67/a6739ac96e28b7855808bdb0370e250606104a859750d209e5a0716fe7ab/librt-0.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f10cf143e4a9bb0f4f5af568a00df94a2d69ef41c2579584454bb0fe5cc642c", size = 103470, upload-time = "2026-05-10T18:16:10.369Z" }, - { url = "https://files.pythonhosted.org/packages/82/61/e59168d4d0bf2bf90f4f0caf7a001bfc60254c3af4586013b04dc3ef517b/librt-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:78dc31f7fdfe9c9d0eb0e8f42d139db230e826415bbcabd9f0e9faaaee909894", size = 144119, upload-time = "2026-05-10T18:16:11.771Z" }, - { url = "https://files.pythonhosted.org/packages/61/fd/caa1d60b12f7dd79ccea23054e06eeaebe266a5f52c40a6b651069200ce5/librt-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fa475675db22290c3158e1d42326d0f5a65f04f44a0e68c3630a25b53560fb9c", size = 143565, upload-time = "2026-05-10T18:16:13.334Z" }, - { url = "https://files.pythonhosted.org/packages/b8/a9/dc744f5c2b4978d48db970be29f22716d3413d28b14ad99740817315cf2c/librt-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:621db29691044bdeda22e789e482e1b0f3a985d90e3426c9c6d17606416205ea", size = 485395, upload-time = "2026-05-10T18:16:14.729Z" }, - { url = "https://files.pythonhosted.org/packages/8f/21/7f8e97a1e4dae952a5a95948f6f8507a173bc1e669f54340bba6ca1ca31b/librt-0.11.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a9010e2ed5b3a9e158c5fd966b3ab7e834bb3d3aacc8f66c91dd4b57a3799230", size = 479383, upload-time = "2026-05-10T18:16:16.321Z" }, - { url = "https://files.pythonhosted.org/packages/a6/6d/d8ee9c114bebf2c50e29ec2aa940826fccb62a645c3e4c18760987d0e16d/librt-0.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c39513d8b7477a2e1ed8c43fc21c524e8d5a0f8d4e8b7b074dbdbe7820a08e2", size = 513010, upload-time = "2026-05-10T18:16:17.647Z" }, - { url = "https://files.pythonhosted.org/packages/f0/43/0b5708af2bd30a46400e72ba6bdaa8f066f15fb9a688527e34220e8d6c06/librt-0.11.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7aef3cf1d5af86e770ab04bfd993dfc4ae8b8c17f66fb77dd4a7d50de7bbb1a3", size = 508433, upload-time = "2026-05-10T18:16:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/4a/50/356187247d09013490481033183b3532b58acf8028bcb34b2b56a375c9b2/librt-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:557183ddc36babe46b27dd60facbd5adb4492181a5be887587d57cda6e092f21", size = 522595, upload-time = "2026-05-10T18:16:20.642Z" }, - { url = "https://files.pythonhosted.org/packages/40/e7/c6ac4240899c7f3248079d5a9900debe0dadb3fdeaf856684c987105ba47/librt-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83d3e1f72bd42f6c5c0b7daec530c3f829bd02db42c70b8ddf0c2d90a2459930", size = 527255, upload-time = "2026-05-10T18:16:22.352Z" }, - { url = "https://files.pythonhosted.org/packages/eb/b5/a81322dbeedeeaf9c1ee6f001734d28a09d8383ac9e6779bc24bbd0743c6/librt-0.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4ce1f21fbe589bc1afd7872dece84fb0e1144f794a288e58a10d2c54a55c43be", size = 516847, upload-time = "2026-05-10T18:16:23.627Z" }, - { url = "https://files.pythonhosted.org/packages/ae/66/6e6323787d592b55204a42595ff1102da5115601b53a7e9ddebc889a6da5/librt-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b09f7044ea2b64c9da42fd3d335666518cfd1c6e8a182c95da73d0214b41e", size = 553920, upload-time = "2026-05-10T18:16:25.025Z" }, - { url = "https://files.pythonhosted.org/packages/9c/21/623f8ca230857102066d9ca8c6c1734995908c4d0d1bee7bb2ef0021cb33/librt-0.11.0-cp313-cp313-win32.whl", hash = "sha256:78fddc31cd4d3caa897ad5d31f856b1faadc9474021ad6cb182b9018793e254e", size = 101898, upload-time = "2026-05-10T18:16:26.649Z" }, - { url = "https://files.pythonhosted.org/packages/b3/1d/b4ebd44dd723f768469007515cb92251e0ae286c94c140f374801140fa74/librt-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ca8aa88751a775870b764e93bad5135385f563cb8dcee399abf034ea4d3cb47", size = 119812, upload-time = "2026-05-10T18:16:27.859Z" }, - { url = "https://files.pythonhosted.org/packages/3b/e4/b2f4ca7965ca373b491cdb4bc25cdb30c1649ca81a8782056a83850292a9/librt-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:96f044bb325fd9cf1a723015638c219e9143f0dfbc0ca54c565df2b7fc748b44", size = 103448, upload-time = "2026-05-10T18:16:29.066Z" }, - { url = "https://files.pythonhosted.org/packages/29/eb/dbce197da4e227779e56b5735f2decc3eb36e55a1cdbf1bd65d6639d76c1/librt-0.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4a017a95e5837dc15a8c5661d60e05daa96b90908b1aa6b7acdf443cd25c8ebd", size = 143345, upload-time = "2026-05-10T18:16:30.674Z" }, - { url = "https://files.pythonhosted.org/packages/76/a3/254bebd0c11c8ba684018efb8006ff22e466abce445215cca6c778e7d9de/librt-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b1ecbd9819deccc39b7542bf4d2a740d8a620694d39989e58661d3763458f8d4", size = 143131, upload-time = "2026-05-10T18:16:32.037Z" }, - { url = "https://files.pythonhosted.org/packages/f1/3f/f77d6122d21ac7bf6ae8a7dfced1bd2a7ac545d3273ebdcaf8042f6d619f/librt-0.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7da327dacd7be8f8ec36547373550744a3cc0e536d54665cd83f8bcd961200e8", size = 477024, upload-time = "2026-05-10T18:16:33.493Z" }, - { url = "https://files.pythonhosted.org/packages/ac/0a/2c996dadebaa7d9bbbd43ef2d4f3e66b6da545f838a41694ef6172cebec8/librt-0.11.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0dc56b1f8d06e60db362cc3fdae206681817f86ce4725d34511473487f12a34b", size = 474221, upload-time = "2026-05-10T18:16:34.864Z" }, - { url = "https://files.pythonhosted.org/packages/0a/7e/f5d92af8486b8272c23b3e686b46ff72d89c8169585eb61eef01a2ac7147/librt-0.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05fb8fb2ab90e21c8d12ea240d744ad514da9baf381ebfa70d91d20d21713175", size = 505174, upload-time = "2026-05-10T18:16:36.705Z" }, - { url = "https://files.pythonhosted.org/packages/af/1a/cb0734fe86398eb33193ab753b7326255c74cac5eb09e76b9b16536e7adb/librt-0.11.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cae74872be221df4374d10fec61f93ed1513b9546ea84f2c0bf73ab3e9bd0b03", size = 497216, upload-time = "2026-05-10T18:16:38.418Z" }, - { url = "https://files.pythonhosted.org/packages/18/06/094820f91558b66e29943c0ec41c9914f460f48dd51fc503c3101e10842d/librt-0.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32bcc918c0148eb7e3d57385125bac7e5f9e4359d05f07448b09f6f778c2f31c", size = 513921, upload-time = "2026-05-10T18:16:39.848Z" }, - { url = "https://files.pythonhosted.org/packages/0b/c2/00de9018871a282f530cacb457d5ec0428f6ac7e6fedde9aff7468d9fb04/librt-0.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f9743fc99135d5f78d2454435615f6dec0473ca507c26ce9d92b10b562a280d3", size = 520850, upload-time = "2026-05-10T18:16:41.471Z" }, - { url = "https://files.pythonhosted.org/packages/51/9d/64631832348fd1834fb3a61b996434edddaaf25a31d03b0a76273159d2cf/librt-0.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5ba067f4aadae8fda802d91d2124c90c42195ff32d9161d3549e6d05cfe26f96", size = 504237, upload-time = "2026-05-10T18:16:43.15Z" }, - { url = "https://files.pythonhosted.org/packages/a5/ec/ae5525eb16edc827a044e7bb8777a455ff95d4bca9379e7e6bddd7383647/librt-0.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:de3bf945454d032f9e390b85c4072e0a0570bf825421c8be0e71209fa65e1abe", size = 546261, upload-time = "2026-05-10T18:16:44.408Z" }, - { url = "https://files.pythonhosted.org/packages/5a/09/adce371f27ca039411da9659f7430fcc2ba6cd0c7b3e4467a0f091be7fa9/librt-0.11.0-cp314-cp314-win32.whl", hash = "sha256:d2277a05f6dcb9fd13db9566aac4fabd68c3ea1ea46ee5567d4eef8efa495a2f", size = 96965, upload-time = "2026-05-10T18:16:46.039Z" }, - { url = "https://files.pythonhosted.org/packages/d6/ee/8ac720d98548f173c7ce2e632a7ca94673f74cacd5c8162a84af5b35958a/librt-0.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:ab73e8db5e3f564d812c1f5c3a175930a5f9bc96ccb5e3b22a34d7858b401cf7", size = 115151, upload-time = "2026-05-10T18:16:47.133Z" }, - { url = "https://files.pythonhosted.org/packages/94/20/c900cf14efeb09b6bef2b2dff20779f73464b97fd58d1c6bccc379588ae3/librt-0.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:aea3caa317752e3a466fa8af45d91ee0ea8c7fdd96e42b0a8dd9b76a7931eba1", size = 98850, upload-time = "2026-05-10T18:16:48.597Z" }, - { url = "https://files.pythonhosted.org/packages/0c/71/944bfe4b64e12abffcd3c15e1cce07f72f3d55655083786285f4dedeb532/librt-0.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d1b36540d7aaf9b9101b3a6f376c8d8e9f7a9aec93ed05918f2c69d493ffef72", size = 151138, upload-time = "2026-05-10T18:16:49.839Z" }, - { url = "https://files.pythonhosted.org/packages/b6/10/99e64a5c86989357fda078c8143c533389585f6473b7439172dd8f3b3b2d/librt-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:efbb343ab2ce3540f4ecbe6315d677ed70f37cd9a72b1e58066c918ca83acbaa", size = 151976, upload-time = "2026-05-10T18:16:51.062Z" }, - { url = "https://files.pythonhosted.org/packages/21/31/5072ad880946d83e5ea4147d6d018c78eefce85b77819b19bdd0ee229435/librt-0.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0dd688aab3f7914d3e6e5e3554978e0383312fb8e771d84be008a35b9ee548", size = 557927, upload-time = "2026-05-10T18:16:52.632Z" }, - { url = "https://files.pythonhosted.org/packages/5e/8d/70b5fb7cfbab60edbe7381614ab985da58e144fbf465c86d44c95f43cdca/librt-0.11.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f5fb36b8c6c63fdcbb1d526d94c0d1331610d43f4118cc1beb4efef4f3faacb2", size = 539698, upload-time = "2026-05-10T18:16:53.934Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a3/ba3495a0b3edbd24a4cae0d1d3c64f39a9fc45d06e812101289b50c1a619/librt-0.11.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a9a237d13addb93715b6fee74023d5ee3469b53fce527626c0e088aa585805f", size = 577162, upload-time = "2026-05-10T18:16:55.589Z" }, - { url = "https://files.pythonhosted.org/packages/f7/db/36e25fb81f99937ff1b96612a1dc9fd66f039cb9cc3aee12c01fac31aab9/librt-0.11.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5ddd17bd87b2c56ddd60e546a7984a2e64c4e8eab92fb4cf3830a48ad5469d51", size = 566494, upload-time = "2026-05-10T18:16:56.975Z" }, - { url = "https://files.pythonhosted.org/packages/33/0d/3f622b47f0b013eeb9cf4cc07ae9bfe378d832a4eec998b2b209fe84244d/librt-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd43992b4473d42f12ff9e68326079f0696d9d4e6000e8f39a0238d482ba6ee2", size = 596858, upload-time = "2026-05-10T18:16:58.374Z" }, - { url = "https://files.pythonhosted.org/packages/a9/02/71b90bc93039c46a2000651f6ad60122b114c8f54c4ad306e0e96f5b75ad/librt-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f8e3e8056dd674e279741485e2e512d6e9a751c7455809d0114e6ebf8d781085", size = 590318, upload-time = "2026-05-10T18:16:59.676Z" }, - { url = "https://files.pythonhosted.org/packages/04/04/418cb3f75621e2b761fb1ab0f017f4d70a1a72a6e7c74ee4f7e8d198c2f3/librt-0.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c1f708d8ae9c56cf38a903c44297243d2ec83fd82b396b977e0144a3e76217e3", size = 575115, upload-time = "2026-05-10T18:17:01.007Z" }, - { url = "https://files.pythonhosted.org/packages/cc/2c/5a2183ac58dd911f26b5d7e7d7d8f1d87fcecdddd99d6c12169a258ff62c/librt-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0add982e0e7b9fc14cf4b33789d5f13f66581889b88c2f58099f6ce8f92617bd", size = 617918, upload-time = "2026-05-10T18:17:02.682Z" }, - { url = "https://files.pythonhosted.org/packages/15/1f/dc6771a52592a4451be6effa200cbfc9cec61e4393d3033d81a9d307961d/librt-0.11.0-cp314-cp314t-win32.whl", hash = "sha256:2b481d846ac894c4e8403c5fd0e87c5d11d6499e404b474602508a224ff531c8", size = 103562, upload-time = "2026-05-10T18:17:03.99Z" }, - { url = "https://files.pythonhosted.org/packages/62/4a/7d1415567027286a75ba1093ec4aca11f073e0f559c530cf3e0a757ad55c/librt-0.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:28edb433edde181112a908c78907af28f964eabc15f4dd16c9d66c834302677c", size = 124327, upload-time = "2026-05-10T18:17:05.465Z" }, - { url = "https://files.pythonhosted.org/packages/ce/62/b40b382fa0c66fee1478073eb8db352a4a6beda4a1adccf1df911d8c289c/librt-0.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dee008f20b542e3cd162ba338a7f9ec0f6d23d395f66fe8aeeec3c9d067ea253", size = 102572, upload-time = "2026-05-10T18:17:06.809Z" }, -] - -[[package]] -name = "mypy" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ast-serialize" }, - { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, - { name = "mypy-extensions" }, - { name = "pathspec" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/82/15/cca9d88503549ed6fedeaa1d448cdddd542ee8a490232d732e278036fbf2/mypy-2.1.0.tar.gz", hash = "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633", size = 3898359, upload-time = "2026-05-11T18:37:36.237Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/a1/639f3024794a2a15899cb90707fe02e044c4412794c39c5769fd3df2e2ef/mypy-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a683016b16fe2f572dc04c72be7ee0504ac1605a265d0200f5cea695fb788f41", size = 14691685, upload-time = "2026-05-11T18:33:27.973Z" }, - { url = "https://files.pythonhosted.org/packages/3b/08/9a585dea4325f20d8b80dc78623fa50d1fd2173b710f6237afd6ba6ab39b/mypy-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a293c534adb55271fef24a26da04b855540a8c13cc07bc5917b9fd2c394f2ca", size = 13555165, upload-time = "2026-05-11T18:32:16.107Z" }, - { url = "https://files.pythonhosted.org/packages/81/dc/7c42cc9c6cb01e8eb09961f1f738741d3e9c7e9d5c5b30ec69222625cd5f/mypy-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7406f4d048e71e576f5356d317e5b0a9e666dfd966bd99f9d14ca06e1a341538", size = 13994376, upload-time = "2026-05-11T18:32:39.256Z" }, - { url = "https://files.pythonhosted.org/packages/d4/fa/285946c33bce716e082c11dfeee9ee196eaf1f5042efb3581a31f9f205e4/mypy-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e0210d626fc8b31ccc90233754c7bc90e1f43205e85d96387f7db1285b55c398", size = 14864618, upload-time = "2026-05-11T18:34:49.765Z" }, - { url = "https://files.pythonhosted.org/packages/2b/83/82397f48af6c27e295d57979ded8490c9829040152cf7571b2f026aeb9a0/mypy-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3712c20deed54e814eaaa825603bada8ea1c390670a397c95b98405347acc563", size = 15102063, upload-time = "2026-05-11T18:34:05.855Z" }, - { url = "https://files.pythonhosted.org/packages/40/68/b02dec39057b88eb03dc0aa854732e26e8361f34f9d0e20c7614967d1eba/mypy-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fcaa0e479066e31f7cceb6a3bea39cb22b2ff51a6b2f24f193d19179ba17c389", size = 11060564, upload-time = "2026-05-11T18:35:36.494Z" }, - { url = "https://files.pythonhosted.org/packages/cf/a8/ea3dcbef31f99b634f2ee23bb0321cbc8c1b388b76a861eb849f13c347dc/mypy-2.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:0b1a5260c95aa443083f9ed3592662941951bca3d4ca224a5dc517c38b7cf666", size = 9966983, upload-time = "2026-05-11T18:37:14.139Z" }, - { url = "https://files.pythonhosted.org/packages/95/b1/55861beb5c339b44f9a2ba92df9e2cb1eeb4ae1eee674cdf7772c797778b/mypy-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:244358bf1c0da7722230bce60683d52e8e9fd030554926f15b747a84efb5b3af", size = 14874381, upload-time = "2026-05-11T18:37:31.784Z" }, - { url = "https://files.pythonhosted.org/packages/0b/b3/b7f770114b7d0ac92d0f76e8d93c2780844a70488a90e91821927850da86/mypy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ec7c57657493c7a75534df2751c8ae2cda383c16ecc55d2106c54476b1b16f6", size = 13665501, upload-time = "2026-05-11T18:34:23.063Z" }, - { url = "https://files.pythonhosted.org/packages/b6/f3/8ae2037967e2126689a0c11d99e2b707134a565191e92c60ca2572aec60a/mypy-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8161b6ff4392410023224f0969d17db93e1e154bc3e4ba62598e720723ae211", size = 14045750, upload-time = "2026-05-11T18:31:48.151Z" }, - { url = "https://files.pythonhosted.org/packages/a0/32/615eb5911859e43d054941b0d0a7d06cfa2870eba86529cf385b052b111c/mypy-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf03e12003084a67395184d3eb8cbd6a489dc3655b5664b28c210a9e2403ab0b", size = 15061630, upload-time = "2026-05-11T18:37:06.898Z" }, - { url = "https://files.pythonhosted.org/packages/d4/03/4eafbfff8bfab1b87082741eae6e6a624028c984e6708b73bce2a8570c9d/mypy-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:20509760fd791c51579d573153407d226385ec1f8bcce55d730b354f3336bc22", size = 15288831, upload-time = "2026-05-11T18:31:18.07Z" }, - { url = "https://files.pythonhosted.org/packages/99/ee/919661478e5891a3c96e549c036e467e64563ab85995b10c53c8358e16a3/mypy-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:6753d0c1fdd6b1a23b9e4f283ce80b2153b724adcb2653b20b85a8a28ac6436b", size = 11135228, upload-time = "2026-05-11T18:34:31.23Z" }, - { url = "https://files.pythonhosted.org/packages/24/0a/6a12b9782ca0831a553192f351679f4548abc9d19a7cc93bb7feb02084c7/mypy-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:98ebb6589bb3b6d0c6f0c459d53ca55b8091fbc13d277c4041c885392e8195e8", size = 10040684, upload-time = "2026-05-11T18:36:48.199Z" }, - { url = "https://files.pythonhosted.org/packages/6e/dd/c7191469c777f07689c032a8f7326e393ea34c92d6d76eb7ce5ba57ea66d/mypy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35aac3bb114e03888f535d5eb51b8bafbb3266586b599da1940f9b1be3ec5bd5", size = 14852174, upload-time = "2026-05-11T18:31:38.929Z" }, - { url = "https://files.pythonhosted.org/packages/55/8c/aed55408879043d72bb9135f4d0d19a02b886dd569631e113e3d2706cb8d/mypy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8de55a8c861f2a49331f807be98d90caeceeef520bde13d43a160207f8af613e", size = 13651542, upload-time = "2026-05-11T18:36:04.636Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8e/f371a824b1f1fa8ea6e3dbb8703d232977d572be2329554a3bc4d960302f/mypy-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fdf2941a07434af755837d9880f7d7d25f1dacb1af9dcd4b9b66f2220a3024e", size = 14033929, upload-time = "2026-05-11T18:35:55.742Z" }, - { url = "https://files.pythonhosted.org/packages/94/21/f54be870d6dd53a82c674407e0f8eed7174b05ec78d42e5abd7b42e84fd5/mypy-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e195b817c13f02352a9c124301f9f30f078405444679b6753c1b96b6eed37285", size = 15039200, upload-time = "2026-05-11T18:33:10.281Z" }, - { url = "https://files.pythonhosted.org/packages/17/99/bf21748626a40ce59fd29a39386ab46afec88b7bd2f0fa6c3a97c995523f/mypy-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5431d42af987ebd92ba2f71d45c85ed41d8e6ca9f5fd209a69f68f707d2469e5", size = 15272690, upload-time = "2026-05-11T18:32:07.205Z" }, - { url = "https://files.pythonhosted.org/packages/d6/d7/9e90d2cf47100bea550ed2bc7b0d4de3a62181d84d5e37da0003e8462637/mypy-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:767fe8c66dc3e01e19e1737d4c38ebefead16125e1b8e58ad421903b376f5c65", size = 11147435, upload-time = "2026-05-11T18:33:56.477Z" }, - { url = "https://files.pythonhosted.org/packages/ec/46/e5c449e858798e35ffc90946282a27c62a77be743fe17480e4977374eb91/mypy-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:ecfe70d43775ab99562ab128ce49854a362044c9f894961f68f898c23cb7429d", size = 10035052, upload-time = "2026-05-11T18:32:30.049Z" }, - { url = "https://files.pythonhosted.org/packages/b0/ca/b279a672e874aedd5498ae25f722dacc8aa86bbffb939b3f97cbb1cf6686/mypy-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7354c5a7f69d9345c3d6e69921d57088eea3ddeeb6b20d34c1b3855b02c36ec2", size = 14848422, upload-time = "2026-05-11T18:35:45.984Z" }, - { url = "https://files.pythonhosted.org/packages/27/e6/3efe56c631d959b9b4454e208b0ac4b7f4f58b404c89f8bec7b49efdfc21/mypy-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:49890d4f76ac9e06ec117f9e09f3174da70a620a0c300953d8595c926e80947f", size = 13677374, upload-time = "2026-05-11T18:36:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/84/7f/8107ea87a44fd1f1b59882442f033c9c3488c127201b1d1d15f1cbd6022e/mypy-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:761be68e023ef5d94678772396a8af1220030f80837a3afd8d0aef3b419666f4", size = 14055743, upload-time = "2026-05-11T18:35:18.361Z" }, - { url = "https://files.pythonhosted.org/packages/51/4d/b6d34db183133b83761b9199a82d31557cdbb70a380d8c3b3438e11882a3/mypy-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c90345fc182dc363b891350457ec69c35140858538f38b4540845afcc32b1aef", size = 15020937, upload-time = "2026-05-11T18:34:59.618Z" }, - { url = "https://files.pythonhosted.org/packages/ff/d7/f08360c691d758acb02f45022c34d98b92892f4ea756644e1000d4b9f3d8/mypy-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b84802e7b5a6daf1f5e15bc9fcd7ddae77be13981ffab037f1c67bb84d67d135", size = 15253371, upload-time = "2026-05-11T18:36:41.081Z" }, - { url = "https://files.pythonhosted.org/packages/67/1b/09460a13719530a19bce27bd3bc8449e83569dd2ba7faf51c9c3c30c0b61/mypy-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:022c771234936ceac541ebaf836fe9e2abeb3f5e09aff21588fe543ff006fe21", size = 11326429, upload-time = "2026-05-11T18:34:13.526Z" }, - { url = "https://files.pythonhosted.org/packages/40/62/75dbf0f82f7b6680340efc614af29dd0b3c17b8a4f1cd09b8bd2fd6bc814/mypy-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:498207db725cec88829a6a5c2fc771205fd043719ef98bc49aba8fb9fc4e6d57", size = 10218799, upload-time = "2026-05-11T18:32:23.491Z" }, - { url = "https://files.pythonhosted.org/packages/b2/66/caca04ed7d972fb6eb6dd1ccd6df1de5c38fae8c5b3dc1c4e8e0d85ee6b9/mypy-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d5e5cad0efeba72b93cd17490cc0d69c5ac9ca132994fe3fb0314808aeeb83e", size = 15923458, upload-time = "2026-05-11T18:35:28.64Z" }, - { url = "https://files.pythonhosted.org/packages/ed/52/2d90cbe49d014b13ed7ff337930c30bad35893fe38a1e4641e756bb62191/mypy-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ff715050c127d724fd260a2e666e7747fdd83511c0c47d449d98238970aef780", size = 14757697, upload-time = "2026-05-11T18:36:14.208Z" }, - { url = "https://files.pythonhosted.org/packages/ac/37/d98f4a14e081b238992d0ed96b6d39c7cc0148c9699eb71eaa68629665ea/mypy-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82208da9e09414d520e912d3e462d454854bed0810b71540bb016dcbca7308fd", size = 15405638, upload-time = "2026-05-11T18:33:48.249Z" }, - { url = "https://files.pythonhosted.org/packages/a3/c2/15c46613b24a84fad2aea1248bf9619b99c2767ae9071fe224c179a0b7d4/mypy-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e79ebc1b904b84f0310dff7469655a9c36c7a68bddb37bdd42b67a332df61d08", size = 16215852, upload-time = "2026-05-11T18:32:50.296Z" }, - { url = "https://files.pythonhosted.org/packages/5c/90/9c16a57f482c76d25f6379762b56bbf65c711d8158cf271fb2802cfb0640/mypy-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e583edc957cfb0deb142079162ae826f58449b116c1d442f2d91c69d9fced081", size = 16452695, upload-time = "2026-05-11T18:33:38.182Z" }, - { url = "https://files.pythonhosted.org/packages/0f/4c/215a4eeb63cacc5f17f516691ea7285d11e249802b942476bff15922a314/mypy-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b33b6cd332695bba180d55e717a79d3038e479a2c49cc5eb3d53603409b9a5d7", size = 12866622, upload-time = "2026-05-11T18:34:39.945Z" }, - { url = "https://files.pythonhosted.org/packages/4b/50/1043e1db5f455ffe4c9ab22747cd8ca2bc492b1e4f4e21b130a44ee2b217/mypy-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:4f910fe825376a7b66ef7ca8c98e5a149e8cd64c19ae71d84047a74ee060d4e6", size = 10610798, upload-time = "2026-05-11T18:36:31.444Z" }, - { url = "https://files.pythonhosted.org/packages/0d/2a/13ca1f292f6db1b98ff495ef3467736b331621c5917cad984b7043e7348d/mypy-2.1.0-py3-none-any.whl", hash = "sha256:a663814603a5c563fb87a4f96fb473eeb30d1f5a4885afcf44f9db000a366289", size = 2693302, upload-time = "2026-05-11T18:31:29.246Z" }, -] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, -] - -[[package]] -name = "packaging" -version = "26.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, -] - -[[package]] -name = "pathspec" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "pygments" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, -] - -[[package]] -name = "pytest" -version = "9.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/84/0e/b5858858d74958632c49b72cb25a3976ff9f632397626715be71c89d3971/pytest-9.1.0.tar.gz", hash = "sha256:41dd9148c08072446394cefd3d79701701335a9f4cae69ba92e39f6c7f5c061c", size = 1634181, upload-time = "2026-06-13T18:52:45.983Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/5a/ba30a81239b909821b3153e303e7def45178bf353da4f72380e6c5e8793b/pytest-9.1.0-py3-none-any.whl", hash = "sha256:8ebb0e7888bdf2bdfc602ec51f8f62d50200af37356c74e503c79a94f5c81f32", size = 386453, upload-time = "2026-06-13T18:52:44.045Z" }, -] - -[[package]] -name = "python-skills-maintainer" -version = "10.0.0" -source = { virtual = "." } - -[package.dev-dependencies] -dev = [ - { name = "mypy" }, - { name = "pytest" }, - { name = "pyyaml" }, - { name = "ruff" }, - { name = "types-pyyaml" }, -] - -[package.metadata] - -[package.metadata.requires-dev] -dev = [ - { name = "mypy", specifier = ">=1.20.1" }, - { name = "pytest", specifier = ">=9.0.3" }, - { name = "pyyaml", specifier = ">=6.0.2,<7" }, - { name = "ruff", specifier = ">=0.14.0" }, - { name = "types-pyyaml", specifier = ">=6.0.12.20250915" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, - { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, - { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, - { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, - { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, - { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, - { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, - { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, - { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "ruff" -version = "0.15.17" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/a9/3abdf488f1bf3d24c699415e454ed554a6350d5d89ce183be1ee0a3361ac/ruff-0.15.17.tar.gz", hash = "sha256:2ec446937fd16c8c4de2674a209cc5af64d9c6f17d21fbf1151054fa0bcf5219", size = 4743346, upload-time = "2026-06-11T17:54:47.663Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/4d/e11259f5da07cb6afb2d074c31bf09da9671993f7329d4f15d2fdc458301/ruff-0.15.17-py3-none-linux_armv6l.whl", hash = "sha256:d9feddb927fc68bd295f5eebc587a7e42cfaf9b65f60ca4a2386febff575da8f", size = 10856677, upload-time = "2026-06-11T17:54:49.533Z" }, - { url = "https://files.pythonhosted.org/packages/29/3e/772d679e1a0dc058e58875bd2c0cb713a0530877b4a76fee3c7966df0d49/ruff-0.15.17-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:25805a226d741c47d274a35ad5c10a7dde175fcddfa511d7cf3da0a21eb3eab7", size = 11223443, upload-time = "2026-06-11T17:55:00.573Z" }, - { url = "https://files.pythonhosted.org/packages/68/58/bd41f7688b2fd5623012605130ed70e60aa7f2244baa3d5066bdd61530c8/ruff-0.15.17-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f6ad73b14c2d18a3bf8ad7cb6974294d7f613a7898604826058e6ac64918ef4d", size = 10566458, upload-time = "2026-06-11T17:55:07.52Z" }, - { url = "https://files.pythonhosted.org/packages/d8/5b/733371013fcf1ec339e477ece6ab42bfe10bdd9bba8ee88a9516aa56bfc0/ruff-0.15.17-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ba0c1e4f95bcb3869d0d30cbd5917071ef2e28665abfec970cdab0492c713ed", size = 10914483, upload-time = "2026-06-11T17:55:05.501Z" }, - { url = "https://files.pythonhosted.org/packages/bd/cc/6f24251cc0252f7239391ccb85833f320efad14ebe5b443943f37ced6332/ruff-0.15.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:81647960f10bff57d2e51cadd0c3950fe598400c852863a038720ef5b8cca91e", size = 10647497, upload-time = "2026-06-11T17:54:57.733Z" }, - { url = "https://files.pythonhosted.org/packages/68/dd/0d10c17ce1a1624d6fc3156309c3f834fdb5dfaad026ec90c85684f3990e/ruff-0.15.17-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e01a84ddbc8c16c23055ba3924476850f1bbc1917cebbb9376665a63e74260d", size = 11416967, upload-time = "2026-06-11T17:54:51.461Z" }, - { url = "https://files.pythonhosted.org/packages/2f/91/556bfb156f6144f355e831c23db00b2fc4120f86b3ce81cc5f7fd2df51f3/ruff-0.15.17-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fe9f653152f8f294f9f7e03bf3a453d8b4a27f7a59c78c8666167f2b17b96c", size = 12335770, upload-time = "2026-06-11T17:54:45.793Z" }, - { url = "https://files.pythonhosted.org/packages/88/82/8b5999aa13355e926f06d9f42a32dcca862f623bf0363785ff89d607dffd/ruff-0.15.17-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c0fe88a7676e7a05b73174d4d4a59cb2ac21ff8263583f87a81a6018475a978", size = 11575441, upload-time = "2026-06-11T17:54:32.661Z" }, - { url = "https://files.pythonhosted.org/packages/11/93/f10377bb04109ca0e8cbc483ff1982c54b6d418210041776f93e8cdc7fa9/ruff-0.15.17-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecfc3c7878fff94633ab0348524e093f9ce3243080416dd7d14f8ba400174719", size = 11557614, upload-time = "2026-06-11T17:54:34.698Z" }, - { url = "https://files.pythonhosted.org/packages/c7/a6/eeeae7f7d5493df41649ab3db92f086b2d0a30199e4efdf8e3dd7a033f24/ruff-0.15.17-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:b8461180b22420b1bdc289909410930761629fddf2a5aaf60fae1ab26cedc4c4", size = 11544450, upload-time = "2026-06-11T17:54:39.042Z" }, - { url = "https://files.pythonhosted.org/packages/32/88/5991ce565129a24dd4a00db1254b3b5db2e53018cbe4018ea5a89738e727/ruff-0.15.17-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6eccbe50a038b503e7140b441aa9c7fc8c1f36edf23ebef9f4165c2f28f568b7", size = 10892524, upload-time = "2026-06-11T17:55:09.432Z" }, - { url = "https://files.pythonhosted.org/packages/f5/1d/0fdd248313425f55223968af04b0a42125466a8d88d21c1d99c6af0a51e8/ruff-0.15.17-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:382fc0521025f5a8ad447d8bdd523545d0d7646adb718eb1c2dac5065ec27c0f", size = 10659573, upload-time = "2026-06-11T17:54:36.824Z" }, - { url = "https://files.pythonhosted.org/packages/9e/0e/072e8260deb9461062ce9311ced27a8e541229a6ffd483013dd37661e43e/ruff-0.15.17-py3-none-musllinux_1_2_i686.whl", hash = "sha256:456d41fcd1b2777ad63f09a6e7121d43f7b688bbc76a800c10f7f8fb1f912c3f", size = 11127818, upload-time = "2026-06-11T17:55:03.124Z" }, - { url = "https://files.pythonhosted.org/packages/ab/b4/55060a34163121498014696b5f656db5b8c6963768f227dbf0d76b311073/ruff-0.15.17-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b1a04bcc94ae6194e9db05d16ad31f298a7194bfbcb08258bbe589cee1d587b8", size = 11655901, upload-time = "2026-06-11T17:54:53.562Z" }, - { url = "https://files.pythonhosted.org/packages/49/71/9b29d6b87cef468d697f43c6a91e3fae4a80185779d7d5a4ef27d173439f/ruff-0.15.17-py3-none-win32.whl", hash = "sha256:596065960ab1ff593f744220c9fe6580eda00a95003cffa9f4048bb5b1bf0392", size = 10925574, upload-time = "2026-06-11T17:54:55.723Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b2/8fc77f3723228836fa5d12497eb71c808f83782e10d058d2b15cfa14640b/ruff-0.15.17-py3-none-win_amd64.whl", hash = "sha256:6769e5fa1710b179b92e0bfa5a51735b35baea9013dadb06d5f44cbcf9547084", size = 12058788, upload-time = "2026-06-11T17:54:41.042Z" }, - { url = "https://files.pythonhosted.org/packages/2d/c7/c53e8dbff9c9dc4b7928773421ae294a5d28fcb8dcda1a089579d3a7e510/ruff-0.15.17-py3-none-win_arm64.whl", hash = "sha256:f3be1fbb34bcdfd146240d8fb92a709d4c2c8191348580a3c044ec60fa0b4456", size = 11355275, upload-time = "2026-06-11T17:54:43.635Z" }, -] - -[[package]] -name = "types-pyyaml" -version = "6.0.12.20260518" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/83/4a1afc3fbfcf5b8d46fc390cd95ed6b0dc9010a265f4e9f46314efffa37a/types_pyyaml-6.0.12.20260518.tar.gz", hash = "sha256:d917f83fb38462550338c1297faedd860b3ec83912b96b1e3d73255f7473e466", size = 17850, upload-time = "2026-05-18T06:01:58.675Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/06/a2/c01db32be2ae7d6a1689972f3c492b149ee4e164b12fdfd9f64b50888215/types_pyyaml-6.0.12.20260518-py3-none-any.whl", hash = "sha256:d2150f75a231c9fe9c7463bd29487d93e60bac90400287351384bc2284eba7cd", size = 20312, upload-time = "2026-05-18T06:01:57.368Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] diff --git a/plugins/repository-skills/skills/maintain-project-api/SKILL.md b/plugins/repository-skills/skills/maintain-project-api/SKILL.md deleted file mode 100644 index 1c55196c5..000000000 --- a/plugins/repository-skills/skills/maintain-project-api/SKILL.md +++ /dev/null @@ -1,89 +0,0 @@ ---- -name: maintain-project-api -description: Maintain canonical API.md files with deterministic audit and bounded apply modes. Use for API surface, authentication, schemas, errors, versioning, compatibility, verification, support guidance, normalization, or targeted fixes. ---- - -# Maintain Project API - -Maintain canonical `API.md` files through one deterministic API-reference workflow. - -This skill is the default baseline path for `API.md` maintenance across most repositories. Reach for a narrower plugin only when the target repo has a specialized shape that deserves its own maintainer contract, such as a skills-export or plugin-export repository. - -## Inputs - -- Required: `--project-root ` -- Required: `--run-mode ` -- Optional: `--api-path ` -- Optional: `--config ` - -## Workflow - -1. Validate the project root and resolve the target `API.md`. -2. Load the canonical API-reference schema from `config/api-customization.template.yaml`, then merge any explicit override or project-local customization file. -3. In `check-only`, audit the required section schema, required subsection schema, required table of contents, placeholder content, and verification-command formatting. -4. In `apply`, keep edits bounded to the target `API.md` while creating a missing file from the bundled template and normalizing the document to the canonical structure. -5. Re-run the same audit to confirm post-fix status. - -## Canonical Base Contract - -The source of truth for the base API-reference contract lives in: - -- `config/api-customization.template.yaml` -- `assets/API.template.md` - -The base contract requires: - -- a top-level title and short summary -- a required `## Table of Contents` -- canonical top-level sections for overview, API surface, authentication and access, requests and responses, errors, versioning and compatibility, local development and verification, and support ownership -- required subsection structure for the sections that need stable consumer-facing detail - -## Writing Expectations - -- `Overview > Who This API Is For` should name the real consumers: humans, services, tools, packages, apps, or maintainers. -- `Overview > Stability Status` should say whether the API is stable, experimental, internal, deprecated, or otherwise constrained. -- `API Surface` should list concrete entry points and the protocol, transport, runtime, package interface, or file format used to reach each one. -- `Authentication and Access` should describe credentials and permissions without inventing secrets, scopes, roles, or access paths. -- `Requests and Responses` should document grounded request inputs, response outputs, and data models. -- `Errors` should make failures actionable by documenting the error shape and common failure modes with likely causes or next checks. -- `Versioning and Compatibility` should make the supported compatibility window and breaking-change handling explicit. -- `Local Development and Verification > Verification` should prefer fenced commands or reproducible call examples with language info strings. -- `Support and Ownership` should name only grounded teams, maintainers, issue trackers, services, or escalation paths. - -## Codex Subagent Fit - -When delegation is explicitly requested or authorized, follow `agent-engineering-skills:orchestrate-agent-work`. This skill is a good fit for read-heavy API evidence gathering before the main workflow edits or reports: inventorying endpoints or public symbols, checking examples against tests, reading schema definitions, or comparing compatibility claims against release notes. - -Keep `apply` edits in the main thread because this skill owns one target `API.md` file and must avoid invented endpoints, schemas, credentials, or support promises. Ask workers for concise evidence and file references instead of replacement API prose. - -## Output Contract - -- Return Markdown plus JSON with: - - `run_context` - - `schema_contract` - - `schema_violations` - - `command_integrity_issues` - - `content_quality_issues` - - `fixes_applied` - - `post_fix_status` - - `errors` -- If there are no issues and no errors, output exactly `No findings.` - -## Guardrails - -- Never auto-commit, auto-push, or open a PR. -- Never invent commands, endpoints, protocols, schemas, environment variables, credentials, permissions, version guarantees, support paths, or compatibility promises that are not grounded in the repo. -- Never edit files other than the target `API.md`. -- Keep `API.md` as the canonical API-reference filename for this skill. -- Treat this skill as a hard-enforced base template. Downstream plugins may specialize the schema, but the base skill should not do repo-profile inference. - -## References - -- `agents/openai.yaml` -- `references/section-schema.md` -- `references/output-contract.md` -- `references/fix-policies.md` -- `references/style-rules.md` -- `references/api-customization.md` -- `references/api-config-schema.md` -- `references/project-api-maintenance-automation-prompts.md` diff --git a/plugins/repository-skills/skills/maintain-project-api/agents/openai.yaml b/plugins/repository-skills/skills/maintain-project-api/agents/openai.yaml deleted file mode 100644 index 11fc52cf0..000000000 --- a/plugins/repository-skills/skills/maintain-project-api/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Maintain Project API" - short_description: "Audit and apply bounded fixes to canonical API.md files." - default_prompt: "Use $maintain-project-api for an ordinary software-project API.md. Load the template-backed API-reference schema first, enforce the required table of contents and canonical section structure, return audit findings in check-only mode, and in apply mode keep edits bounded to grounded API surface, request, response, error, versioning, verification, and support guidance only." diff --git a/plugins/repository-skills/skills/maintain-project-api/assets/API.template.md b/plugins/repository-skills/skills/maintain-project-api/assets/API.template.md deleted file mode 100644 index dca843001..000000000 --- a/plugins/repository-skills/skills/maintain-project-api/assets/API.template.md +++ /dev/null @@ -1,83 +0,0 @@ -# API Reference for {{PROJECT_NAME}} - -Use this reference to understand the public API surface, how to call it, what it returns, and how to verify behavior locally. - -## Table of Contents - -## Overview - -### Who This API Is For - -Explain which humans, services, tools, packages, or apps are expected to consume this API. - -### Stability Status - -State whether the API is stable, experimental, internal, deprecated, or otherwise constrained. - -## API Surface - -### Entry Points - -List the public routes, commands, functions, resources, files, modules, or other callable entry points that belong in this API contract. - -### Protocols and Transports - -Document the concrete protocol, transport, runtime, package interface, or file format used by each entry point. - -## Authentication and Access - -### Credentials - -Document the tokens, secrets, sessions, certificates, local permissions, or unauthenticated access model needed to call the API. - -### Permissions - -Document the scopes, roles, local capabilities, repository permissions, or service privileges required for successful calls. - -## Requests and Responses - -### Request Shape - -Describe the required request fields, parameters, headers, body structure, input files, command arguments, or function inputs. - -### Response Shape - -Describe the returned fields, status values, output files, generated artifacts, events, or function return values. - -### Data Models - -Define the important payload objects, records, enums, resource identifiers, schemas, and persistence-backed structures that API consumers need to understand. - -## Errors - -### Error Shape - -Document the structured error format, exit-code behavior, thrown error types, HTTP status mapping, log surface, or diagnostic output API consumers will see. - -### Common Failure Modes - -Call out the common, actionable failure modes and the most likely cause or next diagnostic step for each one. - -## Versioning and Compatibility - -### Supported Versions - -State the supported API versions, package versions, protocol versions, endpoint revisions, or documented compatibility window. - -### Breaking Changes - -Explain how breaking changes are announced, documented, migrated, or intentionally not supported. - -## Local Development and Verification - -### Runtime Configuration - -Document the concrete local config files, environment variables, feature flags, local services, fixtures, or seed data needed to exercise the API. - -### Verification - -Prefer grounded commands or reproducible call examples with fenced code blocks and language info strings when examples help API consumers verify behavior. - -## Support and Ownership - -Name the team, maintainer, service, issue tracker, or documented escalation path that API consumers should use when the API contract is unclear or broken. diff --git a/plugins/repository-skills/skills/maintain-project-api/config/api-customization.template.yaml b/plugins/repository-skills/skills/maintain-project-api/config/api-customization.template.yaml deleted file mode 100644 index f12b5c397..000000000 --- a/plugins/repository-skills/skills/maintain-project-api/config/api-customization.template.yaml +++ /dev/null @@ -1,154 +0,0 @@ -schemaVersion: 1 -isCustomized: false -profile: base -settings: - preservePreamble: true - allowAdditionalSections: true - requiredSections: - - Overview - - API Surface - - Authentication and Access - - Requests and Responses - - Errors - - Versioning and Compatibility - - Local Development and Verification - - Support and Ownership - sectionOrder: - - Overview - - API Surface - - Authentication and Access - - Requests and Responses - - Errors - - Versioning and Compatibility - - Local Development and Verification - - Support and Ownership - requiredSubsections: - Overview: - - Who This API Is For - - Stability Status - API Surface: - - Entry Points - - Protocols and Transports - Authentication and Access: - - Credentials - - Permissions - Requests and Responses: - - Request Shape - - Response Shape - - Data Models - Errors: - - Error Shape - - Common Failure Modes - Versioning and Compatibility: - - Supported Versions - - Breaking Changes - Local Development and Verification: - - Runtime Configuration - - Verification - sectionAliases: - API Surface: - - Endpoints - - Public API - - Interface - Authentication and Access: - - Authentication - - Authorization - - Access - Requests and Responses: - - Payloads - - Schemas - - Request and Response Shapes - Errors: - - Error Handling - Versioning and Compatibility: - - Compatibility - - Versioning - Local Development and Verification: - - Local Setup - - Development - - Verification - Support and Ownership: - - Support - - Ownership - subsectionAliases: - Overview/Who This API Is For: - - Audience - - Consumers - Overview/Stability Status: - - Stability - - Status - API Surface/Entry Points: - - Routes - - Endpoints - API Surface/Protocols and Transports: - - Protocols - - Transports - Authentication and Access/Credentials: - - Authentication - - Secrets - Authentication and Access/Permissions: - - Authorization - - Scopes - Requests and Responses/Request Shape: - - Requests - - Request Format - Requests and Responses/Response Shape: - - Responses - - Response Format - Requests and Responses/Data Models: - - Schemas - - Models - Errors/Error Shape: - - Error Format - Errors/Common Failure Modes: - - Failure Modes - - Common Errors - Versioning and Compatibility/Supported Versions: - - Versions - - Supported API Versions - Versioning and Compatibility/Breaking Changes: - - Compatibility Notes - - Migration Notes - Local Development and Verification/Runtime Configuration: - - Runtime Config - - Configuration - Local Development and Verification/Verification: - - Validation - sectionTemplates: - Support and Ownership: | - Name the team, maintainer, service, issue tracker, or documented escalation path that API consumers should use when the API contract is unclear or broken. - subsectionTemplates: - Overview/Who This API Is For: | - Explain which humans, services, tools, packages, or apps are expected to consume this API. - Overview/Stability Status: | - State whether the API is stable, experimental, internal, deprecated, or otherwise constrained. - API Surface/Entry Points: | - List the public routes, commands, functions, resources, files, modules, or other callable entry points that belong in this API contract. - API Surface/Protocols and Transports: | - Document the concrete protocol, transport, runtime, package interface, or file format used by each entry point. - Authentication and Access/Credentials: | - Document the tokens, secrets, sessions, certificates, local permissions, or unauthenticated access model needed to call the API. - Authentication and Access/Permissions: | - Document the scopes, roles, local capabilities, repository permissions, or service privileges required for successful calls. - Requests and Responses/Request Shape: | - Describe the required request fields, parameters, headers, body structure, input files, command arguments, or function inputs. - Requests and Responses/Response Shape: | - Describe the returned fields, status values, output files, generated artifacts, events, or function return values. - Requests and Responses/Data Models: | - Define the important payload objects, records, enums, resource identifiers, schemas, and persistence-backed structures that API consumers need to understand. - Errors/Error Shape: | - Document the structured error format, exit-code behavior, thrown error types, HTTP status mapping, log surface, or diagnostic output API consumers will see. - Errors/Common Failure Modes: | - Call out the common, actionable failure modes and the most likely cause or next diagnostic step for each one. - Versioning and Compatibility/Supported Versions: | - State the supported API versions, package versions, protocol versions, endpoint revisions, or documented compatibility window. - Versioning and Compatibility/Breaking Changes: | - Explain how breaking changes are announced, documented, migrated, or intentionally not supported. - Local Development and Verification/Runtime Configuration: | - Document the concrete local config files, environment variables, feature flags, local services, fixtures, or seed data needed to exercise the API. - Local Development and Verification/Verification: | - ```bash - # Replace this with the grounded validation commands for the project. - ``` - - Prefer grounded commands or reproducible call examples with fenced code blocks and language info strings when examples help API consumers verify behavior. diff --git a/plugins/repository-skills/skills/maintain-project-api/references/api-config-schema.md b/plugins/repository-skills/skills/maintain-project-api/references/api-config-schema.md deleted file mode 100644 index d6b2b3d2b..000000000 --- a/plugins/repository-skills/skills/maintain-project-api/references/api-config-schema.md +++ /dev/null @@ -1,28 +0,0 @@ -# API Config Schema - -The API-reference configuration file is YAML and uses this top-level shape: - -- `schemaVersion` -- `isCustomized` -- `profile` -- `settings` - -The `settings` map supports: - -- `preservePreamble` -- `allowAdditionalSections` -- `requiredSections` -- `sectionOrder` -- `requiredSubsections` -- `sectionAliases` -- `subsectionAliases` -- `sectionTemplates` -- `subsectionTemplates` - -Practical rules: - -- `requiredSections` defines the canonical top-level sections, excluding the title, summary, and `Table of Contents`. -- `sectionOrder` defines the enforced top-level ordering. -- `requiredSubsections` defines required `###` headings under specific `##` sections. -- `sectionAliases` and `subsectionAliases` allow bounded migration from older heading names into canonical names. -- `sectionTemplates` and `subsectionTemplates` provide the base scaffolding used during apply mode when content is missing. diff --git a/plugins/repository-skills/skills/maintain-project-api/references/api-customization.md b/plugins/repository-skills/skills/maintain-project-api/references/api-customization.md deleted file mode 100644 index 30a2345d6..000000000 --- a/plugins/repository-skills/skills/maintain-project-api/references/api-customization.md +++ /dev/null @@ -1,19 +0,0 @@ -# API Customization - -The base API-reference contract is defined in `../config/api-customization.template.yaml`. - -Downstream specializations may customize: - -- canonical section order -- required sections -- required subsection structure -- section and subsection aliases -- section and subsection scaffold text - -The base skill always requires: - -- a top-level title and short summary -- a `Table of Contents` -- deterministic normalization to the configured schema - -Use a project-local `config/api-customization.yaml` or `--config ` override when a downstream plugin needs a narrower or expanded API reference structure. diff --git a/plugins/repository-skills/skills/maintain-project-api/references/fix-policies.md b/plugins/repository-skills/skills/maintain-project-api/references/fix-policies.md deleted file mode 100644 index b6635f380..000000000 --- a/plugins/repository-skills/skills/maintain-project-api/references/fix-policies.md +++ /dev/null @@ -1,8 +0,0 @@ -# Fix Policies - -- Keep edits bounded to the target `API.md`. -- Preserve existing section bodies whenever they already satisfy the schema. -- Create a missing `API.md` from the bundled template. -- Normalize canonical headings and subsection headings to the configured names and order. -- Add missing sections or subsections from the configured templates instead of inventing repo-fiction. -- Do not synthesize endpoints, field names, schemas, environment variable names, service names, credential names, permissions, version guarantees, support paths, or compatibility promises. diff --git a/plugins/repository-skills/skills/maintain-project-api/references/output-contract.md b/plugins/repository-skills/skills/maintain-project-api/references/output-contract.md deleted file mode 100644 index 5716d410b..000000000 --- a/plugins/repository-skills/skills/maintain-project-api/references/output-contract.md +++ /dev/null @@ -1,16 +0,0 @@ -# Output Contract - -Return Markdown plus JSON with: - -- `run_context` -- `schema_contract` -- `schema_violations` -- `command_integrity_issues` -- `content_quality_issues` -- `fixes_applied` -- `post_fix_status` -- `errors` - -Clean-run rule: - -- Output exactly `No findings.` only when there are no remaining issues and no errors. diff --git a/plugins/repository-skills/skills/maintain-project-api/references/project-api-maintenance-automation-prompts.md b/plugins/repository-skills/skills/maintain-project-api/references/project-api-maintenance-automation-prompts.md deleted file mode 100644 index ec7e68fca..000000000 --- a/plugins/repository-skills/skills/maintain-project-api/references/project-api-maintenance-automation-prompts.md +++ /dev/null @@ -1,18 +0,0 @@ -# Automation Prompts - -Use these prompts when validating or applying the skill through automation. - -## Check-only - -- Audit `API.md` for the canonical section order. -- Confirm the required table of contents is present and matches the canonical top-level headings. -- Confirm sections with required subsections contain the configured `###` headings. -- Flag placeholder content and malformed or weak verification command blocks. - -## Apply - -- Create a missing `API.md` from the bundled template. -- Normalize `API.md` to the canonical section order. -- Preserve existing API reference guidance where it already matches the schema. -- Add missing sections or subsections from the configured templates only. -- Keep all edits bounded to `API.md`. diff --git a/plugins/repository-skills/skills/maintain-project-api/references/section-schema.md b/plugins/repository-skills/skills/maintain-project-api/references/section-schema.md deleted file mode 100644 index 07de4c760..000000000 --- a/plugins/repository-skills/skills/maintain-project-api/references/section-schema.md +++ /dev/null @@ -1,52 +0,0 @@ -# Section Schema - -The canonical base `API.md` structure is defined by: - -- `../config/api-customization.template.yaml` -- `../assets/API.template.md` - -Base top-level shape: - -1. top-level title -2. short API-consumer-facing summary -3. `## Table of Contents` -4. `## Overview` -5. `## API Surface` -6. `## Authentication and Access` -7. `## Requests and Responses` -8. `## Errors` -9. `## Versioning and Compatibility` -10. `## Local Development and Verification` -11. `## Support and Ownership` - -Required subsection shape: - -- `Overview` - - `Who This API Is For` - - `Stability Status` -- `API Surface` - - `Entry Points` - - `Protocols and Transports` -- `Authentication and Access` - - `Credentials` - - `Permissions` -- `Requests and Responses` - - `Request Shape` - - `Response Shape` - - `Data Models` -- `Errors` - - `Error Shape` - - `Common Failure Modes` -- `Versioning and Compatibility` - - `Supported Versions` - - `Breaking Changes` -- `Local Development and Verification` - - `Runtime Configuration` - - `Verification` - -Schema expectations: - -- Use `##` headings for top-level sections. -- Use `###` headings for required subsections. -- Always include a top-level `Table of Contents`. -- Preserve additional repo-specific sections when present, but keep canonical sections in canonical order. diff --git a/plugins/repository-skills/skills/maintain-project-api/references/style-rules.md b/plugins/repository-skills/skills/maintain-project-api/references/style-rules.md deleted file mode 100644 index 1cf8e9510..000000000 --- a/plugins/repository-skills/skills/maintain-project-api/references/style-rules.md +++ /dev/null @@ -1,10 +0,0 @@ -# Style Rules - -- Keep API reference guidance direct, concrete, and repo-grounded. -- Favor short explanatory paragraphs plus small bullet lists when they improve scanability. -- Treat `API.md` as a public-interface contract, not as a product overview, implementation narrative, or contributor guide. -- Keep `API Surface` concrete enough that a consumer can find the callable entry points without reverse-engineering the repo. -- Keep `Requests and Responses` grounded in real fields, arguments, files, commands, return values, or generated artifacts. -- Keep `Errors` actionable: explain what the caller sees and the most likely next diagnostic step. -- Keep `Local Development and Verification` operational and practical, with reproducible commands or call examples when they are known. -- Prefer fenced code blocks with language info strings when showing verification commands or API call examples. diff --git a/plugins/repository-skills/skills/maintain-project-api/scripts/maintain_project_api.py b/plugins/repository-skills/skills/maintain-project-api/scripts/maintain_project_api.py deleted file mode 100644 index fef84c5d9..000000000 --- a/plugins/repository-skills/skills/maintain-project-api/scripts/maintain_project_api.py +++ /dev/null @@ -1,823 +0,0 @@ -#!/usr/bin/env -S uv run --script -# /// script -# requires-python = ">=3.9" -# dependencies = [ -# "PyYAML>=6.0.2,<7", -# ] -# /// -"""Audit and apply bounded API.md maintenance from a hard-enforced schema.""" - -from __future__ import annotations - -import argparse -import json -import re -import sys -from dataclasses import dataclass -from datetime import datetime, timezone -from pathlib import Path -from typing import Any, Dict, List, Optional, Sequence, Tuple - -import yaml - -H2_RE = re.compile(r"^##\s+(.+?)\s*$", re.MULTILINE) -H3_RE = re.compile(r"^###\s+(.+?)\s*$", re.MULTILINE) -SHELL_FENCE_RE = re.compile(r"```([^\n`]*)\n(.*?)```", re.DOTALL) -PLACEHOLDER_PATTERNS = [ - re.compile(r"\bTODO\b", re.IGNORECASE), - re.compile(r"\bTBD\b", re.IGNORECASE), - re.compile(r"<[^>]+>"), -] - - -@dataclass -class Issue: - issue_id: str - category: str - severity: str - file: str - evidence: str - recommended_fix: str - auto_fixable: bool - fixed: bool = False - - def to_dict(self) -> Dict[str, Any]: - return { - "issue_id": self.issue_id, - "category": self.category, - "severity": self.severity, - "file": self.file, - "evidence": self.evidence, - "recommended_fix": self.recommended_fix, - "auto_fixable": self.auto_fixable, - "fixed": self.fixed, - } - - -def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser( - description="Audit and optionally apply bounded API.md maintenance from a hard-enforced schema." - ) - parser.add_argument("--project-root", required=True, help="Absolute project root path") - parser.add_argument("--api-path", help="Optional API path override") - parser.add_argument("--run-mode", required=True, choices=["check-only", "apply"], help="Execution mode") - parser.add_argument("--config", help="Optional API config override") - parser.add_argument("--json-out", help="Write JSON report path") - parser.add_argument("--md-out", help="Write markdown report path") - parser.add_argument("--print-json", action="store_true", help="Print JSON report") - parser.add_argument("--print-md", action="store_true", help="Print markdown report") - parser.add_argument("--fail-on-issues", action="store_true", help="Exit non-zero when unresolved issues remain") - return parser.parse_args() - - -def read_text(path: Path) -> str: - try: - return path.read_text(encoding="utf-8") - except UnicodeDecodeError: - return path.read_text(encoding="utf-8", errors="ignore") - - -def write_text(path: Path, content: str) -> None: - path.write_text(content, encoding="utf-8") - - -def normalize_whitespace(text: str) -> str: - return text.strip() + "\n" - - -def slugify_heading(heading: str) -> str: - slug = heading.lower().strip() - slug = re.sub(r"[^\w\s-]", "", slug) - slug = re.sub(r"\s+", "-", slug) - slug = re.sub(r"-{2,}", "-", slug) - return slug - - -def split_sections(text: str) -> Tuple[str, List[Tuple[str, str]]]: - matches = list(H2_RE.finditer(text)) - if not matches: - return text.strip(), [] - - preamble = text[: matches[0].start()].rstrip() - sections: List[Tuple[str, str]] = [] - for idx, match in enumerate(matches): - start = match.end() - end = matches[idx + 1].start() if idx + 1 < len(matches) else len(text) - sections.append((match.group(1).strip(), text[start:end].strip("\n"))) - return preamble, sections - - -def split_subsections(body: str) -> Tuple[str, List[Tuple[str, str]]]: - matches = list(H3_RE.finditer(body)) - if not matches: - return body.strip(), [] - - preamble = body[: matches[0].start()].strip() - subsections: List[Tuple[str, str]] = [] - for idx, match in enumerate(matches): - start = match.end() - end = matches[idx + 1].start() if idx + 1 < len(matches) else len(body) - subsections.append((match.group(1).strip(), body[start:end].strip("\n"))) - return preamble, subsections - - -def section_map(sections: Sequence[Tuple[str, str]]) -> Dict[str, str]: - return {heading: body for heading, body in sections} - - -def parse_title_and_summary(preamble: str) -> Tuple[Optional[str], Optional[str], List[str]]: - lines = [line.rstrip() for line in preamble.splitlines()] - title: Optional[str] = None - summary: Optional[str] = None - extras: List[str] = [] - title_index: Optional[int] = None - summary_index: Optional[int] = None - - for idx, line in enumerate(lines): - if line.startswith("# "): - title = line[2:].strip() - title_index = idx - break - - if title_index is None: - return None, None, lines - - for idx in range(title_index + 1, len(lines)): - if lines[idx].strip(): - summary = lines[idx].strip() - summary_index = idx - break - - for idx, line in enumerate(lines): - if idx in {title_index, summary_index}: - continue - extras.append(line) - - return title, summary, extras - - -def collapse_blank_lines(lines: Sequence[str]) -> List[str]: - collapsed: List[str] = [] - previous_blank = False - for line in lines: - blank = line.strip() == "" - if blank and previous_blank: - continue - collapsed.append(line) - previous_blank = blank - while collapsed and collapsed[0].strip() == "": - collapsed.pop(0) - while collapsed and collapsed[-1].strip() == "": - collapsed.pop() - return collapsed - - -def normalize_preamble(preamble: str, project_name: str, preserve_preamble: bool) -> str: - title, summary, extras = parse_title_and_summary(preamble) - normalized_title = title or f"API Reference for {project_name}" - normalized_summary = ( - summary - or "Use this reference to understand the public API surface, how to call it, what it returns, and how to verify behavior locally." - ) - lines = [f"# {normalized_title}", "", normalized_summary] - if preserve_preamble: - extra_lines = collapse_blank_lines(extras) - if extra_lines: - lines.extend(["", *extra_lines]) - return "\n".join(lines).strip() - - -def read_yaml(path: Path) -> Dict[str, Any]: - data = yaml.safe_load(read_text(path)) - return data if isinstance(data, dict) else {} - - -def deep_merge(base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]: - merged: Dict[str, Any] = dict(base) - for key, value in override.items(): - if isinstance(value, dict) and isinstance(merged.get(key), dict): - merged[key] = deep_merge(merged[key], value) # type: ignore[arg-type] - else: - merged[key] = value - return merged - - -def load_config(project_root: Path, config_override: Optional[str]) -> Dict[str, Any]: - default_path = Path(__file__).resolve().parents[1] / "config" / "api-customization.template.yaml" - default_config = read_yaml(default_path) - loaded_path = default_path - - if config_override: - override_path = Path(config_override).expanduser().resolve() - merged = deep_merge(default_config, read_yaml(override_path)) - merged["isCustomized"] = True - merged["configPath"] = str(override_path) - merged["defaultConfigPath"] = str(default_path) - return merged - - project_config = project_root / "config" / "api-customization.yaml" - if project_config.is_file(): - loaded_path = project_config - merged = deep_merge(default_config, read_yaml(project_config)) - merged["isCustomized"] = True - else: - merged = dict(default_config) - merged["isCustomized"] = bool(default_config.get("isCustomized", False)) - - merged["configPath"] = str(loaded_path) - merged["defaultConfigPath"] = str(default_path) - return merged - - -def config_settings(config: Dict[str, Any]) -> Dict[str, Any]: - settings = config.get("settings", {}) - return settings if isinstance(settings, dict) else {} - - -def required_sections(settings: Dict[str, Any]) -> List[str]: - values = settings.get("requiredSections", []) - return [str(item) for item in values] if isinstance(values, list) else [] - - -def canonical_order(settings: Dict[str, Any]) -> List[str]: - values = settings.get("sectionOrder", []) - return [str(item) for item in values] if isinstance(values, list) else [] - - -def required_subsections(settings: Dict[str, Any]) -> Dict[str, List[str]]: - raw = settings.get("requiredSubsections", {}) - if not isinstance(raw, dict): - return {} - return { - str(key): [str(item) for item in value] - for key, value in raw.items() - if isinstance(value, list) - } - - -def section_aliases(settings: Dict[str, Any]) -> Dict[str, List[str]]: - raw = settings.get("sectionAliases", {}) - if not isinstance(raw, dict): - return {} - return { - str(key): [str(item) for item in value] - for key, value in raw.items() - if isinstance(value, list) - } - - -def subsection_aliases(settings: Dict[str, Any]) -> Dict[str, List[str]]: - raw = settings.get("subsectionAliases", {}) - if not isinstance(raw, dict): - return {} - return { - str(key): [str(item) for item in value] - for key, value in raw.items() - if isinstance(value, list) - } - - -def section_templates(settings: Dict[str, Any]) -> Dict[str, str]: - raw = settings.get("sectionTemplates", {}) - if not isinstance(raw, dict): - return {} - return {str(key): str(value).strip() for key, value in raw.items()} - - -def subsection_templates(settings: Dict[str, Any]) -> Dict[str, str]: - raw = settings.get("subsectionTemplates", {}) - if not isinstance(raw, dict): - return {} - return {str(key): str(value).strip() for key, value in raw.items()} - - -def build_toc(headings: Sequence[str]) -> str: - return "\n".join(f"- [{heading}](#{slugify_heading(heading)})" for heading in headings) - - -def toc_entries(body: str) -> List[str]: - entries: List[str] = [] - for line in body.splitlines(): - match = re.match(r"^\s*-\s+\[(.+?)\]\(#(.+?)\)\s*$", line.strip()) - if match: - entries.append(match.group(1).strip()) - return entries - - -def section_alias_lookup(settings: Dict[str, Any]) -> Dict[str, str]: - reverse: Dict[str, str] = {} - for canonical, aliases in section_aliases(settings).items(): - for alias in aliases: - reverse[alias] = canonical - return reverse - - -def subsection_alias_lookup(settings: Dict[str, Any]) -> Dict[Tuple[str, str], str]: - reverse: Dict[Tuple[str, str], str] = {} - for canonical_path, aliases in subsection_aliases(settings).items(): - if "/" not in canonical_path: - continue - parent, canonical_name = canonical_path.split("/", 1) - for alias in aliases: - reverse[(parent, alias)] = canonical_name - return reverse - - -def render_template_bootstrap(project_root: Path) -> str: - template_path = Path(__file__).resolve().parents[1] / "assets" / "API.template.md" - template = read_text(template_path) - return normalize_whitespace(template.replace("{{PROJECT_NAME}}", project_root.name)) - - -def render_section_body(heading: str, existing_body: str, settings: Dict[str, Any]) -> str: - required_children = required_subsections(settings).get(heading, []) - section_template_map = section_templates(settings) - subsection_template_map = subsection_templates(settings) - subsection_alias_map = subsection_alias_lookup(settings) - - if not required_children: - return existing_body.strip() or section_template_map.get(heading, "") - - preamble, subsections = split_subsections(existing_body) - subsection_lookup: Dict[str, str] = {} - extra_subsections: List[Tuple[str, str]] = [] - for name, body in subsections: - canonical_name = subsection_alias_map.get((heading, name), name) - if canonical_name in required_children and canonical_name not in subsection_lookup: - subsection_lookup[canonical_name] = body - else: - extra_subsections.append((name, body)) - - lines: List[str] = [] - if preamble.strip(): - lines.extend([preamble.strip(), ""]) - - for idx, child in enumerate(required_children): - child_body = subsection_lookup.get(child, "").strip() or subsection_template_map.get(f"{heading}/{child}", "") - lines.extend([f"### {child}", "", child_body.strip()]) - if idx < len(required_children) - 1 or extra_subsections: - lines.append("") - - for idx, (name, body) in enumerate(extra_subsections): - lines.extend([f"### {name}", "", body.strip()]) - if idx < len(extra_subsections) - 1: - lines.append("") - - rendered = "\n".join(lines).strip() - return rendered or section_template_map.get(heading, "") - - -def validate_schema( - api_path: Path, - api_text: str, - config: Dict[str, Any], -) -> Tuple[List[Issue], List[Issue], List[Issue], List[Tuple[str, str]]]: - settings = config_settings(config) - required = required_sections(settings) - order = canonical_order(settings) - subsection_map = required_subsections(settings) - section_alias_map = section_alias_lookup(settings) - - schema_issues: List[Issue] = [] - command_issues: List[Issue] = [] - content_issues: List[Issue] = [] - - preamble, sections = split_sections(api_text) - lookup = section_map(sections) - title, summary, _extras = parse_title_and_summary(preamble) - - if not title: - schema_issues.append( - Issue( - issue_id="missing-title", - category="schema", - severity="high", - file=str(api_path), - evidence="API.md is missing a top-level '# API Reference for ' title.", - recommended_fix="Add a clear top-level API title.", - auto_fixable=True, - ) - ) - if not summary: - schema_issues.append( - Issue( - issue_id="missing-summary", - category="schema", - severity="medium", - file=str(api_path), - evidence="API.md is missing a short API consumer-facing summary beneath the title.", - recommended_fix="Add a short summary sentence beneath the top-level title.", - auto_fixable=True, - ) - ) - - if "Table of Contents" not in lookup: - schema_issues.append( - Issue( - issue_id="missing-table-of-contents", - category="schema", - severity="medium", - file=str(api_path), - evidence="API.md is missing the required '## Table of Contents' section.", - recommended_fix="Add a table of contents that mirrors the canonical top-level headings.", - auto_fixable=True, - ) - ) - - observed_headings = [heading for heading, _body in sections] - canonical_positions = {heading: idx for idx, heading in enumerate(observed_headings)} - for heading in required: - if heading in lookup: - continue - alias_found = next( - (alias for alias, canonical in section_alias_map.items() if canonical == heading and alias in lookup), - None, - ) - if alias_found: - schema_issues.append( - Issue( - issue_id=f"non-canonical-heading-{slugify_heading(heading)}", - category="schema", - severity="medium", - file=str(api_path), - evidence=f"API.md uses alias heading '## {alias_found}' where the canonical schema expects '## {heading}'.", - recommended_fix=f"Rename '## {alias_found}' to '## {heading}'.", - auto_fixable=True, - ) - ) - else: - schema_issues.append( - Issue( - issue_id=f"missing-section-{slugify_heading(heading)}", - category="schema", - severity="high", - file=str(api_path), - evidence=f"API.md is missing required section '## {heading}'.", - recommended_fix=f"Add the required '## {heading}' section.", - auto_fixable=True, - ) - ) - - order_positions: List[int] = [] - for heading in order: - if heading in canonical_positions: - order_positions.append(canonical_positions[heading]) - continue - alias_found = next( - (alias for alias, canonical in section_alias_map.items() if canonical == heading and alias in canonical_positions), - None, - ) - if alias_found: - order_positions.append(canonical_positions[alias_found]) - if order_positions and order_positions != sorted(order_positions): - schema_issues.append( - Issue( - issue_id="canonical-section-order", - category="schema", - severity="medium", - file=str(api_path), - evidence="Canonical API sections are not in the configured order.", - recommended_fix="Normalize the top-level sections into canonical order.", - auto_fixable=True, - ) - ) - - subsection_alias_map = subsection_alias_lookup(settings) - for parent, children in subsection_map.items(): - body = lookup.get(parent, "") - if not body: - alias_parent = next( - (alias for alias, canonical in section_alias_map.items() if canonical == parent and alias in lookup), - None, - ) - body = lookup.get(alias_parent, "") if alias_parent else "" - if not body: - continue - _preamble, subsections = split_subsections(body) - found = { - subsection_alias_map.get((parent, name), name): subsection_body - for name, subsection_body in subsections - } - for child in children: - if child not in found: - schema_issues.append( - Issue( - issue_id=f"missing-subsection-{slugify_heading(parent)}-{slugify_heading(child)}", - category="schema", - severity="high", - file=str(api_path), - evidence=f"Section '## {parent}' is missing required subsection '### {child}'.", - recommended_fix=f"Add the required subsection '### {child}' under '## {parent}'.", - auto_fixable=True, - ) - ) - - if "Table of Contents" in lookup: - expected_toc = [heading for heading in order if heading in required or heading in lookup] - actual_toc = toc_entries(lookup["Table of Contents"]) - if actual_toc != expected_toc: - schema_issues.append( - Issue( - issue_id="stale-table-of-contents", - category="schema", - severity="low", - file=str(api_path), - evidence="Table of contents entries do not match the canonical top-level section headings in order.", - recommended_fix="Regenerate the table of contents from the canonical section list.", - auto_fixable=True, - ) - ) - - for heading in required: - required_body = lookup.get(heading) - if not required_body: - continue - if not required_body.strip(): - schema_issues.append( - Issue( - issue_id=f"empty-section-{slugify_heading(heading)}", - category="schema", - severity="medium", - file=str(api_path), - evidence=f"Section '## {heading}' is present but empty.", - recommended_fix=f"Add grounded content to '## {heading}'.", - auto_fixable=True, - ) - ) - if any(pattern.search(required_body) for pattern in PLACEHOLDER_PATTERNS): - content_issues.append( - Issue( - issue_id=f"placeholder-content-{slugify_heading(heading)}", - category="content-quality", - severity="medium", - file=str(api_path), - evidence=f"Section '## {heading}' contains placeholder-style content.", - recommended_fix="Replace placeholder content with repo-grounded API consumer guidance.", - auto_fixable=False, - ) - ) - - verification_body = lookup.get("Local Development and Verification", "") - _preamble, dev_subsections = split_subsections(verification_body) - verification_lookup = {name: body for name, body in dev_subsections} - verification_text = verification_lookup.get("Verification", "").strip() - if verification_text: - shell_blocks = list(SHELL_FENCE_RE.finditer(verification_text)) - if shell_blocks: - for match in shell_blocks: - info = match.group(1).strip() - block = match.group(2).strip() - if not info: - command_issues.append( - Issue( - issue_id=f"missing-code-fence-info-string-{match.start()}", - category="command-integrity", - severity="low", - file=str(api_path), - evidence="Verification uses a fenced code block without a language info string.", - recommended_fix="Use fenced code blocks with an info string such as ```bash for verification commands.", - auto_fixable=False, - ) - ) - if not block: - command_issues.append( - Issue( - issue_id=f"empty-shell-block-{match.start()}", - category="command-integrity", - severity="medium", - file=str(api_path), - evidence="Verification contains an empty fenced code block.", - recommended_fix="Remove the empty block or replace it with grounded validation commands.", - auto_fixable=True, - ) - ) - if any(pattern.search(block) for pattern in PLACEHOLDER_PATTERNS): - command_issues.append( - Issue( - issue_id=f"placeholder-command-block-{match.start()}", - category="command-integrity", - severity="high", - file=str(api_path), - evidence="Verification contains a placeholder command block.", - recommended_fix="Replace the placeholder command block with grounded validation commands or prose.", - auto_fixable=False, - ) - ) - elif len(verification_text.split()) < 6: - content_issues.append( - Issue( - issue_id="thin-verification-guidance", - category="content-quality", - severity="medium", - file=str(api_path), - evidence="Local Development and Verification > Verification is too thin to help API consumers verify behavior.", - recommended_fix="Add grounded validation guidance, preferably with fenced code blocks and language info strings.", - auto_fixable=False, - ) - ) - - return schema_issues, command_issues, content_issues, sections - - -def apply_fixes( - project_root: Path, - api_path: Path, - api_text: str, - config: Dict[str, Any], -) -> Tuple[str, List[Dict[str, str]]]: - fixes: List[Dict[str, str]] = [] - if not api_text.strip(): - api_text = render_template_bootstrap(project_root) - fixes.append( - { - "action": "create-api-from-template", - "file": str(api_path), - "reason": "Created a missing API.md from the bundled canonical template.", - } - ) - - settings = config_settings(config) - required = required_sections(settings) - order = canonical_order(settings) - preserve_preamble = bool(settings.get("preservePreamble", True)) - allow_additional = bool(settings.get("allowAdditionalSections", True)) - section_alias_map = section_alias_lookup(settings) - - preamble, sections = split_sections(api_text) - normalized_preamble = normalize_preamble(preamble, project_root.name, preserve_preamble) - - canonical_lookup: Dict[str, str] = {} - extra_sections: List[Tuple[str, str]] = [] - for heading, body in sections: - if heading == "Table of Contents": - continue - canonical_heading = section_alias_map.get(heading, heading) - if canonical_heading in order or canonical_heading in required: - canonical_lookup[canonical_heading] = body - elif allow_additional: - extra_sections.append((heading, body)) - - canonical_sections: List[Tuple[str, str]] = [] - for heading in order: - body = render_section_body(heading, canonical_lookup.get(heading, ""), settings).strip() - canonical_sections.append((heading, body)) - - headings_for_toc = [heading for heading, _body in canonical_sections] - if allow_additional: - headings_for_toc.extend(heading for heading, _body in extra_sections) - rendered_sections = [("Table of Contents", build_toc(headings_for_toc).strip()), *canonical_sections] - if allow_additional: - rendered_sections.extend(extra_sections) - - parts = [normalized_preamble] - for heading, body in rendered_sections: - parts.extend(["", f"## {heading}", "", body.strip()]) - document = "\n".join(parts).strip() + "\n" - fixes.append( - { - "action": "normalize-api-structure", - "file": str(api_path), - "reason": "Normalized API.md to the canonical template-backed section schema.", - } - ) - return normalize_whitespace(document), fixes - - -def format_report(report: Dict[str, Any]) -> str: - total_issues = ( - len(report["schema_violations"]) - + len(report["command_integrity_issues"]) - + len(report["content_quality_issues"]) - ) - if total_issues == 0 and not report["errors"]: - return "No findings." - - lines = [ - "# API.md Maintenance Report", - "", - f"- Target: `{report['run_context']['api_path']}`", - f"- Mode: `{report['run_context']['run_mode']}`", - f"- Config: `{report['schema_contract']['config_path']}`", - ] - - for key, title in ( - ("schema_violations", "Schema Violations"), - ("command_integrity_issues", "Command Integrity Issues"), - ("content_quality_issues", "Content Quality Issues"), - ("fixes_applied", "Fixes Applied"), - ("errors", "Errors"), - ): - items = report[key] - if not items: - continue - lines.extend(["", f"## {title}"]) - for item in items: - evidence = item.get("evidence") or item.get("reason") or item.get("message") - lines.append(f"- {item.get('issue_id', item.get('action', 'item'))}: {evidence}") - - return "\n".join(lines).strip() + "\n" - - -def run_maintenance(args: argparse.Namespace) -> Tuple[Dict[str, Any], str]: - project_root = Path(args.project_root).expanduser().resolve() - if not project_root.is_dir(): - raise ValueError(f"Project root does not exist or is not a directory: {project_root}") - - api_path = ( - Path(args.api_path).expanduser().resolve() - if args.api_path - else project_root / "API.md" - ) - config = load_config(project_root, args.config) - - errors: List[str] = [] - fixes_applied: List[Dict[str, str]] = [] - existing_text = read_text(api_path) if api_path.is_file() else "" - - if args.run_mode == "apply": - new_text, applied = apply_fixes(project_root, api_path, existing_text, config) - if not api_path.parent.exists(): - api_path.parent.mkdir(parents=True, exist_ok=True) - if normalize_whitespace(existing_text) != new_text: - write_text(api_path, new_text) - fixes_applied.extend(applied) - existing_text = new_text - - if existing_text: - schema_issues, command_issues, content_issues, _sections = validate_schema( - api_path, existing_text, config - ) - else: - schema_issues = [ - Issue( - issue_id="missing-api-file", - category="schema", - severity="high", - file=str(api_path), - evidence="API.md does not exist.", - recommended_fix="Create the canonical API.md file from the bundled template.", - auto_fixable=True, - ) - ] - command_issues = [] - content_issues = [] - - report = { - "run_context": { - "project_root": str(project_root), - "api_path": str(api_path), - "run_mode": args.run_mode, - "generated_at": datetime.now(timezone.utc).isoformat(), - }, - "schema_contract": { - "config_path": config.get("configPath"), - "default_config_path": config.get("defaultConfigPath"), - "required_table_of_contents": True, - "required_sections": required_sections(config_settings(config)), - "section_order": canonical_order(config_settings(config)), - "required_subsections": required_subsections(config_settings(config)), - }, - "schema_violations": [issue.to_dict() for issue in schema_issues], - "command_integrity_issues": [issue.to_dict() for issue in command_issues], - "content_quality_issues": [issue.to_dict() for issue in content_issues], - "fixes_applied": fixes_applied, - "post_fix_status": { - "remaining_issue_count": len(schema_issues) + len(command_issues) + len(content_issues), - "is_clean": not schema_issues and not command_issues and not content_issues and not errors, - }, - "errors": errors, - } - markdown = format_report(report) - return report, markdown - - -def main() -> int: - args = parse_args() - try: - report, markdown = run_maintenance(args) - except ValueError as exc: - print(str(exc), file=sys.stderr) - return 1 - - if args.json_out: - Path(args.json_out).write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8") - if args.md_out: - Path(args.md_out).write_text(markdown, encoding="utf-8") - if args.print_json: - print(json.dumps(report, indent=2)) - if args.print_md: - print(markdown, end="") - - has_issues = ( - bool(report["schema_violations"]) - or bool(report["command_integrity_issues"]) - or bool(report["content_quality_issues"]) - or bool(report["errors"]) - ) - if args.fail_on_issues and has_issues: - return 2 - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/plugins/repository-skills/skills/maintain-project-docs/SKILL.md b/plugins/repository-skills/skills/maintain-project-docs/SKILL.md index da0c511c0..ced89515c 100644 --- a/plugins/repository-skills/skills/maintain-project-docs/SKILL.md +++ b/plugins/repository-skills/skills/maintain-project-docs/SKILL.md @@ -1,6 +1,6 @@ --- name: maintain-project-docs -description: Coordinate maintenance across README.md, CONTRIBUTING.md, AGENTS.md, optional API.md, and ROADMAP.md. Use for a complete documentation sweep, ownership-boundary audit, repeated-content cleanup, or one coordinated project-docs pass. +description: Coordinate maintenance across README.md, CONTRIBUTING.md, AGENTS.md, and ROADMAP.md. Use for a complete documentation sweep, ownership-boundary audit, repeated-content cleanup, or one coordinated project-docs pass. --- # Maintain Project Docs @@ -13,8 +13,8 @@ This skill is the umbrella layer for repository documentation maintenance. It de - Required: `--project-root ` - Required: `--run-mode ` -- Optional: `--include ` -- Optional: `--skip ` +- Optional: `--include ` +- Optional: `--skip ` - Optional: `--collect-source-tickets` - Optional: `--collect-github-issues` - Optional: `--github-repo ` @@ -22,7 +22,8 @@ This skill is the umbrella layer for repository documentation maintenance. It de ## Workflow 1. Validate the project root and choose the included document workflows. -2. Run the owner workflows serially in this order: README, CONTRIBUTING, AGENTS, API when `API.md` exists, ROADMAP. +2. Run the owner workflows serially in this order: README, CONTRIBUTING, + AGENTS, ROADMAP. 3. Pass `--collect-source-tickets`, `--collect-github-issues`, and `--github-repo` only to the roadmap workflow. 4. In `check-only`, report each owner workflow's findings plus cross-document responsibility issues. 5. In `apply`, let each owner workflow make only its own bounded file edits, then audit the resulting document set again. @@ -33,7 +34,6 @@ This skill is the umbrella layer for repository documentation maintenance. It de - `README.md`: product overview, status, quick start, usage, and short development pointer. - `CONTRIBUTING.md`: human contributor workflow, setup, development expectations, PR expectations, and communication rules. - `AGENTS.md`: durable agent-facing repo scope, routing, commands, review and delivery rules, and safety boundaries. -- `API.md`: grounded API surface, authentication, schemas, errors, versioning, compatibility, and verification guidance. - `ROADMAP.md`: milestones, small tickets, TODO/FIXME imports, backlog planning, and notable roadmap history. When content belongs in another file, report the mismatch instead of copying or rewriting it silently. @@ -61,7 +61,8 @@ Keep `apply` edits in the main thread. The auditor may return proposed patch-set - Never auto-commit, auto-push, or open a PR. - Never invent project claims, commands, API behavior, roadmap scope, or agent policy. - Never make broad cross-file rewrites as a side effect of running the umbrella workflow. -- Do not duplicate detailed rules across README, CONTRIBUTING, AGENTS, API, and ROADMAP just to make each file self-contained. +- Do not duplicate detailed rules across README, CONTRIBUTING, AGENTS, and + ROADMAP just to make each file self-contained. - Treat this skill as an orchestrator. The owner document skills keep their own schemas, templates, and bounded apply behavior. ## References diff --git a/plugins/repository-skills/skills/maintain-project-docs/agents/openai.yaml b/plugins/repository-skills/skills/maintain-project-docs/agents/openai.yaml index 81f6ebdcd..72b3a66b9 100644 --- a/plugins/repository-skills/skills/maintain-project-docs/agents/openai.yaml +++ b/plugins/repository-skills/skills/maintain-project-docs/agents/openai.yaml @@ -1,4 +1,4 @@ interface: display_name: "Maintain Project Docs" short_description: "Run a coordinated docs sweep across README, CONTRIBUTING, AGENTS, optional API, and ROADMAP." - default_prompt: "Use $maintain-project-docs when a repository needs a complete documentation maintenance pass across README.md, CONTRIBUTING.md, AGENTS.md, optional API.md, and ROADMAP.md. Delegate each file to its owner maintenance skill, run the workflows serially, preserve the responsibility split between product docs, contributor workflow, agent guidance, API reference, and roadmap planning, and report cross-document drift instead of rewriting several files at once." + default_prompt: "Use $maintain-project-docs when a repository needs a complete documentation maintenance pass across README.md, CONTRIBUTING.md, AGENTS.md, and ROADMAP.md. Delegate each file to its owner maintenance skill, run the workflows serially, preserve the responsibility split between product docs, contributor workflow, agent guidance, and roadmap planning, and report cross-document drift instead of rewriting several files at once." diff --git a/plugins/repository-skills/skills/maintain-project-docs/references/document-boundaries.md b/plugins/repository-skills/skills/maintain-project-docs/references/document-boundaries.md index 154777898..059fe6c54 100644 --- a/plugins/repository-skills/skills/maintain-project-docs/references/document-boundaries.md +++ b/plugins/repository-skills/skills/maintain-project-docs/references/document-boundaries.md @@ -9,7 +9,6 @@ Use this reference when an audit finds repeated content or a doc section that se | `README.md` | Product identity, current status, quick start, usage, repo structure, and a short development pointer. | `CONTRIBUTING.md`, `ROADMAP.md`, relevant package or product docs. | | `CONTRIBUTING.md` | Human contributor setup, workflow, verification, PR expectations, and communication guidance. | `README.md`, repo-maintenance docs, `AGENTS.md` only when contributors need to know agent rules exist. | | `AGENTS.md` | Durable instructions for Codex and other repo agents: scope, routing, commands, workflow constraints, delivery rules, safety boundaries, and local overrides. | `README.md`, `CONTRIBUTING.md`, `ROADMAP.md`, maintainer docs. | -| `API.md` | Grounded API surface, authentication, request and response schemas, errors, versioning, compatibility, verification, and support guidance. | Implementations, schemas, tests, and release notes that prove the documented contract. | | `ROADMAP.md` | Milestones, small tickets, imported TODO/FIXME work, issue links, and notable planning history. | GitHub issues, source references, docs that explain accepted scope. | ## Drift Signals @@ -17,7 +16,6 @@ Use this reference when an audit finds repeated content or a doc section that se - README contains full branch, PR, release, or maintainer workflow instructions instead of a short pointer. - CONTRIBUTING repeats product overview or roadmap history instead of contributor workflow. - AGENTS repeats public product marketing, contributor onboarding prose, or detailed accessibility standards. -- API documentation invents behavior or repeats contributor and roadmap procedure. - ROADMAP contains long procedural guidance that should live in AGENTS, CONTRIBUTING, or maintainer docs. ## Fixing Policy diff --git a/plugins/repository-skills/skills/maintain-project-docs/references/project-docs-maintenance-automation-prompts.md b/plugins/repository-skills/skills/maintain-project-docs/references/project-docs-maintenance-automation-prompts.md index c33ad48cd..73a6bfca7 100644 --- a/plugins/repository-skills/skills/maintain-project-docs/references/project-docs-maintenance-automation-prompts.md +++ b/plugins/repository-skills/skills/maintain-project-docs/references/project-docs-maintenance-automation-prompts.md @@ -4,7 +4,11 @@ Use these prompts when scheduling or delegating recurring documentation sweeps. ## Check-Only Sweep -Run `maintain-project-docs` in `check-only` mode for the target repository. Audit README, CONTRIBUTING, AGENTS, API when present, and ROADMAP in that order. Report owner-skill findings, cross-document responsibility drift, stale command evidence, and any roadmap small-ticket candidates requested by the operator. Do not edit files, commit, push, or open a pull request. +Run `maintain-project-docs` in `check-only` mode for the target repository. +Audit README, CONTRIBUTING, AGENTS, and ROADMAP in that order. Report +owner-skill findings, cross-document responsibility drift, stale command +evidence, and any roadmap small-ticket candidates requested by the operator. Do +not edit files, commit, push, or open a pull request. ## Bounded Apply Sweep diff --git a/plugins/repository-skills/skills/maintain-project-docs/scripts/maintain_project_docs.py b/plugins/repository-skills/skills/maintain-project-docs/scripts/maintain_project_docs.py index 0e4033f9c..50b4b2521 100644 --- a/plugins/repository-skills/skills/maintain-project-docs/scripts/maintain_project_docs.py +++ b/plugins/repository-skills/skills/maintain-project-docs/scripts/maintain_project_docs.py @@ -30,35 +30,32 @@ class DocumentWorkflow: key="readme", label="README", filename="README.md", - script=PLUGIN_ROOT / "skills/maintain-project-readme/scripts/maintain_project_readme.py", + script=PLUGIN_ROOT + / "skills/maintain-project-readme/scripts/maintain_project_readme.py", path_arg="--readme-path", ), DocumentWorkflow( key="contributing", label="CONTRIBUTING", filename="CONTRIBUTING.md", - script=PLUGIN_ROOT / "skills/maintain-project-contributing/scripts/maintain_project_contributing.py", + script=PLUGIN_ROOT + / "skills/maintain-project-contributing/scripts/maintain_project_contributing.py", path_arg="--contributing-path", ), DocumentWorkflow( key="agents", label="AGENTS", filename="AGENTS.md", - script=PLUGIN_ROOT / "skills/maintain-project-agents/scripts/maintain_project_agents.py", + script=PLUGIN_ROOT + / "skills/maintain-project-agents/scripts/maintain_project_agents.py", path_arg="--agents-path", ), - DocumentWorkflow( - key="api", - label="API", - filename="API.md", - script=PLUGIN_ROOT / "skills/maintain-project-api/scripts/maintain_project_api.py", - path_arg="--api-path", - ), DocumentWorkflow( key="roadmap", label="ROADMAP", filename="ROADMAP.md", - script=PLUGIN_ROOT / "skills/maintain-project-roadmap/scripts/maintain_project_roadmap.py", + script=PLUGIN_ROOT + / "skills/maintain-project-roadmap/scripts/maintain_project_roadmap.py", path_arg="--roadmap-path", ), ) @@ -77,16 +74,31 @@ class DocumentWorkflow: def parse_args(argv: Optional[Sequence[str]] = None) -> argparse.Namespace: - parser = argparse.ArgumentParser(description="Run a coordinated project documentation maintenance sweep.") - parser.add_argument("--project-root", required=True, help="Absolute project root path") - parser.add_argument("--run-mode", required=True, choices=["check-only", "apply"], help="Execution mode") - parser.add_argument("--include", help="Comma-separated document workflow keys to include") + parser = argparse.ArgumentParser( + description="Run a coordinated project documentation maintenance sweep." + ) + parser.add_argument( + "--project-root", required=True, help="Absolute project root path" + ) + parser.add_argument( + "--run-mode", + required=True, + choices=["check-only", "apply"], + help="Execution mode", + ) + parser.add_argument( + "--include", help="Comma-separated document workflow keys to include" + ) parser.add_argument("--skip", help="Comma-separated document workflow keys to skip") parser.add_argument("--json-out", help="Write JSON report path") parser.add_argument("--md-out", help="Write markdown report path") parser.add_argument("--print-json", action="store_true", help="Print JSON report") parser.add_argument("--print-md", action="store_true", help="Print markdown report") - parser.add_argument("--fail-on-issues", action="store_true", help="Exit non-zero when findings remain") + parser.add_argument( + "--fail-on-issues", + action="store_true", + help="Exit non-zero when findings remain", + ) parser.add_argument( "--collect-source-tickets", action="store_true", @@ -97,7 +109,10 @@ def parse_args(argv: Optional[Sequence[str]] = None) -> argparse.Namespace: action="store_true", help="Pass GitHub issue collection through to the roadmap workflow.", ) - parser.add_argument("--github-repo", help="Optional OWNER/REPO override for roadmap GitHub issue collection") + parser.add_argument( + "--github-repo", + help="Optional OWNER/REPO override for roadmap GitHub issue collection", + ) return parser.parse_args(argv) @@ -120,16 +135,13 @@ def select_workflows( if include_keys: selected = [known[key] for key in include_keys if key in known] else: - selected = [ - workflow - for workflow in DOCUMENT_WORKFLOWS - if workflow.key != "api" - or (project_root is not None and (project_root / workflow.filename).is_file()) - ] + selected = list(DOCUMENT_WORKFLOWS) return [workflow for workflow in selected if workflow.key not in skip_keys], errors -def build_child_command(args: argparse.Namespace, workflow: DocumentWorkflow, project_root: Path) -> List[str]: +def build_child_command( + args: argparse.Namespace, workflow: DocumentWorkflow, project_root: Path +) -> List[str]: command = [ sys.executable, str(workflow.script), @@ -151,9 +163,13 @@ def build_child_command(args: argparse.Namespace, workflow: DocumentWorkflow, pr return command -def run_child(args: argparse.Namespace, workflow: DocumentWorkflow, project_root: Path) -> Dict[str, Any]: +def run_child( + args: argparse.Namespace, workflow: DocumentWorkflow, project_root: Path +) -> Dict[str, Any]: command = build_child_command(args, workflow, project_root) - proc = subprocess.run(command, cwd=project_root, capture_output=True, text=True, check=False) + proc = subprocess.run( + command, cwd=project_root, capture_output=True, text=True, check=False + ) child: Dict[str, Any] = { "key": workflow.key, "label": workflow.label, @@ -171,7 +187,9 @@ def run_child(args: argparse.Namespace, workflow: DocumentWorkflow, project_root if proc.stdout.strip(): child["stdout"] = proc.stdout.strip() if proc.returncode != 0: - child["errors"].append(f"{workflow.label} workflow exited with status {proc.returncode}.") + child["errors"].append( + f"{workflow.label} workflow exited with status {proc.returncode}." + ) return child @@ -187,7 +205,9 @@ def heading_present(text: str, heading: str) -> bool: return re.search(pattern, text) is not None -def responsibility_issue(file: Path, issue_id: str, message: str, destination: str) -> Dict[str, Any]: +def responsibility_issue( + file: Path, issue_id: str, message: str, destination: str +) -> Dict[str, Any]: return { "issue_id": issue_id, "severity": "warning", @@ -197,7 +217,9 @@ def responsibility_issue(file: Path, issue_id: str, message: str, destination: s } -def audit_responsibility_boundaries(project_root: Path, selected: Sequence[DocumentWorkflow]) -> List[Dict[str, Any]]: +def audit_responsibility_boundaries( + project_root: Path, selected: Sequence[DocumentWorkflow] +) -> List[Dict[str, Any]]: selected_keys = {workflow.key for workflow in selected} issues: List[Dict[str, Any]] = [] @@ -209,7 +231,11 @@ def maybe_read(key: str, filename: str) -> Tuple[Path, str]: readme_path, readme = maybe_read("readme", "README.md") if readme: - for heading in ("Contribution Workflow", "Review Expectations", "Release Process"): + for heading in ( + "Contribution Workflow", + "Review Expectations", + "Release Process", + ): if heading_present(readme, heading): issues.append( responsibility_issue( @@ -237,7 +263,9 @@ def maybe_read(key: str, filename: str) -> Tuple[Path, str]: if agents: for heading in ("Quick Start", "Usage", "Known Gaps"): if heading_present(agents, heading): - destination = "README.md" if heading in {"Quick Start", "Usage"} else "ROADMAP.md" + destination = ( + "README.md" if heading in {"Quick Start", "Usage"} else "ROADMAP.md" + ) issues.append( responsibility_issue( agents_path, @@ -251,7 +279,9 @@ def maybe_read(key: str, filename: str) -> Tuple[Path, str]: if roadmap: for heading in ("Contribution Workflow", "Local Setup", "Safety Boundaries"): if heading_present(roadmap, heading): - destination = "CONTRIBUTING.md" if heading != "Safety Boundaries" else "AGENTS.md" + destination = ( + "CONTRIBUTING.md" if heading != "Safety Boundaries" else "AGENTS.md" + ) issues.append( responsibility_issue( roadmap_path, @@ -267,9 +297,11 @@ def child_issue_count(child: Dict[str, Any]) -> int: report = child.get("report") if not isinstance(report, dict): return len(child.get("errors", [])) - return sum(len(report.get(key, [])) for key in ISSUE_KEYS if isinstance(report.get(key), list)) + len( - child.get("errors", []) - ) + return sum( + len(report.get(key, [])) + for key in ISSUE_KEYS + if isinstance(report.get(key), list) + ) + len(child.get("errors", [])) def child_fixes(child: Dict[str, Any]) -> List[Dict[str, Any]]: @@ -297,7 +329,9 @@ def markdown_report(report: Dict[str, Any]) -> str: ] for child in report["document_reports"]: issue_count = child_issue_count(child) - lines.append(f"- `{child['key']}`: exit `{child['returncode']}`, {issue_count} issue(s)") + lines.append( + f"- `{child['key']}`: exit `{child['returncode']}`, {issue_count} issue(s)" + ) lines.extend(["", "## Responsibility Issues", ""]) if report["responsibility_issues"]: @@ -332,9 +366,9 @@ def write_text(path: Path, text: str) -> None: def unresolved_issues(report: Dict[str, Any]) -> bool: - return bool(report["responsibility_issues"] or report["errors"] or report["post_fix_status"]) or any( - child_issue_count(child) for child in report["document_reports"] - ) + return bool( + report["responsibility_issues"] or report["errors"] or report["post_fix_status"] + ) or any(child_issue_count(child) for child in report["document_reports"]) def run_maintenance(args: argparse.Namespace) -> Tuple[Dict[str, Any], str]: @@ -357,7 +391,9 @@ def run_maintenance(args: argparse.Namespace) -> Tuple[Dict[str, Any], str]: "errors": selection_errors, } if not project_root.is_dir(): - report["errors"].append(f"Project root does not exist or is not a directory: {project_root}") + report["errors"].append( + f"Project root does not exist or is not a directory: {project_root}" + ) return report, markdown_report(report) if not report["errors"]: @@ -367,7 +403,9 @@ def run_maintenance(args: argparse.Namespace) -> Tuple[Dict[str, Any], str]: report["fixes_applied"].extend(child_fixes(child)) report["post_fix_status"].extend(child_post_fix_status(child)) report["errors"].extend(child["errors"]) - report["responsibility_issues"] = audit_responsibility_boundaries(project_root, selected) + report["responsibility_issues"] = audit_responsibility_boundaries( + project_root, selected + ) return report, markdown_report(report) diff --git a/plugins/repository-skills/skills/repository-operations-workflow/SKILL.md b/plugins/repository-skills/skills/repository-operations-workflow/SKILL.md index 43d6abdc1..c278a19b6 100644 --- a/plugins/repository-skills/skills/repository-operations-workflow/SKILL.md +++ b/plugins/repository-skills/skills/repository-operations-workflow/SKILL.md @@ -24,7 +24,7 @@ needed for that classification, then hand work to the narrowest owner. | Codex worktree mode, local environment, or app-owned worktree behavior | `codex-gui-worktree-workflow` | | Parallel worker branch, worktree, write, or integration ownership | `coordinate-worktrees-and-threads` | | GitHub settings, rulesets, security automation, or repository policy | `maintain-github-repository` | - | README, CONTRIBUTING, AGENTS, API, or ROADMAP maintenance | The matching `maintain-project-*` document workflow | + | README, CONTRIBUTING, AGENTS, or ROADMAP maintenance | The matching `maintain-project-*` document workflow | | Coordinated repository-document sweep | `maintain-project-docs` | | Version bump, tag, release, publication, or branch-accounting cleanup | `maintain-project-repo` | 3. When the request crosses multiple outcomes, name the sequence and keep each diff --git a/scripts/export_hermes_skills.py b/scripts/export_hermes_skills.py index 438226941..4ec7e16d0 100644 --- a/scripts/export_hermes_skills.py +++ b/scripts/export_hermes_skills.py @@ -176,7 +176,6 @@ "github-collaboration-workflow", "maintain-github-repository", "maintain-project-agents", - "maintain-project-api", "maintain-project-contributing", "maintain-project-docs", "maintain-project-readme", diff --git a/scripts/validate_socket.py b/scripts/validate_socket.py index 24c2d9ad5..851104b1c 100644 --- a/scripts/validate_socket.py +++ b/scripts/validate_socket.py @@ -13,6 +13,9 @@ REPO_ROOT = Path(__file__).resolve().parent.parent +PYTEST_CACHE = REPO_ROOT / ".codex" / ".cache" / "pytest" +MYPY_CACHE = REPO_ROOT / ".codex" / ".cache" / "mypy" +RUFF_CACHE = REPO_ROOT / ".codex" / ".cache" / "ruff" @dataclass(frozen=True) @@ -23,15 +26,47 @@ class Check: def root_python(script_name: str) -> tuple[str, ...]: - return (sys.executable, f"scripts/{script_name}") + return (sys.executable, "-B", f"scripts/{script_name}") + + +def python_module(module: str, *args: str) -> tuple[str, ...]: + return (sys.executable, "-B", "-m", module, *args) + + +def pytest(*paths: str) -> tuple[str, ...]: + return python_module( + "pytest", + *paths, + "-o", + f"cache_dir={PYTEST_CACHE}", + ) + + +def mypy(*paths: str, cache_name: str = "root") -> tuple[str, ...]: + return python_module( + "mypy", + "--cache-dir", + str(MYPY_CACHE / cache_name), + *paths, + ) + + +def ruff(*paths: str, cache_name: str = "root") -> tuple[str, ...]: + return python_module( + "ruff", + "check", + "--cache-dir", + str(RUFF_CACHE / cache_name), + *paths, + ) CORE_CHECKS = ( Check("root marketplace metadata", root_python("validate_socket_metadata.py")), Check("shared skill metadata", root_python("validate_socket_skill_metadata.py")), - Check("root tests", (sys.executable, "-m", "pytest")), - Check("root type checks", (sys.executable, "-m", "mypy")), - Check("root lint", (sys.executable, "-m", "ruff", "check", "scripts", "tests")), + Check("root tests", pytest()), + Check("root type checks", mypy()), + Check("root lint", ruff("scripts", "tests")), ) COMPATIBILITY_CHECKS = ( Check("Hermes compatibility", root_python("validate_hermes_compatibility.py")), @@ -40,23 +75,26 @@ def root_python(script_name: str) -> tuple[str, ...]: CHILD_CHECKS = ( Check( "Agent Engineering Skills tests", - ("uv", "run", "pytest"), - REPO_ROOT / "plugins" / "agent-engineering-skills", + pytest( + "plugins/agent-engineering-skills/skills/design-agent-automation-workflow/tests", + "plugins/agent-engineering-skills/skills/design-agent-eval-workflow/tests", + ), ), Check( "Agent Portability Skills tests", - ("uv", "run", "pytest"), - REPO_ROOT / "plugins" / "agent-portability-skills", + pytest( + "plugins/agent-portability-skills/tests", + "plugins/agent-portability-skills/skills/bootstrap-skills-plugin-repo/tests", + "plugins/agent-portability-skills/skills/sync-skills-repo-guidance/tests", + ), ), Check( "Agent Portability Skills lint", - ("uv", "run", "ruff", "check", "."), - REPO_ROOT / "plugins" / "agent-portability-skills", + ruff("plugins/agent-portability-skills", cache_name="agent-portability-skills"), ), Check( "Agent Portability Skills type checks", - ("uv", "run", "mypy", "."), - REPO_ROOT / "plugins" / "agent-portability-skills", + mypy("plugins/agent-portability-skills", cache_name="agent-portability-skills"), ), Check( "Apple Dev Skills docs", @@ -65,53 +103,58 @@ def root_python(script_name: str) -> tuple[str, ...]: ), Check( "Apple Dev Skills tests", - ("uv", "run", "pytest"), - REPO_ROOT / "plugins" / "apple-dev-skills", + pytest("plugins/apple-dev-skills/tests"), ), Check( "Professional Skills tests", - ("uv", "run", "pytest"), - REPO_ROOT / "plugins" / "professional-skills", + pytest("plugins/professional-skills/skills/dice-job-search-workflow/tests"), ), Check( "Python Skills metadata", - ("uv", "run", "scripts/validate_repo_metadata.py"), + ( + sys.executable, + "-B", + "scripts/validate_repo_metadata.py", + ), REPO_ROOT / "plugins" / "python-skills", ), Check( "Python Skills tests", - ("uv", "run", "pytest"), - REPO_ROOT / "plugins" / "python-skills", + pytest("plugins/python-skills/tests"), ), Check( "Python Skills lint", - ("uv", "run", "ruff", "check", "."), - REPO_ROOT / "plugins" / "python-skills", + ruff("plugins/python-skills", cache_name="python-skills"), ), Check( "Python Skills type checks", - ("uv", "run", "mypy", "."), - REPO_ROOT / "plugins" / "python-skills", + mypy("plugins/python-skills", cache_name="python-skills"), ), Check( "Cybersecurity Skills metadata", - ("uv", "run", "scripts/validate_repo_metadata.py"), + ( + sys.executable, + "-B", + "scripts/validate_repo_metadata.py", + ), REPO_ROOT / "plugins" / "cybersecurity-skills", ), Check( "Cybersecurity Skills tests", - ("uv", "run", "pytest", "tests"), - REPO_ROOT / "plugins" / "cybersecurity-skills", + pytest("plugins/cybersecurity-skills/tests"), ), Check( "Reverse Engineering Skills metadata", - ("uv", "run", "scripts/validate_repo_metadata.py"), + ( + sys.executable, + "-B", + "scripts/validate_repo_metadata.py", + ), REPO_ROOT / "plugins" / "reverse-engineering-skills", ), Check( "Reverse Engineering Skills tests", - ("uv", "run", "pytest", "tests"), - REPO_ROOT / "plugins" / "reverse-engineering-skills", + pytest("plugins/reverse-engineering-skills/tests"), ), ) @@ -130,9 +173,9 @@ def run_check(check: Check, *, dry_run: bool) -> None: relative_cwd = check.cwd.relative_to(REPO_ROOT) print(f"\n==> {check.name}\n cwd: {relative_cwd or '.'}\n {rendered_command}") if not dry_run: - environment = None + environment = os.environ.copy() + environment["PYTHONDONTWRITEBYTECODE"] = "1" if check.cwd != REPO_ROOT: - environment = os.environ.copy() environment.pop("VIRTUAL_ENV", None) subprocess.run(check.command, cwd=check.cwd, check=True, env=environment) @@ -145,7 +188,9 @@ def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace: default="core", help="Validation breadth; defaults to the fast PR-safe core profile.", ) - parser.add_argument("--dry-run", action="store_true", help="Print checks without running them.") + parser.add_argument( + "--dry-run", action="store_true", help="Print checks without running them." + ) return parser.parse_args(argv) @@ -157,7 +202,9 @@ def main(argv: Sequence[str] | None = None) -> int: raise SystemExit(f"validate-socket: {error}") from error for check in checks: run_check(check, dry_run=args.dry_run) - print(f"\nSocket validation profile `{args.profile}` passed ({len(checks)} checks).") + print( + f"\nSocket validation profile `{args.profile}` passed ({len(checks)} checks)." + ) return 0 diff --git a/skills.sh.json b/skills.sh.json index e34e494d7..5f9e48e89 100644 --- a/skills.sh.json +++ b/skills.sh.json @@ -190,7 +190,6 @@ "github-collaboration-workflow", "maintain-github-repository", "maintain-project-agents", - "maintain-project-api", "maintain-project-contributing", "maintain-project-docs", "maintain-project-readme", diff --git a/skills/bootstrap-skills-plugin-repo/SKILL.md b/skills/bootstrap-skills-plugin-repo/SKILL.md index 85a0a583e..abcbd55ca 100644 --- a/skills/bootstrap-skills-plugin-repo/SKILL.md +++ b/skills/bootstrap-skills-plugin-repo/SKILL.md @@ -52,7 +52,9 @@ When creating or aligning skills that can benefit from parallel support work, ad - Apply-mode or implementation edits should stay in the main thread unless the user explicitly asks for parallel implementation and each worker has a disjoint write scope. - Plugin-specific guidance can be stricter. For example, Codex Security repository-wide scans may require asking for subagent use because the scan quality depends on parallel file-pass review. -Do not add subagent guidance to every skill by default. Use `docs/maintainers/codex-subagent-skill-guidance.md` to decide whether the target skill has real parallelizable support work. +Do not add subagent guidance to every skill by default. Use +`references/codex-subagent-skill-guidance.md` to decide whether the target skill +has real parallelizable support work. ## Codex Install Guidance diff --git a/skills/bootstrap-skills-plugin-repo/references/codex-subagent-skill-guidance.md b/skills/bootstrap-skills-plugin-repo/references/codex-subagent-skill-guidance.md new file mode 100644 index 000000000..1ead0f734 --- /dev/null +++ b/skills/bootstrap-skills-plugin-repo/references/codex-subagent-skill-guidance.md @@ -0,0 +1,62 @@ +# Codex Subagent Skill Guidance + +Use this reference when bootstrapping or auditing guidance about Codex subagents +in skills-export and plugin-export repositories. It does not replace OpenAI's +Codex documentation; it records the narrow Socket house pattern for optional +subagent guidance. + +Date checked: 2026-07-19. + +## Official Model + +- Codex calls delegated agents `subagents` and their coordinated use a + `subagent workflow`. +- Subagent workflows require an explicit trigger: the user asks for subagents + or parallel work, or narrower workflow guidance asks first and the user grants + permission. +- Built-in roles include `default`, `worker`, and `explorer`. Project-scoped + custom roles belong under `.codex/agents/` only when the repository + intentionally owns that configuration. +- Bounded read-heavy discovery, tests, triage, log analysis, documentation + lookup, and summarization are the normal fit. +- Parallel writes require disjoint ownership because shared edits create merge + conflicts and coordination overhead. + +## What Skills Should Say + +Add a `Codex Subagent Fit` section only when the workflow has independently +useful support work. Good candidates include documentation verification, +metadata or packaging audits, broad codebase exploration, test or CI triage, +and migration checks with separate evidence surfaces. + +Avoid subagent guidance for narrow single-file changes, one sequential command, +workflows where each output determines the next input, or tightly bounded write +targets with no independent discovery phase. + +When subagent guidance is present, require: + +- bounded, independently useful jobs; +- concise findings, evidence, links, or file references instead of raw logs; +- main-thread ownership of apply-mode edits unless the user explicitly requests + parallel implementation with disjoint write scopes; +- local model choices only when a repository intentionally owns them, without + turning one role's choice into a global rule; and +- stricter plugin-specific policy when the owning workflow requires it. + +## Review Checklist + +Flag guidance that: + +- implies Codex delegates automatically; +- recommends delegation merely because work is long or complex; +- recommends parallel writes without separate ownership; +- hides token, latency, or coordination costs; +- requests raw exploratory dumps instead of distilled findings; or +- uses vague `multi-agent` wording where current Codex documentation uses + `subagent`. + +## Official References + +- [OpenAI Codex Subagents](https://developers.openai.com/codex/subagents) +- [OpenAI Codex Subagent concepts](https://developers.openai.com/codex/concepts/subagents) +- [OpenAI Codex subagent model guidance](https://learn.chatgpt.com/docs/agent-configuration/subagents#choosing-models-and-reasoning) diff --git a/skills/maintain-project-api/SKILL.md b/skills/maintain-project-api/SKILL.md deleted file mode 100644 index 1c55196c5..000000000 --- a/skills/maintain-project-api/SKILL.md +++ /dev/null @@ -1,89 +0,0 @@ ---- -name: maintain-project-api -description: Maintain canonical API.md files with deterministic audit and bounded apply modes. Use for API surface, authentication, schemas, errors, versioning, compatibility, verification, support guidance, normalization, or targeted fixes. ---- - -# Maintain Project API - -Maintain canonical `API.md` files through one deterministic API-reference workflow. - -This skill is the default baseline path for `API.md` maintenance across most repositories. Reach for a narrower plugin only when the target repo has a specialized shape that deserves its own maintainer contract, such as a skills-export or plugin-export repository. - -## Inputs - -- Required: `--project-root ` -- Required: `--run-mode ` -- Optional: `--api-path ` -- Optional: `--config ` - -## Workflow - -1. Validate the project root and resolve the target `API.md`. -2. Load the canonical API-reference schema from `config/api-customization.template.yaml`, then merge any explicit override or project-local customization file. -3. In `check-only`, audit the required section schema, required subsection schema, required table of contents, placeholder content, and verification-command formatting. -4. In `apply`, keep edits bounded to the target `API.md` while creating a missing file from the bundled template and normalizing the document to the canonical structure. -5. Re-run the same audit to confirm post-fix status. - -## Canonical Base Contract - -The source of truth for the base API-reference contract lives in: - -- `config/api-customization.template.yaml` -- `assets/API.template.md` - -The base contract requires: - -- a top-level title and short summary -- a required `## Table of Contents` -- canonical top-level sections for overview, API surface, authentication and access, requests and responses, errors, versioning and compatibility, local development and verification, and support ownership -- required subsection structure for the sections that need stable consumer-facing detail - -## Writing Expectations - -- `Overview > Who This API Is For` should name the real consumers: humans, services, tools, packages, apps, or maintainers. -- `Overview > Stability Status` should say whether the API is stable, experimental, internal, deprecated, or otherwise constrained. -- `API Surface` should list concrete entry points and the protocol, transport, runtime, package interface, or file format used to reach each one. -- `Authentication and Access` should describe credentials and permissions without inventing secrets, scopes, roles, or access paths. -- `Requests and Responses` should document grounded request inputs, response outputs, and data models. -- `Errors` should make failures actionable by documenting the error shape and common failure modes with likely causes or next checks. -- `Versioning and Compatibility` should make the supported compatibility window and breaking-change handling explicit. -- `Local Development and Verification > Verification` should prefer fenced commands or reproducible call examples with language info strings. -- `Support and Ownership` should name only grounded teams, maintainers, issue trackers, services, or escalation paths. - -## Codex Subagent Fit - -When delegation is explicitly requested or authorized, follow `agent-engineering-skills:orchestrate-agent-work`. This skill is a good fit for read-heavy API evidence gathering before the main workflow edits or reports: inventorying endpoints or public symbols, checking examples against tests, reading schema definitions, or comparing compatibility claims against release notes. - -Keep `apply` edits in the main thread because this skill owns one target `API.md` file and must avoid invented endpoints, schemas, credentials, or support promises. Ask workers for concise evidence and file references instead of replacement API prose. - -## Output Contract - -- Return Markdown plus JSON with: - - `run_context` - - `schema_contract` - - `schema_violations` - - `command_integrity_issues` - - `content_quality_issues` - - `fixes_applied` - - `post_fix_status` - - `errors` -- If there are no issues and no errors, output exactly `No findings.` - -## Guardrails - -- Never auto-commit, auto-push, or open a PR. -- Never invent commands, endpoints, protocols, schemas, environment variables, credentials, permissions, version guarantees, support paths, or compatibility promises that are not grounded in the repo. -- Never edit files other than the target `API.md`. -- Keep `API.md` as the canonical API-reference filename for this skill. -- Treat this skill as a hard-enforced base template. Downstream plugins may specialize the schema, but the base skill should not do repo-profile inference. - -## References - -- `agents/openai.yaml` -- `references/section-schema.md` -- `references/output-contract.md` -- `references/fix-policies.md` -- `references/style-rules.md` -- `references/api-customization.md` -- `references/api-config-schema.md` -- `references/project-api-maintenance-automation-prompts.md` diff --git a/skills/maintain-project-api/agents/openai.yaml b/skills/maintain-project-api/agents/openai.yaml deleted file mode 100644 index 11fc52cf0..000000000 --- a/skills/maintain-project-api/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Maintain Project API" - short_description: "Audit and apply bounded fixes to canonical API.md files." - default_prompt: "Use $maintain-project-api for an ordinary software-project API.md. Load the template-backed API-reference schema first, enforce the required table of contents and canonical section structure, return audit findings in check-only mode, and in apply mode keep edits bounded to grounded API surface, request, response, error, versioning, verification, and support guidance only." diff --git a/skills/maintain-project-api/assets/API.template.md b/skills/maintain-project-api/assets/API.template.md deleted file mode 100644 index dca843001..000000000 --- a/skills/maintain-project-api/assets/API.template.md +++ /dev/null @@ -1,83 +0,0 @@ -# API Reference for {{PROJECT_NAME}} - -Use this reference to understand the public API surface, how to call it, what it returns, and how to verify behavior locally. - -## Table of Contents - -## Overview - -### Who This API Is For - -Explain which humans, services, tools, packages, or apps are expected to consume this API. - -### Stability Status - -State whether the API is stable, experimental, internal, deprecated, or otherwise constrained. - -## API Surface - -### Entry Points - -List the public routes, commands, functions, resources, files, modules, or other callable entry points that belong in this API contract. - -### Protocols and Transports - -Document the concrete protocol, transport, runtime, package interface, or file format used by each entry point. - -## Authentication and Access - -### Credentials - -Document the tokens, secrets, sessions, certificates, local permissions, or unauthenticated access model needed to call the API. - -### Permissions - -Document the scopes, roles, local capabilities, repository permissions, or service privileges required for successful calls. - -## Requests and Responses - -### Request Shape - -Describe the required request fields, parameters, headers, body structure, input files, command arguments, or function inputs. - -### Response Shape - -Describe the returned fields, status values, output files, generated artifacts, events, or function return values. - -### Data Models - -Define the important payload objects, records, enums, resource identifiers, schemas, and persistence-backed structures that API consumers need to understand. - -## Errors - -### Error Shape - -Document the structured error format, exit-code behavior, thrown error types, HTTP status mapping, log surface, or diagnostic output API consumers will see. - -### Common Failure Modes - -Call out the common, actionable failure modes and the most likely cause or next diagnostic step for each one. - -## Versioning and Compatibility - -### Supported Versions - -State the supported API versions, package versions, protocol versions, endpoint revisions, or documented compatibility window. - -### Breaking Changes - -Explain how breaking changes are announced, documented, migrated, or intentionally not supported. - -## Local Development and Verification - -### Runtime Configuration - -Document the concrete local config files, environment variables, feature flags, local services, fixtures, or seed data needed to exercise the API. - -### Verification - -Prefer grounded commands or reproducible call examples with fenced code blocks and language info strings when examples help API consumers verify behavior. - -## Support and Ownership - -Name the team, maintainer, service, issue tracker, or documented escalation path that API consumers should use when the API contract is unclear or broken. diff --git a/skills/maintain-project-api/config/api-customization.template.yaml b/skills/maintain-project-api/config/api-customization.template.yaml deleted file mode 100644 index f12b5c397..000000000 --- a/skills/maintain-project-api/config/api-customization.template.yaml +++ /dev/null @@ -1,154 +0,0 @@ -schemaVersion: 1 -isCustomized: false -profile: base -settings: - preservePreamble: true - allowAdditionalSections: true - requiredSections: - - Overview - - API Surface - - Authentication and Access - - Requests and Responses - - Errors - - Versioning and Compatibility - - Local Development and Verification - - Support and Ownership - sectionOrder: - - Overview - - API Surface - - Authentication and Access - - Requests and Responses - - Errors - - Versioning and Compatibility - - Local Development and Verification - - Support and Ownership - requiredSubsections: - Overview: - - Who This API Is For - - Stability Status - API Surface: - - Entry Points - - Protocols and Transports - Authentication and Access: - - Credentials - - Permissions - Requests and Responses: - - Request Shape - - Response Shape - - Data Models - Errors: - - Error Shape - - Common Failure Modes - Versioning and Compatibility: - - Supported Versions - - Breaking Changes - Local Development and Verification: - - Runtime Configuration - - Verification - sectionAliases: - API Surface: - - Endpoints - - Public API - - Interface - Authentication and Access: - - Authentication - - Authorization - - Access - Requests and Responses: - - Payloads - - Schemas - - Request and Response Shapes - Errors: - - Error Handling - Versioning and Compatibility: - - Compatibility - - Versioning - Local Development and Verification: - - Local Setup - - Development - - Verification - Support and Ownership: - - Support - - Ownership - subsectionAliases: - Overview/Who This API Is For: - - Audience - - Consumers - Overview/Stability Status: - - Stability - - Status - API Surface/Entry Points: - - Routes - - Endpoints - API Surface/Protocols and Transports: - - Protocols - - Transports - Authentication and Access/Credentials: - - Authentication - - Secrets - Authentication and Access/Permissions: - - Authorization - - Scopes - Requests and Responses/Request Shape: - - Requests - - Request Format - Requests and Responses/Response Shape: - - Responses - - Response Format - Requests and Responses/Data Models: - - Schemas - - Models - Errors/Error Shape: - - Error Format - Errors/Common Failure Modes: - - Failure Modes - - Common Errors - Versioning and Compatibility/Supported Versions: - - Versions - - Supported API Versions - Versioning and Compatibility/Breaking Changes: - - Compatibility Notes - - Migration Notes - Local Development and Verification/Runtime Configuration: - - Runtime Config - - Configuration - Local Development and Verification/Verification: - - Validation - sectionTemplates: - Support and Ownership: | - Name the team, maintainer, service, issue tracker, or documented escalation path that API consumers should use when the API contract is unclear or broken. - subsectionTemplates: - Overview/Who This API Is For: | - Explain which humans, services, tools, packages, or apps are expected to consume this API. - Overview/Stability Status: | - State whether the API is stable, experimental, internal, deprecated, or otherwise constrained. - API Surface/Entry Points: | - List the public routes, commands, functions, resources, files, modules, or other callable entry points that belong in this API contract. - API Surface/Protocols and Transports: | - Document the concrete protocol, transport, runtime, package interface, or file format used by each entry point. - Authentication and Access/Credentials: | - Document the tokens, secrets, sessions, certificates, local permissions, or unauthenticated access model needed to call the API. - Authentication and Access/Permissions: | - Document the scopes, roles, local capabilities, repository permissions, or service privileges required for successful calls. - Requests and Responses/Request Shape: | - Describe the required request fields, parameters, headers, body structure, input files, command arguments, or function inputs. - Requests and Responses/Response Shape: | - Describe the returned fields, status values, output files, generated artifacts, events, or function return values. - Requests and Responses/Data Models: | - Define the important payload objects, records, enums, resource identifiers, schemas, and persistence-backed structures that API consumers need to understand. - Errors/Error Shape: | - Document the structured error format, exit-code behavior, thrown error types, HTTP status mapping, log surface, or diagnostic output API consumers will see. - Errors/Common Failure Modes: | - Call out the common, actionable failure modes and the most likely cause or next diagnostic step for each one. - Versioning and Compatibility/Supported Versions: | - State the supported API versions, package versions, protocol versions, endpoint revisions, or documented compatibility window. - Versioning and Compatibility/Breaking Changes: | - Explain how breaking changes are announced, documented, migrated, or intentionally not supported. - Local Development and Verification/Runtime Configuration: | - Document the concrete local config files, environment variables, feature flags, local services, fixtures, or seed data needed to exercise the API. - Local Development and Verification/Verification: | - ```bash - # Replace this with the grounded validation commands for the project. - ``` - - Prefer grounded commands or reproducible call examples with fenced code blocks and language info strings when examples help API consumers verify behavior. diff --git a/skills/maintain-project-api/references/api-config-schema.md b/skills/maintain-project-api/references/api-config-schema.md deleted file mode 100644 index d6b2b3d2b..000000000 --- a/skills/maintain-project-api/references/api-config-schema.md +++ /dev/null @@ -1,28 +0,0 @@ -# API Config Schema - -The API-reference configuration file is YAML and uses this top-level shape: - -- `schemaVersion` -- `isCustomized` -- `profile` -- `settings` - -The `settings` map supports: - -- `preservePreamble` -- `allowAdditionalSections` -- `requiredSections` -- `sectionOrder` -- `requiredSubsections` -- `sectionAliases` -- `subsectionAliases` -- `sectionTemplates` -- `subsectionTemplates` - -Practical rules: - -- `requiredSections` defines the canonical top-level sections, excluding the title, summary, and `Table of Contents`. -- `sectionOrder` defines the enforced top-level ordering. -- `requiredSubsections` defines required `###` headings under specific `##` sections. -- `sectionAliases` and `subsectionAliases` allow bounded migration from older heading names into canonical names. -- `sectionTemplates` and `subsectionTemplates` provide the base scaffolding used during apply mode when content is missing. diff --git a/skills/maintain-project-api/references/api-customization.md b/skills/maintain-project-api/references/api-customization.md deleted file mode 100644 index 30a2345d6..000000000 --- a/skills/maintain-project-api/references/api-customization.md +++ /dev/null @@ -1,19 +0,0 @@ -# API Customization - -The base API-reference contract is defined in `../config/api-customization.template.yaml`. - -Downstream specializations may customize: - -- canonical section order -- required sections -- required subsection structure -- section and subsection aliases -- section and subsection scaffold text - -The base skill always requires: - -- a top-level title and short summary -- a `Table of Contents` -- deterministic normalization to the configured schema - -Use a project-local `config/api-customization.yaml` or `--config ` override when a downstream plugin needs a narrower or expanded API reference structure. diff --git a/skills/maintain-project-api/references/fix-policies.md b/skills/maintain-project-api/references/fix-policies.md deleted file mode 100644 index b6635f380..000000000 --- a/skills/maintain-project-api/references/fix-policies.md +++ /dev/null @@ -1,8 +0,0 @@ -# Fix Policies - -- Keep edits bounded to the target `API.md`. -- Preserve existing section bodies whenever they already satisfy the schema. -- Create a missing `API.md` from the bundled template. -- Normalize canonical headings and subsection headings to the configured names and order. -- Add missing sections or subsections from the configured templates instead of inventing repo-fiction. -- Do not synthesize endpoints, field names, schemas, environment variable names, service names, credential names, permissions, version guarantees, support paths, or compatibility promises. diff --git a/skills/maintain-project-api/references/output-contract.md b/skills/maintain-project-api/references/output-contract.md deleted file mode 100644 index 5716d410b..000000000 --- a/skills/maintain-project-api/references/output-contract.md +++ /dev/null @@ -1,16 +0,0 @@ -# Output Contract - -Return Markdown plus JSON with: - -- `run_context` -- `schema_contract` -- `schema_violations` -- `command_integrity_issues` -- `content_quality_issues` -- `fixes_applied` -- `post_fix_status` -- `errors` - -Clean-run rule: - -- Output exactly `No findings.` only when there are no remaining issues and no errors. diff --git a/skills/maintain-project-api/references/project-api-maintenance-automation-prompts.md b/skills/maintain-project-api/references/project-api-maintenance-automation-prompts.md deleted file mode 100644 index ec7e68fca..000000000 --- a/skills/maintain-project-api/references/project-api-maintenance-automation-prompts.md +++ /dev/null @@ -1,18 +0,0 @@ -# Automation Prompts - -Use these prompts when validating or applying the skill through automation. - -## Check-only - -- Audit `API.md` for the canonical section order. -- Confirm the required table of contents is present and matches the canonical top-level headings. -- Confirm sections with required subsections contain the configured `###` headings. -- Flag placeholder content and malformed or weak verification command blocks. - -## Apply - -- Create a missing `API.md` from the bundled template. -- Normalize `API.md` to the canonical section order. -- Preserve existing API reference guidance where it already matches the schema. -- Add missing sections or subsections from the configured templates only. -- Keep all edits bounded to `API.md`. diff --git a/skills/maintain-project-api/references/section-schema.md b/skills/maintain-project-api/references/section-schema.md deleted file mode 100644 index 07de4c760..000000000 --- a/skills/maintain-project-api/references/section-schema.md +++ /dev/null @@ -1,52 +0,0 @@ -# Section Schema - -The canonical base `API.md` structure is defined by: - -- `../config/api-customization.template.yaml` -- `../assets/API.template.md` - -Base top-level shape: - -1. top-level title -2. short API-consumer-facing summary -3. `## Table of Contents` -4. `## Overview` -5. `## API Surface` -6. `## Authentication and Access` -7. `## Requests and Responses` -8. `## Errors` -9. `## Versioning and Compatibility` -10. `## Local Development and Verification` -11. `## Support and Ownership` - -Required subsection shape: - -- `Overview` - - `Who This API Is For` - - `Stability Status` -- `API Surface` - - `Entry Points` - - `Protocols and Transports` -- `Authentication and Access` - - `Credentials` - - `Permissions` -- `Requests and Responses` - - `Request Shape` - - `Response Shape` - - `Data Models` -- `Errors` - - `Error Shape` - - `Common Failure Modes` -- `Versioning and Compatibility` - - `Supported Versions` - - `Breaking Changes` -- `Local Development and Verification` - - `Runtime Configuration` - - `Verification` - -Schema expectations: - -- Use `##` headings for top-level sections. -- Use `###` headings for required subsections. -- Always include a top-level `Table of Contents`. -- Preserve additional repo-specific sections when present, but keep canonical sections in canonical order. diff --git a/skills/maintain-project-api/references/style-rules.md b/skills/maintain-project-api/references/style-rules.md deleted file mode 100644 index 1cf8e9510..000000000 --- a/skills/maintain-project-api/references/style-rules.md +++ /dev/null @@ -1,10 +0,0 @@ -# Style Rules - -- Keep API reference guidance direct, concrete, and repo-grounded. -- Favor short explanatory paragraphs plus small bullet lists when they improve scanability. -- Treat `API.md` as a public-interface contract, not as a product overview, implementation narrative, or contributor guide. -- Keep `API Surface` concrete enough that a consumer can find the callable entry points without reverse-engineering the repo. -- Keep `Requests and Responses` grounded in real fields, arguments, files, commands, return values, or generated artifacts. -- Keep `Errors` actionable: explain what the caller sees and the most likely next diagnostic step. -- Keep `Local Development and Verification` operational and practical, with reproducible commands or call examples when they are known. -- Prefer fenced code blocks with language info strings when showing verification commands or API call examples. diff --git a/skills/maintain-project-api/scripts/maintain_project_api.py b/skills/maintain-project-api/scripts/maintain_project_api.py deleted file mode 100644 index fef84c5d9..000000000 --- a/skills/maintain-project-api/scripts/maintain_project_api.py +++ /dev/null @@ -1,823 +0,0 @@ -#!/usr/bin/env -S uv run --script -# /// script -# requires-python = ">=3.9" -# dependencies = [ -# "PyYAML>=6.0.2,<7", -# ] -# /// -"""Audit and apply bounded API.md maintenance from a hard-enforced schema.""" - -from __future__ import annotations - -import argparse -import json -import re -import sys -from dataclasses import dataclass -from datetime import datetime, timezone -from pathlib import Path -from typing import Any, Dict, List, Optional, Sequence, Tuple - -import yaml - -H2_RE = re.compile(r"^##\s+(.+?)\s*$", re.MULTILINE) -H3_RE = re.compile(r"^###\s+(.+?)\s*$", re.MULTILINE) -SHELL_FENCE_RE = re.compile(r"```([^\n`]*)\n(.*?)```", re.DOTALL) -PLACEHOLDER_PATTERNS = [ - re.compile(r"\bTODO\b", re.IGNORECASE), - re.compile(r"\bTBD\b", re.IGNORECASE), - re.compile(r"<[^>]+>"), -] - - -@dataclass -class Issue: - issue_id: str - category: str - severity: str - file: str - evidence: str - recommended_fix: str - auto_fixable: bool - fixed: bool = False - - def to_dict(self) -> Dict[str, Any]: - return { - "issue_id": self.issue_id, - "category": self.category, - "severity": self.severity, - "file": self.file, - "evidence": self.evidence, - "recommended_fix": self.recommended_fix, - "auto_fixable": self.auto_fixable, - "fixed": self.fixed, - } - - -def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser( - description="Audit and optionally apply bounded API.md maintenance from a hard-enforced schema." - ) - parser.add_argument("--project-root", required=True, help="Absolute project root path") - parser.add_argument("--api-path", help="Optional API path override") - parser.add_argument("--run-mode", required=True, choices=["check-only", "apply"], help="Execution mode") - parser.add_argument("--config", help="Optional API config override") - parser.add_argument("--json-out", help="Write JSON report path") - parser.add_argument("--md-out", help="Write markdown report path") - parser.add_argument("--print-json", action="store_true", help="Print JSON report") - parser.add_argument("--print-md", action="store_true", help="Print markdown report") - parser.add_argument("--fail-on-issues", action="store_true", help="Exit non-zero when unresolved issues remain") - return parser.parse_args() - - -def read_text(path: Path) -> str: - try: - return path.read_text(encoding="utf-8") - except UnicodeDecodeError: - return path.read_text(encoding="utf-8", errors="ignore") - - -def write_text(path: Path, content: str) -> None: - path.write_text(content, encoding="utf-8") - - -def normalize_whitespace(text: str) -> str: - return text.strip() + "\n" - - -def slugify_heading(heading: str) -> str: - slug = heading.lower().strip() - slug = re.sub(r"[^\w\s-]", "", slug) - slug = re.sub(r"\s+", "-", slug) - slug = re.sub(r"-{2,}", "-", slug) - return slug - - -def split_sections(text: str) -> Tuple[str, List[Tuple[str, str]]]: - matches = list(H2_RE.finditer(text)) - if not matches: - return text.strip(), [] - - preamble = text[: matches[0].start()].rstrip() - sections: List[Tuple[str, str]] = [] - for idx, match in enumerate(matches): - start = match.end() - end = matches[idx + 1].start() if idx + 1 < len(matches) else len(text) - sections.append((match.group(1).strip(), text[start:end].strip("\n"))) - return preamble, sections - - -def split_subsections(body: str) -> Tuple[str, List[Tuple[str, str]]]: - matches = list(H3_RE.finditer(body)) - if not matches: - return body.strip(), [] - - preamble = body[: matches[0].start()].strip() - subsections: List[Tuple[str, str]] = [] - for idx, match in enumerate(matches): - start = match.end() - end = matches[idx + 1].start() if idx + 1 < len(matches) else len(body) - subsections.append((match.group(1).strip(), body[start:end].strip("\n"))) - return preamble, subsections - - -def section_map(sections: Sequence[Tuple[str, str]]) -> Dict[str, str]: - return {heading: body for heading, body in sections} - - -def parse_title_and_summary(preamble: str) -> Tuple[Optional[str], Optional[str], List[str]]: - lines = [line.rstrip() for line in preamble.splitlines()] - title: Optional[str] = None - summary: Optional[str] = None - extras: List[str] = [] - title_index: Optional[int] = None - summary_index: Optional[int] = None - - for idx, line in enumerate(lines): - if line.startswith("# "): - title = line[2:].strip() - title_index = idx - break - - if title_index is None: - return None, None, lines - - for idx in range(title_index + 1, len(lines)): - if lines[idx].strip(): - summary = lines[idx].strip() - summary_index = idx - break - - for idx, line in enumerate(lines): - if idx in {title_index, summary_index}: - continue - extras.append(line) - - return title, summary, extras - - -def collapse_blank_lines(lines: Sequence[str]) -> List[str]: - collapsed: List[str] = [] - previous_blank = False - for line in lines: - blank = line.strip() == "" - if blank and previous_blank: - continue - collapsed.append(line) - previous_blank = blank - while collapsed and collapsed[0].strip() == "": - collapsed.pop(0) - while collapsed and collapsed[-1].strip() == "": - collapsed.pop() - return collapsed - - -def normalize_preamble(preamble: str, project_name: str, preserve_preamble: bool) -> str: - title, summary, extras = parse_title_and_summary(preamble) - normalized_title = title or f"API Reference for {project_name}" - normalized_summary = ( - summary - or "Use this reference to understand the public API surface, how to call it, what it returns, and how to verify behavior locally." - ) - lines = [f"# {normalized_title}", "", normalized_summary] - if preserve_preamble: - extra_lines = collapse_blank_lines(extras) - if extra_lines: - lines.extend(["", *extra_lines]) - return "\n".join(lines).strip() - - -def read_yaml(path: Path) -> Dict[str, Any]: - data = yaml.safe_load(read_text(path)) - return data if isinstance(data, dict) else {} - - -def deep_merge(base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]: - merged: Dict[str, Any] = dict(base) - for key, value in override.items(): - if isinstance(value, dict) and isinstance(merged.get(key), dict): - merged[key] = deep_merge(merged[key], value) # type: ignore[arg-type] - else: - merged[key] = value - return merged - - -def load_config(project_root: Path, config_override: Optional[str]) -> Dict[str, Any]: - default_path = Path(__file__).resolve().parents[1] / "config" / "api-customization.template.yaml" - default_config = read_yaml(default_path) - loaded_path = default_path - - if config_override: - override_path = Path(config_override).expanduser().resolve() - merged = deep_merge(default_config, read_yaml(override_path)) - merged["isCustomized"] = True - merged["configPath"] = str(override_path) - merged["defaultConfigPath"] = str(default_path) - return merged - - project_config = project_root / "config" / "api-customization.yaml" - if project_config.is_file(): - loaded_path = project_config - merged = deep_merge(default_config, read_yaml(project_config)) - merged["isCustomized"] = True - else: - merged = dict(default_config) - merged["isCustomized"] = bool(default_config.get("isCustomized", False)) - - merged["configPath"] = str(loaded_path) - merged["defaultConfigPath"] = str(default_path) - return merged - - -def config_settings(config: Dict[str, Any]) -> Dict[str, Any]: - settings = config.get("settings", {}) - return settings if isinstance(settings, dict) else {} - - -def required_sections(settings: Dict[str, Any]) -> List[str]: - values = settings.get("requiredSections", []) - return [str(item) for item in values] if isinstance(values, list) else [] - - -def canonical_order(settings: Dict[str, Any]) -> List[str]: - values = settings.get("sectionOrder", []) - return [str(item) for item in values] if isinstance(values, list) else [] - - -def required_subsections(settings: Dict[str, Any]) -> Dict[str, List[str]]: - raw = settings.get("requiredSubsections", {}) - if not isinstance(raw, dict): - return {} - return { - str(key): [str(item) for item in value] - for key, value in raw.items() - if isinstance(value, list) - } - - -def section_aliases(settings: Dict[str, Any]) -> Dict[str, List[str]]: - raw = settings.get("sectionAliases", {}) - if not isinstance(raw, dict): - return {} - return { - str(key): [str(item) for item in value] - for key, value in raw.items() - if isinstance(value, list) - } - - -def subsection_aliases(settings: Dict[str, Any]) -> Dict[str, List[str]]: - raw = settings.get("subsectionAliases", {}) - if not isinstance(raw, dict): - return {} - return { - str(key): [str(item) for item in value] - for key, value in raw.items() - if isinstance(value, list) - } - - -def section_templates(settings: Dict[str, Any]) -> Dict[str, str]: - raw = settings.get("sectionTemplates", {}) - if not isinstance(raw, dict): - return {} - return {str(key): str(value).strip() for key, value in raw.items()} - - -def subsection_templates(settings: Dict[str, Any]) -> Dict[str, str]: - raw = settings.get("subsectionTemplates", {}) - if not isinstance(raw, dict): - return {} - return {str(key): str(value).strip() for key, value in raw.items()} - - -def build_toc(headings: Sequence[str]) -> str: - return "\n".join(f"- [{heading}](#{slugify_heading(heading)})" for heading in headings) - - -def toc_entries(body: str) -> List[str]: - entries: List[str] = [] - for line in body.splitlines(): - match = re.match(r"^\s*-\s+\[(.+?)\]\(#(.+?)\)\s*$", line.strip()) - if match: - entries.append(match.group(1).strip()) - return entries - - -def section_alias_lookup(settings: Dict[str, Any]) -> Dict[str, str]: - reverse: Dict[str, str] = {} - for canonical, aliases in section_aliases(settings).items(): - for alias in aliases: - reverse[alias] = canonical - return reverse - - -def subsection_alias_lookup(settings: Dict[str, Any]) -> Dict[Tuple[str, str], str]: - reverse: Dict[Tuple[str, str], str] = {} - for canonical_path, aliases in subsection_aliases(settings).items(): - if "/" not in canonical_path: - continue - parent, canonical_name = canonical_path.split("/", 1) - for alias in aliases: - reverse[(parent, alias)] = canonical_name - return reverse - - -def render_template_bootstrap(project_root: Path) -> str: - template_path = Path(__file__).resolve().parents[1] / "assets" / "API.template.md" - template = read_text(template_path) - return normalize_whitespace(template.replace("{{PROJECT_NAME}}", project_root.name)) - - -def render_section_body(heading: str, existing_body: str, settings: Dict[str, Any]) -> str: - required_children = required_subsections(settings).get(heading, []) - section_template_map = section_templates(settings) - subsection_template_map = subsection_templates(settings) - subsection_alias_map = subsection_alias_lookup(settings) - - if not required_children: - return existing_body.strip() or section_template_map.get(heading, "") - - preamble, subsections = split_subsections(existing_body) - subsection_lookup: Dict[str, str] = {} - extra_subsections: List[Tuple[str, str]] = [] - for name, body in subsections: - canonical_name = subsection_alias_map.get((heading, name), name) - if canonical_name in required_children and canonical_name not in subsection_lookup: - subsection_lookup[canonical_name] = body - else: - extra_subsections.append((name, body)) - - lines: List[str] = [] - if preamble.strip(): - lines.extend([preamble.strip(), ""]) - - for idx, child in enumerate(required_children): - child_body = subsection_lookup.get(child, "").strip() or subsection_template_map.get(f"{heading}/{child}", "") - lines.extend([f"### {child}", "", child_body.strip()]) - if idx < len(required_children) - 1 or extra_subsections: - lines.append("") - - for idx, (name, body) in enumerate(extra_subsections): - lines.extend([f"### {name}", "", body.strip()]) - if idx < len(extra_subsections) - 1: - lines.append("") - - rendered = "\n".join(lines).strip() - return rendered or section_template_map.get(heading, "") - - -def validate_schema( - api_path: Path, - api_text: str, - config: Dict[str, Any], -) -> Tuple[List[Issue], List[Issue], List[Issue], List[Tuple[str, str]]]: - settings = config_settings(config) - required = required_sections(settings) - order = canonical_order(settings) - subsection_map = required_subsections(settings) - section_alias_map = section_alias_lookup(settings) - - schema_issues: List[Issue] = [] - command_issues: List[Issue] = [] - content_issues: List[Issue] = [] - - preamble, sections = split_sections(api_text) - lookup = section_map(sections) - title, summary, _extras = parse_title_and_summary(preamble) - - if not title: - schema_issues.append( - Issue( - issue_id="missing-title", - category="schema", - severity="high", - file=str(api_path), - evidence="API.md is missing a top-level '# API Reference for ' title.", - recommended_fix="Add a clear top-level API title.", - auto_fixable=True, - ) - ) - if not summary: - schema_issues.append( - Issue( - issue_id="missing-summary", - category="schema", - severity="medium", - file=str(api_path), - evidence="API.md is missing a short API consumer-facing summary beneath the title.", - recommended_fix="Add a short summary sentence beneath the top-level title.", - auto_fixable=True, - ) - ) - - if "Table of Contents" not in lookup: - schema_issues.append( - Issue( - issue_id="missing-table-of-contents", - category="schema", - severity="medium", - file=str(api_path), - evidence="API.md is missing the required '## Table of Contents' section.", - recommended_fix="Add a table of contents that mirrors the canonical top-level headings.", - auto_fixable=True, - ) - ) - - observed_headings = [heading for heading, _body in sections] - canonical_positions = {heading: idx for idx, heading in enumerate(observed_headings)} - for heading in required: - if heading in lookup: - continue - alias_found = next( - (alias for alias, canonical in section_alias_map.items() if canonical == heading and alias in lookup), - None, - ) - if alias_found: - schema_issues.append( - Issue( - issue_id=f"non-canonical-heading-{slugify_heading(heading)}", - category="schema", - severity="medium", - file=str(api_path), - evidence=f"API.md uses alias heading '## {alias_found}' where the canonical schema expects '## {heading}'.", - recommended_fix=f"Rename '## {alias_found}' to '## {heading}'.", - auto_fixable=True, - ) - ) - else: - schema_issues.append( - Issue( - issue_id=f"missing-section-{slugify_heading(heading)}", - category="schema", - severity="high", - file=str(api_path), - evidence=f"API.md is missing required section '## {heading}'.", - recommended_fix=f"Add the required '## {heading}' section.", - auto_fixable=True, - ) - ) - - order_positions: List[int] = [] - for heading in order: - if heading in canonical_positions: - order_positions.append(canonical_positions[heading]) - continue - alias_found = next( - (alias for alias, canonical in section_alias_map.items() if canonical == heading and alias in canonical_positions), - None, - ) - if alias_found: - order_positions.append(canonical_positions[alias_found]) - if order_positions and order_positions != sorted(order_positions): - schema_issues.append( - Issue( - issue_id="canonical-section-order", - category="schema", - severity="medium", - file=str(api_path), - evidence="Canonical API sections are not in the configured order.", - recommended_fix="Normalize the top-level sections into canonical order.", - auto_fixable=True, - ) - ) - - subsection_alias_map = subsection_alias_lookup(settings) - for parent, children in subsection_map.items(): - body = lookup.get(parent, "") - if not body: - alias_parent = next( - (alias for alias, canonical in section_alias_map.items() if canonical == parent and alias in lookup), - None, - ) - body = lookup.get(alias_parent, "") if alias_parent else "" - if not body: - continue - _preamble, subsections = split_subsections(body) - found = { - subsection_alias_map.get((parent, name), name): subsection_body - for name, subsection_body in subsections - } - for child in children: - if child not in found: - schema_issues.append( - Issue( - issue_id=f"missing-subsection-{slugify_heading(parent)}-{slugify_heading(child)}", - category="schema", - severity="high", - file=str(api_path), - evidence=f"Section '## {parent}' is missing required subsection '### {child}'.", - recommended_fix=f"Add the required subsection '### {child}' under '## {parent}'.", - auto_fixable=True, - ) - ) - - if "Table of Contents" in lookup: - expected_toc = [heading for heading in order if heading in required or heading in lookup] - actual_toc = toc_entries(lookup["Table of Contents"]) - if actual_toc != expected_toc: - schema_issues.append( - Issue( - issue_id="stale-table-of-contents", - category="schema", - severity="low", - file=str(api_path), - evidence="Table of contents entries do not match the canonical top-level section headings in order.", - recommended_fix="Regenerate the table of contents from the canonical section list.", - auto_fixable=True, - ) - ) - - for heading in required: - required_body = lookup.get(heading) - if not required_body: - continue - if not required_body.strip(): - schema_issues.append( - Issue( - issue_id=f"empty-section-{slugify_heading(heading)}", - category="schema", - severity="medium", - file=str(api_path), - evidence=f"Section '## {heading}' is present but empty.", - recommended_fix=f"Add grounded content to '## {heading}'.", - auto_fixable=True, - ) - ) - if any(pattern.search(required_body) for pattern in PLACEHOLDER_PATTERNS): - content_issues.append( - Issue( - issue_id=f"placeholder-content-{slugify_heading(heading)}", - category="content-quality", - severity="medium", - file=str(api_path), - evidence=f"Section '## {heading}' contains placeholder-style content.", - recommended_fix="Replace placeholder content with repo-grounded API consumer guidance.", - auto_fixable=False, - ) - ) - - verification_body = lookup.get("Local Development and Verification", "") - _preamble, dev_subsections = split_subsections(verification_body) - verification_lookup = {name: body for name, body in dev_subsections} - verification_text = verification_lookup.get("Verification", "").strip() - if verification_text: - shell_blocks = list(SHELL_FENCE_RE.finditer(verification_text)) - if shell_blocks: - for match in shell_blocks: - info = match.group(1).strip() - block = match.group(2).strip() - if not info: - command_issues.append( - Issue( - issue_id=f"missing-code-fence-info-string-{match.start()}", - category="command-integrity", - severity="low", - file=str(api_path), - evidence="Verification uses a fenced code block without a language info string.", - recommended_fix="Use fenced code blocks with an info string such as ```bash for verification commands.", - auto_fixable=False, - ) - ) - if not block: - command_issues.append( - Issue( - issue_id=f"empty-shell-block-{match.start()}", - category="command-integrity", - severity="medium", - file=str(api_path), - evidence="Verification contains an empty fenced code block.", - recommended_fix="Remove the empty block or replace it with grounded validation commands.", - auto_fixable=True, - ) - ) - if any(pattern.search(block) for pattern in PLACEHOLDER_PATTERNS): - command_issues.append( - Issue( - issue_id=f"placeholder-command-block-{match.start()}", - category="command-integrity", - severity="high", - file=str(api_path), - evidence="Verification contains a placeholder command block.", - recommended_fix="Replace the placeholder command block with grounded validation commands or prose.", - auto_fixable=False, - ) - ) - elif len(verification_text.split()) < 6: - content_issues.append( - Issue( - issue_id="thin-verification-guidance", - category="content-quality", - severity="medium", - file=str(api_path), - evidence="Local Development and Verification > Verification is too thin to help API consumers verify behavior.", - recommended_fix="Add grounded validation guidance, preferably with fenced code blocks and language info strings.", - auto_fixable=False, - ) - ) - - return schema_issues, command_issues, content_issues, sections - - -def apply_fixes( - project_root: Path, - api_path: Path, - api_text: str, - config: Dict[str, Any], -) -> Tuple[str, List[Dict[str, str]]]: - fixes: List[Dict[str, str]] = [] - if not api_text.strip(): - api_text = render_template_bootstrap(project_root) - fixes.append( - { - "action": "create-api-from-template", - "file": str(api_path), - "reason": "Created a missing API.md from the bundled canonical template.", - } - ) - - settings = config_settings(config) - required = required_sections(settings) - order = canonical_order(settings) - preserve_preamble = bool(settings.get("preservePreamble", True)) - allow_additional = bool(settings.get("allowAdditionalSections", True)) - section_alias_map = section_alias_lookup(settings) - - preamble, sections = split_sections(api_text) - normalized_preamble = normalize_preamble(preamble, project_root.name, preserve_preamble) - - canonical_lookup: Dict[str, str] = {} - extra_sections: List[Tuple[str, str]] = [] - for heading, body in sections: - if heading == "Table of Contents": - continue - canonical_heading = section_alias_map.get(heading, heading) - if canonical_heading in order or canonical_heading in required: - canonical_lookup[canonical_heading] = body - elif allow_additional: - extra_sections.append((heading, body)) - - canonical_sections: List[Tuple[str, str]] = [] - for heading in order: - body = render_section_body(heading, canonical_lookup.get(heading, ""), settings).strip() - canonical_sections.append((heading, body)) - - headings_for_toc = [heading for heading, _body in canonical_sections] - if allow_additional: - headings_for_toc.extend(heading for heading, _body in extra_sections) - rendered_sections = [("Table of Contents", build_toc(headings_for_toc).strip()), *canonical_sections] - if allow_additional: - rendered_sections.extend(extra_sections) - - parts = [normalized_preamble] - for heading, body in rendered_sections: - parts.extend(["", f"## {heading}", "", body.strip()]) - document = "\n".join(parts).strip() + "\n" - fixes.append( - { - "action": "normalize-api-structure", - "file": str(api_path), - "reason": "Normalized API.md to the canonical template-backed section schema.", - } - ) - return normalize_whitespace(document), fixes - - -def format_report(report: Dict[str, Any]) -> str: - total_issues = ( - len(report["schema_violations"]) - + len(report["command_integrity_issues"]) - + len(report["content_quality_issues"]) - ) - if total_issues == 0 and not report["errors"]: - return "No findings." - - lines = [ - "# API.md Maintenance Report", - "", - f"- Target: `{report['run_context']['api_path']}`", - f"- Mode: `{report['run_context']['run_mode']}`", - f"- Config: `{report['schema_contract']['config_path']}`", - ] - - for key, title in ( - ("schema_violations", "Schema Violations"), - ("command_integrity_issues", "Command Integrity Issues"), - ("content_quality_issues", "Content Quality Issues"), - ("fixes_applied", "Fixes Applied"), - ("errors", "Errors"), - ): - items = report[key] - if not items: - continue - lines.extend(["", f"## {title}"]) - for item in items: - evidence = item.get("evidence") or item.get("reason") or item.get("message") - lines.append(f"- {item.get('issue_id', item.get('action', 'item'))}: {evidence}") - - return "\n".join(lines).strip() + "\n" - - -def run_maintenance(args: argparse.Namespace) -> Tuple[Dict[str, Any], str]: - project_root = Path(args.project_root).expanduser().resolve() - if not project_root.is_dir(): - raise ValueError(f"Project root does not exist or is not a directory: {project_root}") - - api_path = ( - Path(args.api_path).expanduser().resolve() - if args.api_path - else project_root / "API.md" - ) - config = load_config(project_root, args.config) - - errors: List[str] = [] - fixes_applied: List[Dict[str, str]] = [] - existing_text = read_text(api_path) if api_path.is_file() else "" - - if args.run_mode == "apply": - new_text, applied = apply_fixes(project_root, api_path, existing_text, config) - if not api_path.parent.exists(): - api_path.parent.mkdir(parents=True, exist_ok=True) - if normalize_whitespace(existing_text) != new_text: - write_text(api_path, new_text) - fixes_applied.extend(applied) - existing_text = new_text - - if existing_text: - schema_issues, command_issues, content_issues, _sections = validate_schema( - api_path, existing_text, config - ) - else: - schema_issues = [ - Issue( - issue_id="missing-api-file", - category="schema", - severity="high", - file=str(api_path), - evidence="API.md does not exist.", - recommended_fix="Create the canonical API.md file from the bundled template.", - auto_fixable=True, - ) - ] - command_issues = [] - content_issues = [] - - report = { - "run_context": { - "project_root": str(project_root), - "api_path": str(api_path), - "run_mode": args.run_mode, - "generated_at": datetime.now(timezone.utc).isoformat(), - }, - "schema_contract": { - "config_path": config.get("configPath"), - "default_config_path": config.get("defaultConfigPath"), - "required_table_of_contents": True, - "required_sections": required_sections(config_settings(config)), - "section_order": canonical_order(config_settings(config)), - "required_subsections": required_subsections(config_settings(config)), - }, - "schema_violations": [issue.to_dict() for issue in schema_issues], - "command_integrity_issues": [issue.to_dict() for issue in command_issues], - "content_quality_issues": [issue.to_dict() for issue in content_issues], - "fixes_applied": fixes_applied, - "post_fix_status": { - "remaining_issue_count": len(schema_issues) + len(command_issues) + len(content_issues), - "is_clean": not schema_issues and not command_issues and not content_issues and not errors, - }, - "errors": errors, - } - markdown = format_report(report) - return report, markdown - - -def main() -> int: - args = parse_args() - try: - report, markdown = run_maintenance(args) - except ValueError as exc: - print(str(exc), file=sys.stderr) - return 1 - - if args.json_out: - Path(args.json_out).write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8") - if args.md_out: - Path(args.md_out).write_text(markdown, encoding="utf-8") - if args.print_json: - print(json.dumps(report, indent=2)) - if args.print_md: - print(markdown, end="") - - has_issues = ( - bool(report["schema_violations"]) - or bool(report["command_integrity_issues"]) - or bool(report["content_quality_issues"]) - or bool(report["errors"]) - ) - if args.fail_on_issues and has_issues: - return 2 - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/skills/maintain-project-docs/SKILL.md b/skills/maintain-project-docs/SKILL.md index da0c511c0..ced89515c 100644 --- a/skills/maintain-project-docs/SKILL.md +++ b/skills/maintain-project-docs/SKILL.md @@ -1,6 +1,6 @@ --- name: maintain-project-docs -description: Coordinate maintenance across README.md, CONTRIBUTING.md, AGENTS.md, optional API.md, and ROADMAP.md. Use for a complete documentation sweep, ownership-boundary audit, repeated-content cleanup, or one coordinated project-docs pass. +description: Coordinate maintenance across README.md, CONTRIBUTING.md, AGENTS.md, and ROADMAP.md. Use for a complete documentation sweep, ownership-boundary audit, repeated-content cleanup, or one coordinated project-docs pass. --- # Maintain Project Docs @@ -13,8 +13,8 @@ This skill is the umbrella layer for repository documentation maintenance. It de - Required: `--project-root ` - Required: `--run-mode ` -- Optional: `--include ` -- Optional: `--skip ` +- Optional: `--include ` +- Optional: `--skip ` - Optional: `--collect-source-tickets` - Optional: `--collect-github-issues` - Optional: `--github-repo ` @@ -22,7 +22,8 @@ This skill is the umbrella layer for repository documentation maintenance. It de ## Workflow 1. Validate the project root and choose the included document workflows. -2. Run the owner workflows serially in this order: README, CONTRIBUTING, AGENTS, API when `API.md` exists, ROADMAP. +2. Run the owner workflows serially in this order: README, CONTRIBUTING, + AGENTS, ROADMAP. 3. Pass `--collect-source-tickets`, `--collect-github-issues`, and `--github-repo` only to the roadmap workflow. 4. In `check-only`, report each owner workflow's findings plus cross-document responsibility issues. 5. In `apply`, let each owner workflow make only its own bounded file edits, then audit the resulting document set again. @@ -33,7 +34,6 @@ This skill is the umbrella layer for repository documentation maintenance. It de - `README.md`: product overview, status, quick start, usage, and short development pointer. - `CONTRIBUTING.md`: human contributor workflow, setup, development expectations, PR expectations, and communication rules. - `AGENTS.md`: durable agent-facing repo scope, routing, commands, review and delivery rules, and safety boundaries. -- `API.md`: grounded API surface, authentication, schemas, errors, versioning, compatibility, and verification guidance. - `ROADMAP.md`: milestones, small tickets, TODO/FIXME imports, backlog planning, and notable roadmap history. When content belongs in another file, report the mismatch instead of copying or rewriting it silently. @@ -61,7 +61,8 @@ Keep `apply` edits in the main thread. The auditor may return proposed patch-set - Never auto-commit, auto-push, or open a PR. - Never invent project claims, commands, API behavior, roadmap scope, or agent policy. - Never make broad cross-file rewrites as a side effect of running the umbrella workflow. -- Do not duplicate detailed rules across README, CONTRIBUTING, AGENTS, API, and ROADMAP just to make each file self-contained. +- Do not duplicate detailed rules across README, CONTRIBUTING, AGENTS, and + ROADMAP just to make each file self-contained. - Treat this skill as an orchestrator. The owner document skills keep their own schemas, templates, and bounded apply behavior. ## References diff --git a/skills/maintain-project-docs/agents/openai.yaml b/skills/maintain-project-docs/agents/openai.yaml index 81f6ebdcd..72b3a66b9 100644 --- a/skills/maintain-project-docs/agents/openai.yaml +++ b/skills/maintain-project-docs/agents/openai.yaml @@ -1,4 +1,4 @@ interface: display_name: "Maintain Project Docs" short_description: "Run a coordinated docs sweep across README, CONTRIBUTING, AGENTS, optional API, and ROADMAP." - default_prompt: "Use $maintain-project-docs when a repository needs a complete documentation maintenance pass across README.md, CONTRIBUTING.md, AGENTS.md, optional API.md, and ROADMAP.md. Delegate each file to its owner maintenance skill, run the workflows serially, preserve the responsibility split between product docs, contributor workflow, agent guidance, API reference, and roadmap planning, and report cross-document drift instead of rewriting several files at once." + default_prompt: "Use $maintain-project-docs when a repository needs a complete documentation maintenance pass across README.md, CONTRIBUTING.md, AGENTS.md, and ROADMAP.md. Delegate each file to its owner maintenance skill, run the workflows serially, preserve the responsibility split between product docs, contributor workflow, agent guidance, and roadmap planning, and report cross-document drift instead of rewriting several files at once." diff --git a/skills/maintain-project-docs/references/document-boundaries.md b/skills/maintain-project-docs/references/document-boundaries.md index 154777898..059fe6c54 100644 --- a/skills/maintain-project-docs/references/document-boundaries.md +++ b/skills/maintain-project-docs/references/document-boundaries.md @@ -9,7 +9,6 @@ Use this reference when an audit finds repeated content or a doc section that se | `README.md` | Product identity, current status, quick start, usage, repo structure, and a short development pointer. | `CONTRIBUTING.md`, `ROADMAP.md`, relevant package or product docs. | | `CONTRIBUTING.md` | Human contributor setup, workflow, verification, PR expectations, and communication guidance. | `README.md`, repo-maintenance docs, `AGENTS.md` only when contributors need to know agent rules exist. | | `AGENTS.md` | Durable instructions for Codex and other repo agents: scope, routing, commands, workflow constraints, delivery rules, safety boundaries, and local overrides. | `README.md`, `CONTRIBUTING.md`, `ROADMAP.md`, maintainer docs. | -| `API.md` | Grounded API surface, authentication, request and response schemas, errors, versioning, compatibility, verification, and support guidance. | Implementations, schemas, tests, and release notes that prove the documented contract. | | `ROADMAP.md` | Milestones, small tickets, imported TODO/FIXME work, issue links, and notable planning history. | GitHub issues, source references, docs that explain accepted scope. | ## Drift Signals @@ -17,7 +16,6 @@ Use this reference when an audit finds repeated content or a doc section that se - README contains full branch, PR, release, or maintainer workflow instructions instead of a short pointer. - CONTRIBUTING repeats product overview or roadmap history instead of contributor workflow. - AGENTS repeats public product marketing, contributor onboarding prose, or detailed accessibility standards. -- API documentation invents behavior or repeats contributor and roadmap procedure. - ROADMAP contains long procedural guidance that should live in AGENTS, CONTRIBUTING, or maintainer docs. ## Fixing Policy diff --git a/skills/maintain-project-docs/references/project-docs-maintenance-automation-prompts.md b/skills/maintain-project-docs/references/project-docs-maintenance-automation-prompts.md index c33ad48cd..73a6bfca7 100644 --- a/skills/maintain-project-docs/references/project-docs-maintenance-automation-prompts.md +++ b/skills/maintain-project-docs/references/project-docs-maintenance-automation-prompts.md @@ -4,7 +4,11 @@ Use these prompts when scheduling or delegating recurring documentation sweeps. ## Check-Only Sweep -Run `maintain-project-docs` in `check-only` mode for the target repository. Audit README, CONTRIBUTING, AGENTS, API when present, and ROADMAP in that order. Report owner-skill findings, cross-document responsibility drift, stale command evidence, and any roadmap small-ticket candidates requested by the operator. Do not edit files, commit, push, or open a pull request. +Run `maintain-project-docs` in `check-only` mode for the target repository. +Audit README, CONTRIBUTING, AGENTS, and ROADMAP in that order. Report +owner-skill findings, cross-document responsibility drift, stale command +evidence, and any roadmap small-ticket candidates requested by the operator. Do +not edit files, commit, push, or open a pull request. ## Bounded Apply Sweep diff --git a/skills/maintain-project-docs/scripts/maintain_project_docs.py b/skills/maintain-project-docs/scripts/maintain_project_docs.py index 0e4033f9c..50b4b2521 100644 --- a/skills/maintain-project-docs/scripts/maintain_project_docs.py +++ b/skills/maintain-project-docs/scripts/maintain_project_docs.py @@ -30,35 +30,32 @@ class DocumentWorkflow: key="readme", label="README", filename="README.md", - script=PLUGIN_ROOT / "skills/maintain-project-readme/scripts/maintain_project_readme.py", + script=PLUGIN_ROOT + / "skills/maintain-project-readme/scripts/maintain_project_readme.py", path_arg="--readme-path", ), DocumentWorkflow( key="contributing", label="CONTRIBUTING", filename="CONTRIBUTING.md", - script=PLUGIN_ROOT / "skills/maintain-project-contributing/scripts/maintain_project_contributing.py", + script=PLUGIN_ROOT + / "skills/maintain-project-contributing/scripts/maintain_project_contributing.py", path_arg="--contributing-path", ), DocumentWorkflow( key="agents", label="AGENTS", filename="AGENTS.md", - script=PLUGIN_ROOT / "skills/maintain-project-agents/scripts/maintain_project_agents.py", + script=PLUGIN_ROOT + / "skills/maintain-project-agents/scripts/maintain_project_agents.py", path_arg="--agents-path", ), - DocumentWorkflow( - key="api", - label="API", - filename="API.md", - script=PLUGIN_ROOT / "skills/maintain-project-api/scripts/maintain_project_api.py", - path_arg="--api-path", - ), DocumentWorkflow( key="roadmap", label="ROADMAP", filename="ROADMAP.md", - script=PLUGIN_ROOT / "skills/maintain-project-roadmap/scripts/maintain_project_roadmap.py", + script=PLUGIN_ROOT + / "skills/maintain-project-roadmap/scripts/maintain_project_roadmap.py", path_arg="--roadmap-path", ), ) @@ -77,16 +74,31 @@ class DocumentWorkflow: def parse_args(argv: Optional[Sequence[str]] = None) -> argparse.Namespace: - parser = argparse.ArgumentParser(description="Run a coordinated project documentation maintenance sweep.") - parser.add_argument("--project-root", required=True, help="Absolute project root path") - parser.add_argument("--run-mode", required=True, choices=["check-only", "apply"], help="Execution mode") - parser.add_argument("--include", help="Comma-separated document workflow keys to include") + parser = argparse.ArgumentParser( + description="Run a coordinated project documentation maintenance sweep." + ) + parser.add_argument( + "--project-root", required=True, help="Absolute project root path" + ) + parser.add_argument( + "--run-mode", + required=True, + choices=["check-only", "apply"], + help="Execution mode", + ) + parser.add_argument( + "--include", help="Comma-separated document workflow keys to include" + ) parser.add_argument("--skip", help="Comma-separated document workflow keys to skip") parser.add_argument("--json-out", help="Write JSON report path") parser.add_argument("--md-out", help="Write markdown report path") parser.add_argument("--print-json", action="store_true", help="Print JSON report") parser.add_argument("--print-md", action="store_true", help="Print markdown report") - parser.add_argument("--fail-on-issues", action="store_true", help="Exit non-zero when findings remain") + parser.add_argument( + "--fail-on-issues", + action="store_true", + help="Exit non-zero when findings remain", + ) parser.add_argument( "--collect-source-tickets", action="store_true", @@ -97,7 +109,10 @@ def parse_args(argv: Optional[Sequence[str]] = None) -> argparse.Namespace: action="store_true", help="Pass GitHub issue collection through to the roadmap workflow.", ) - parser.add_argument("--github-repo", help="Optional OWNER/REPO override for roadmap GitHub issue collection") + parser.add_argument( + "--github-repo", + help="Optional OWNER/REPO override for roadmap GitHub issue collection", + ) return parser.parse_args(argv) @@ -120,16 +135,13 @@ def select_workflows( if include_keys: selected = [known[key] for key in include_keys if key in known] else: - selected = [ - workflow - for workflow in DOCUMENT_WORKFLOWS - if workflow.key != "api" - or (project_root is not None and (project_root / workflow.filename).is_file()) - ] + selected = list(DOCUMENT_WORKFLOWS) return [workflow for workflow in selected if workflow.key not in skip_keys], errors -def build_child_command(args: argparse.Namespace, workflow: DocumentWorkflow, project_root: Path) -> List[str]: +def build_child_command( + args: argparse.Namespace, workflow: DocumentWorkflow, project_root: Path +) -> List[str]: command = [ sys.executable, str(workflow.script), @@ -151,9 +163,13 @@ def build_child_command(args: argparse.Namespace, workflow: DocumentWorkflow, pr return command -def run_child(args: argparse.Namespace, workflow: DocumentWorkflow, project_root: Path) -> Dict[str, Any]: +def run_child( + args: argparse.Namespace, workflow: DocumentWorkflow, project_root: Path +) -> Dict[str, Any]: command = build_child_command(args, workflow, project_root) - proc = subprocess.run(command, cwd=project_root, capture_output=True, text=True, check=False) + proc = subprocess.run( + command, cwd=project_root, capture_output=True, text=True, check=False + ) child: Dict[str, Any] = { "key": workflow.key, "label": workflow.label, @@ -171,7 +187,9 @@ def run_child(args: argparse.Namespace, workflow: DocumentWorkflow, project_root if proc.stdout.strip(): child["stdout"] = proc.stdout.strip() if proc.returncode != 0: - child["errors"].append(f"{workflow.label} workflow exited with status {proc.returncode}.") + child["errors"].append( + f"{workflow.label} workflow exited with status {proc.returncode}." + ) return child @@ -187,7 +205,9 @@ def heading_present(text: str, heading: str) -> bool: return re.search(pattern, text) is not None -def responsibility_issue(file: Path, issue_id: str, message: str, destination: str) -> Dict[str, Any]: +def responsibility_issue( + file: Path, issue_id: str, message: str, destination: str +) -> Dict[str, Any]: return { "issue_id": issue_id, "severity": "warning", @@ -197,7 +217,9 @@ def responsibility_issue(file: Path, issue_id: str, message: str, destination: s } -def audit_responsibility_boundaries(project_root: Path, selected: Sequence[DocumentWorkflow]) -> List[Dict[str, Any]]: +def audit_responsibility_boundaries( + project_root: Path, selected: Sequence[DocumentWorkflow] +) -> List[Dict[str, Any]]: selected_keys = {workflow.key for workflow in selected} issues: List[Dict[str, Any]] = [] @@ -209,7 +231,11 @@ def maybe_read(key: str, filename: str) -> Tuple[Path, str]: readme_path, readme = maybe_read("readme", "README.md") if readme: - for heading in ("Contribution Workflow", "Review Expectations", "Release Process"): + for heading in ( + "Contribution Workflow", + "Review Expectations", + "Release Process", + ): if heading_present(readme, heading): issues.append( responsibility_issue( @@ -237,7 +263,9 @@ def maybe_read(key: str, filename: str) -> Tuple[Path, str]: if agents: for heading in ("Quick Start", "Usage", "Known Gaps"): if heading_present(agents, heading): - destination = "README.md" if heading in {"Quick Start", "Usage"} else "ROADMAP.md" + destination = ( + "README.md" if heading in {"Quick Start", "Usage"} else "ROADMAP.md" + ) issues.append( responsibility_issue( agents_path, @@ -251,7 +279,9 @@ def maybe_read(key: str, filename: str) -> Tuple[Path, str]: if roadmap: for heading in ("Contribution Workflow", "Local Setup", "Safety Boundaries"): if heading_present(roadmap, heading): - destination = "CONTRIBUTING.md" if heading != "Safety Boundaries" else "AGENTS.md" + destination = ( + "CONTRIBUTING.md" if heading != "Safety Boundaries" else "AGENTS.md" + ) issues.append( responsibility_issue( roadmap_path, @@ -267,9 +297,11 @@ def child_issue_count(child: Dict[str, Any]) -> int: report = child.get("report") if not isinstance(report, dict): return len(child.get("errors", [])) - return sum(len(report.get(key, [])) for key in ISSUE_KEYS if isinstance(report.get(key), list)) + len( - child.get("errors", []) - ) + return sum( + len(report.get(key, [])) + for key in ISSUE_KEYS + if isinstance(report.get(key), list) + ) + len(child.get("errors", [])) def child_fixes(child: Dict[str, Any]) -> List[Dict[str, Any]]: @@ -297,7 +329,9 @@ def markdown_report(report: Dict[str, Any]) -> str: ] for child in report["document_reports"]: issue_count = child_issue_count(child) - lines.append(f"- `{child['key']}`: exit `{child['returncode']}`, {issue_count} issue(s)") + lines.append( + f"- `{child['key']}`: exit `{child['returncode']}`, {issue_count} issue(s)" + ) lines.extend(["", "## Responsibility Issues", ""]) if report["responsibility_issues"]: @@ -332,9 +366,9 @@ def write_text(path: Path, text: str) -> None: def unresolved_issues(report: Dict[str, Any]) -> bool: - return bool(report["responsibility_issues"] or report["errors"] or report["post_fix_status"]) or any( - child_issue_count(child) for child in report["document_reports"] - ) + return bool( + report["responsibility_issues"] or report["errors"] or report["post_fix_status"] + ) or any(child_issue_count(child) for child in report["document_reports"]) def run_maintenance(args: argparse.Namespace) -> Tuple[Dict[str, Any], str]: @@ -357,7 +391,9 @@ def run_maintenance(args: argparse.Namespace) -> Tuple[Dict[str, Any], str]: "errors": selection_errors, } if not project_root.is_dir(): - report["errors"].append(f"Project root does not exist or is not a directory: {project_root}") + report["errors"].append( + f"Project root does not exist or is not a directory: {project_root}" + ) return report, markdown_report(report) if not report["errors"]: @@ -367,7 +403,9 @@ def run_maintenance(args: argparse.Namespace) -> Tuple[Dict[str, Any], str]: report["fixes_applied"].extend(child_fixes(child)) report["post_fix_status"].extend(child_post_fix_status(child)) report["errors"].extend(child["errors"]) - report["responsibility_issues"] = audit_responsibility_boundaries(project_root, selected) + report["responsibility_issues"] = audit_responsibility_boundaries( + project_root, selected + ) return report, markdown_report(report) diff --git a/skills/repository-operations-workflow/SKILL.md b/skills/repository-operations-workflow/SKILL.md index 43d6abdc1..c278a19b6 100644 --- a/skills/repository-operations-workflow/SKILL.md +++ b/skills/repository-operations-workflow/SKILL.md @@ -24,7 +24,7 @@ needed for that classification, then hand work to the narrowest owner. | Codex worktree mode, local environment, or app-owned worktree behavior | `codex-gui-worktree-workflow` | | Parallel worker branch, worktree, write, or integration ownership | `coordinate-worktrees-and-threads` | | GitHub settings, rulesets, security automation, or repository policy | `maintain-github-repository` | - | README, CONTRIBUTING, AGENTS, API, or ROADMAP maintenance | The matching `maintain-project-*` document workflow | + | README, CONTRIBUTING, AGENTS, or ROADMAP maintenance | The matching `maintain-project-*` document workflow | | Coordinated repository-document sweep | `maintain-project-docs` | | Version bump, tag, release, publication, or branch-accounting cleanup | `maintain-project-repo` | 3. When the request crosses multiple outcomes, name the sequence and keep each diff --git a/tests/test_repository_docs_workflow.py b/tests/test_repository_docs_workflow.py index 85e1a17f0..cb8a24874 100644 --- a/tests/test_repository_docs_workflow.py +++ b/tests/test_repository_docs_workflow.py @@ -9,7 +9,10 @@ ROOT = Path(__file__).resolve().parents[1] -SCRIPT = ROOT / "plugins/repository-skills/skills/maintain-project-docs/scripts/maintain_project_docs.py" +SCRIPT = ( + ROOT + / "plugins/repository-skills/skills/maintain-project-docs/scripts/maintain_project_docs.py" +) spec = importlib.util.spec_from_file_location("maintain_project_docs", SCRIPT) assert spec is not None and spec.loader is not None @@ -19,10 +22,22 @@ class MaintainProjectDocsTests(unittest.TestCase): + def test_retired_api_skill_is_absent_from_source_export_and_dispatch(self) -> None: + self.assertFalse( + (ROOT / "plugins/repository-skills/skills/maintain-project-api").exists() + ) + self.assertFalse((ROOT / "skills/maintain-project-api").exists()) + self.assertNotIn( + "api", {workflow.key for workflow in module.DOCUMENT_WORKFLOWS} + ) + def test_select_workflows_preserves_canonical_order(self) -> None: selected, errors = module.select_workflows(None, None) self.assertEqual(errors, []) - self.assertEqual([workflow.key for workflow in selected], ["readme", "contributing", "agents", "roadmap"]) + self.assertEqual( + [workflow.key for workflow in selected], + ["readme", "contributing", "agents", "roadmap"], + ) def test_select_workflows_reports_unknown_keys(self) -> None: selected, errors = module.select_workflows("readme,unknown", "roadmap") @@ -42,8 +57,12 @@ def test_build_child_command_passes_ticket_flags_only_to_roadmap(self) -> None: "owner/repo", ] ) - readme_command = module.build_child_command(args, module.DOCUMENT_WORKFLOWS[0], Path("/tmp/demo")) - roadmap_command = module.build_child_command(args, module.DOCUMENT_WORKFLOWS[-1], Path("/tmp/demo")) + readme_command = module.build_child_command( + args, module.DOCUMENT_WORKFLOWS[0], Path("/tmp/demo") + ) + roadmap_command = module.build_child_command( + args, module.DOCUMENT_WORKFLOWS[-1], Path("/tmp/demo") + ) self.assertNotIn("--collect-source-tickets", readme_command) self.assertIn("--collect-source-tickets", roadmap_command) self.assertIn("--collect-github-issues", roadmap_command) @@ -52,9 +71,16 @@ def test_build_child_command_passes_ticket_flags_only_to_roadmap(self) -> None: def test_responsibility_audit_flags_cross_doc_drift(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) - (root / "README.md").write_text("# Demo\n\n## Contribution Workflow\n\nDo all the things.\n", encoding="utf-8") - (root / "ROADMAP.md").write_text("# Roadmap\n\n## Safety Boundaries\n\nDo not.\n", encoding="utf-8") - issues = module.audit_responsibility_boundaries(root, module.DOCUMENT_WORKFLOWS) + (root / "README.md").write_text( + "# Demo\n\n## Contribution Workflow\n\nDo all the things.\n", + encoding="utf-8", + ) + (root / "ROADMAP.md").write_text( + "# Roadmap\n\n## Safety Boundaries\n\nDo not.\n", encoding="utf-8" + ) + issues = module.audit_responsibility_boundaries( + root, module.DOCUMENT_WORKFLOWS + ) issue_ids = {issue["issue_id"] for issue in issues} self.assertIn("readme-contains-maintainer-workflow", issue_ids) self.assertIn("roadmap-contains-procedural-guidance", issue_ids) diff --git a/tests/test_validate_socket.py b/tests/test_validate_socket.py index 91c38e240..a6cc39f0f 100644 --- a/tests/test_validate_socket.py +++ b/tests/test_validate_socket.py @@ -54,6 +54,33 @@ def test_full_profile_adds_compatibility_and_child_checks_once() -> None: assert "release readiness" not in names +def test_child_python_checks_use_root_runtime_and_central_caches() -> None: + checks = [ + check for check in validate_socket.CHILD_CHECKS if check.command[0] != "bash" + ] + + assert checks + assert all(check.command[0] == sys.executable for check in checks) + assert all("uv" not in check.command for check in checks) + rendered = "\n".join(" ".join(check.command) for check in checks) + assert ".codex/.cache" in rendered + + +def test_monorepo_children_do_not_own_python_tooling_roots() -> None: + child_names = ( + "agent-engineering-skills", + "agent-portability-skills", + "apple-dev-skills", + "professional-skills", + "python-skills", + ) + + for child_name in child_names: + child_root = ROOT / "plugins" / child_name + assert not (child_root / "pyproject.toml").exists() + assert not (child_root / "uv.lock").exists() + + def test_validation_profiles_do_not_duplicate_release_choreography() -> None: source = (ROOT / "scripts" / "validate_socket.py").read_text(encoding="utf-8") @@ -84,7 +111,9 @@ def unexpected_run(*args: object, **kwargs: object) -> None: assert not called -def test_shared_skill_validator_accepts_valid_plugin(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: +def test_shared_skill_validator_accepts_valid_plugin( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: plugin_root = tmp_path / "plugins" / "example-skills" write(plugin_root / ".codex-plugin" / "plugin.json", "{}\n") write(plugin_root / "AGENTS.md", "# Guidance\n") @@ -122,7 +151,10 @@ def test_shared_skill_validator_rejects_directory_name_drift( [ ("interface: {}\n", "non-empty interface"), ("interface:\n default_prompt: Use this skill.\n", "invocation token"), - ("interface:\n default_prompt: Use $example-skill.\n display_name: ''\n", "display_name"), + ( + "interface:\n default_prompt: Use $example-skill.\n display_name: ''\n", + "display_name", + ), ], ) def test_shared_skill_validator_rejects_invalid_openai_interface( @@ -139,7 +171,9 @@ def test_shared_skill_validator_rejects_invalid_openai_interface( plugin_root / "skills" / "example-skill" / "SKILL.md", "---\nname: example-skill\ndescription: A valid skill.\n---\n", ) - write(plugin_root / "skills" / "example-skill" / "agents" / "openai.yaml", interface) + write( + plugin_root / "skills" / "example-skill" / "agents" / "openai.yaml", interface + ) monkeypatch.setattr(validate_skill_metadata, "REPO_ROOT", tmp_path) with pytest.raises(SystemExit): From 87f4c5f9b5c01dbea300a82b12c63c23c36c7bbc Mon Sep 17 00:00:00 2001 From: Gale W Date: Thu, 20 Aug 2026 21:48:00 -0400 Subject: [PATCH 2/4] docs: add Socket v10.0.1 release notes --- docs/releases/v10.0.1.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 docs/releases/v10.0.1.md diff --git a/docs/releases/v10.0.1.md b/docs/releases/v10.0.1.md new file mode 100644 index 000000000..de9a62f7d --- /dev/null +++ b/docs/releases/v10.0.1.md @@ -0,0 +1,36 @@ +# Socket v10.0.1 + +## Changes + +- Uses the Socket root `uv` environment for all monorepo-owned plugin + validation and redirects pytest, Ruff, and mypy caches to the root + `.codex/.cache/` directory. +- Removes redundant child `pyproject.toml`, `uv.lock`, `.gitignore`, virtual + environment, bytecode, and tool-cache state from plugin payloads. +- Removes historical nested maintainer documents whose durable conclusions now + live in owning skills, tests, `AGENTS.md`, or roadmap history, and moves live + Codex subagent guidance into its owning bootstrap skill reference. +- Permanently removes `repository-skills:maintain-project-api` from the authored + plugin, Hermes export, inventory, automation guidance, and coordinated docs + dispatcher. + +## Breaking Changes + +- `repository-skills:maintain-project-api` is no longer available. Remove that + invocation from agent prompts and automations. API-reference maintenance is + now project-specific rather than owned by a Socket repository skill. +- `repository-skills:maintain-project-docs` no longer discovers or dispatches + `API.md`; its coordinated pass covers README, CONTRIBUTING, AGENTS, and + ROADMAP. + +## Upgrade + +Upgrade the Socket marketplace normally. No environment migration is required; +the next root validation run uses the existing root environment and centralized +cache paths. + +## Verification + +- `uv run python -B scripts/validate_socket.py --profile full` +- Post-validation inventory confirmed no plugin-local `.venv`, `__pycache__`, + `.pytest_cache`, `.mypy_cache`, or `.ruff_cache` directories were recreated. From 9f45ff8b3ac78b6ffb872fcd26c2ca7ce0cf8157 Mon Sep 17 00:00:00 2001 From: Gale W Date: Thu, 20 Aug 2026 21:48:19 -0400 Subject: [PATCH 3/4] release: prepare Socket v10.0.1 --- plugins/agent-engineering-skills/.codex-plugin/plugin.json | 2 +- plugins/agent-portability-skills/.codex-plugin/plugin.json | 2 +- plugins/agentdeck/.codex-plugin/plugin.json | 2 +- plugins/android-dev-skills/.codex-plugin/plugin.json | 2 +- plugins/apple-creator-studio-skills/.codex-plugin/plugin.json | 2 +- plugins/apple-dev-skills/.codex-plugin/plugin.json | 2 +- plugins/cloud-deployment-skills/.codex-plugin/plugin.json | 2 +- plugins/cloud-inference-skills/.codex-plugin/plugin.json | 2 +- plugins/codebase-understanding-skills/.codex-plugin/plugin.json | 2 +- plugins/cybersecurity-skills/.codex-plugin/plugin.json | 2 +- plugins/dotnet-skills/.codex-plugin/plugin.json | 2 +- plugins/game-dev-skills/.codex-plugin/plugin.json | 2 +- .../messaging-collaboration-skills/.codex-plugin/plugin.json | 2 +- plugins/model-lab-skills/.codex-plugin/plugin.json | 2 +- plugins/network-protocol-skills/.codex-plugin/plugin.json | 2 +- plugins/professional-skills/.codex-plugin/plugin.json | 2 +- plugins/python-skills/.codex-plugin/plugin.json | 2 +- plugins/repository-skills/.codex-plugin/plugin.json | 2 +- plugins/reverse-engineering-skills/.codex-plugin/plugin.json | 2 +- plugins/rust-skills/.codex-plugin/plugin.json | 2 +- plugins/server-side-jvm/.codex-plugin/plugin.json | 2 +- plugins/server-side-swift/.codex-plugin/plugin.json | 2 +- plugins/swift-lang/.codex-plugin/plugin.json | 2 +- plugins/swiftasb-skills/.codex-plugin/plugin.json | 2 +- plugins/web-dev-skills/.codex-plugin/plugin.json | 2 +- pyproject.toml | 2 +- uv.lock | 2 +- 27 files changed, 27 insertions(+), 27 deletions(-) diff --git a/plugins/agent-engineering-skills/.codex-plugin/plugin.json b/plugins/agent-engineering-skills/.codex-plugin/plugin.json index fad4e2502..81794d34f 100644 --- a/plugins/agent-engineering-skills/.codex-plugin/plugin.json +++ b/plugins/agent-engineering-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "agent-engineering-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Agent-system design, orchestration, scheduling, external-agent, and evaluation workflow skills.", "author": { "name": "Gale", diff --git a/plugins/agent-portability-skills/.codex-plugin/plugin.json b/plugins/agent-portability-skills/.codex-plugin/plugin.json index 208671c65..d71039da8 100644 --- a/plugins/agent-portability-skills/.codex-plugin/plugin.json +++ b/plugins/agent-portability-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "agent-portability-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Skills for agent portability, ACP, A2A, Zed, Hermes, Codex plugin surfaces, and host adapter guidance.", "author": { "name": "Gale", diff --git a/plugins/agentdeck/.codex-plugin/plugin.json b/plugins/agentdeck/.codex-plugin/plugin.json index c2dbb9896..1952a8fd1 100644 --- a/plugins/agentdeck/.codex-plugin/plugin.json +++ b/plugins/agentdeck/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "agentdeck", - "version": "10.0.0", + "version": "10.0.1", "description": "Local Codex runtime utilities for thread, hook, and app-server workflows.", "author": { "name": "Gale", diff --git a/plugins/android-dev-skills/.codex-plugin/plugin.json b/plugins/android-dev-skills/.codex-plugin/plugin.json index 5e7d75299..dc52c475d 100644 --- a/plugins/android-dev-skills/.codex-plugin/plugin.json +++ b/plugins/android-dev-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "android-dev-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Android, Kotlin, Java, Gradle, Android Gradle Plugin, testing, lint, UI implementation, and release-readiness workflow skills.", "author": { "name": "Gale", diff --git a/plugins/apple-creator-studio-skills/.codex-plugin/plugin.json b/plugins/apple-creator-studio-skills/.codex-plugin/plugin.json index 021b05ae4..ae5d772d5 100644 --- a/plugins/apple-creator-studio-skills/.codex-plugin/plugin.json +++ b/plugins/apple-creator-studio-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "apple-creator-studio-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Human-facing and Computer Use-aware Apple Creator Studio workflows for Final Cut Pro editing, Motion templates, Compressor delivery, Logic Pro production, MainStage concert preparation, and GarageBand projects.", "author": { "name": "Gale", diff --git a/plugins/apple-dev-skills/.codex-plugin/plugin.json b/plugins/apple-dev-skills/.codex-plugin/plugin.json index aac0ff4e4..97a4e30c7 100644 --- a/plugins/apple-dev-skills/.codex-plugin/plugin.json +++ b/plugins/apple-dev-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "apple-dev-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Apple development workflows for Codex, including macOS privacy permissions, sandbox file access, entitlement diagnosis, virtualization, SwiftPM, Xcode, app extensions, media, provisioning, SwiftUI, AppKit, Safari, security, OpenAPI, and DocC.", "author": { "name": "Gale", diff --git a/plugins/cloud-deployment-skills/.codex-plugin/plugin.json b/plugins/cloud-deployment-skills/.codex-plugin/plugin.json index cbcf8ef3a..2e6c5aca2 100644 --- a/plugins/cloud-deployment-skills/.codex-plugin/plugin.json +++ b/plugins/cloud-deployment-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "cloud-deployment-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Codex skills for routing cloud deployment work through official provider plugins, MCP servers, CLIs, and a reusable Dockerized-service release and deployment contract.", "author": { "name": "Gale", diff --git a/plugins/cloud-inference-skills/.codex-plugin/plugin.json b/plugins/cloud-inference-skills/.codex-plugin/plugin.json index 364c0c0ba..123fd7a01 100644 --- a/plugins/cloud-inference-skills/.codex-plugin/plugin.json +++ b/plugins/cloud-inference-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "cloud-inference-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Cloud AI inference workflow skills for routing model serving, training, conversion, and GPU infrastructure work across Runpod, Hugging Face, AWS, Vast.ai, CoreWeave, and similar providers.", "author": { "name": "Gale", diff --git a/plugins/codebase-understanding-skills/.codex-plugin/plugin.json b/plugins/codebase-understanding-skills/.codex-plugin/plugin.json index 375ad41b8..6b377d383 100644 --- a/plugins/codebase-understanding-skills/.codex-plugin/plugin.json +++ b/plugins/codebase-understanding-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "codebase-understanding-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Code-path exploration, call-site tracing, and evidence-based codebase explanation skills.", "author": { "name": "Gale", diff --git a/plugins/cybersecurity-skills/.codex-plugin/plugin.json b/plugins/cybersecurity-skills/.codex-plugin/plugin.json index 504b361de..a3fb99cb2 100644 --- a/plugins/cybersecurity-skills/.codex-plugin/plugin.json +++ b/plugins/cybersecurity-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "cybersecurity-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Defensive cybersecurity, isolated Linux and macOS analysis labs, suspicious-content and malware analysis, macOS defense, vulnerability testing, pentesting, and incident response workflows.", "skills": "./skills/", "author": { diff --git a/plugins/dotnet-skills/.codex-plugin/plugin.json b/plugins/dotnet-skills/.codex-plugin/plugin.json index e9ad5f1aa..e2acb029a 100644 --- a/plugins/dotnet-skills/.codex-plugin/plugin.json +++ b/plugins/dotnet-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "dotnet-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Codex skills for choosing, bootstrapping, building, testing, packaging, diagnosing, and maintaining .NET projects, including F# web applications, with F# and C# as equal first-party languages.", "author": { "name": "Gale", diff --git a/plugins/game-dev-skills/.codex-plugin/plugin.json b/plugins/game-dev-skills/.codex-plugin/plugin.json index 9f4ed2992..4ba38ccba 100644 --- a/plugins/game-dev-skills/.codex-plugin/plugin.json +++ b/plugins/game-dev-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "game-dev-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Apple platform game development workflow skills for native Metal rendering, Game Porting Toolkit routing, MetalFX, GPU asset streaming, neural rendering, frameworks, input, haptics, and profiling.", "author": { "name": "Gale", diff --git a/plugins/messaging-collaboration-skills/.codex-plugin/plugin.json b/plugins/messaging-collaboration-skills/.codex-plugin/plugin.json index b793c021d..e68c8182d 100644 --- a/plugins/messaging-collaboration-skills/.codex-plugin/plugin.json +++ b/plugins/messaging-collaboration-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "messaging-collaboration-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Codex workflows for chat apps, bots, collaboration, iMessage, Apple notifications and Push to Talk, VoIP, and default communication-app planning.", "author": { "name": "Gale", diff --git a/plugins/model-lab-skills/.codex-plugin/plugin.json b/plugins/model-lab-skills/.codex-plugin/plugin.json index f6f9324ff..fc4072d3f 100644 --- a/plugins/model-lab-skills/.codex-plugin/plugin.json +++ b/plugins/model-lab-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "model-lab-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Reproducible model training, evaluation, intervention, and runtime research workflows.", "author": { "name": "Gale" diff --git a/plugins/network-protocol-skills/.codex-plugin/plugin.json b/plugins/network-protocol-skills/.codex-plugin/plugin.json index aaeb8bba8..696bcd9e0 100644 --- a/plugins/network-protocol-skills/.codex-plugin/plugin.json +++ b/plugins/network-protocol-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "network-protocol-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Codex skills for choosing, planning, implementing, and diagnosing modern application transports and real-time networking protocols, including QUIC, HTTP/3, WebRTC, Media over QUIC, WebTransport-adjacent handoffs, protocol maturity checks, and stack-specific implementation routing.", "author": { "name": "Gale", diff --git a/plugins/professional-skills/.codex-plugin/plugin.json b/plugins/professional-skills/.codex-plugin/plugin.json index 0536ac0ed..b2fd8cb42 100644 --- a/plugins/professional-skills/.codex-plugin/plugin.json +++ b/plugins/professional-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "professional-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Professional workflow skills for job search, career materials, and external professional services.", "author": { "name": "Gale", diff --git a/plugins/python-skills/.codex-plugin/plugin.json b/plugins/python-skills/.codex-plugin/plugin.json index 5fba86c09..bfd95d3fe 100644 --- a/plugins/python-skills/.codex-plugin/plugin.json +++ b/plugins/python-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "python-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Bundled Python-focused Codex skills for uv bootstrapping, project implementation, diagnostics, packaging, tooling, CI, upgrades, FastAPI service maintenance, FastMCP service maintenance, and testing workflows.", "author": { "name": "Gale", diff --git a/plugins/repository-skills/.codex-plugin/plugin.json b/plugins/repository-skills/.codex-plugin/plugin.json index 6289d8362..addd96bf4 100644 --- a/plugins/repository-skills/.codex-plugin/plugin.json +++ b/plugins/repository-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "repository-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Repository operations, documentation maintenance, GitHub settings, worktree, and release workflow skills.", "author": { "name": "Gale", diff --git a/plugins/reverse-engineering-skills/.codex-plugin/plugin.json b/plugins/reverse-engineering-skills/.codex-plugin/plugin.json index 73f716c5b..cfd7a3077 100644 --- a/plugins/reverse-engineering-skills/.codex-plugin/plugin.json +++ b/plugins/reverse-engineering-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "reverse-engineering-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Workflow skills for reverse engineering, decompilation, disassembly, symbols, artifact analysis, and exact-build macOS security-control research.", "skills": "./skills/", "author": { diff --git a/plugins/rust-skills/.codex-plugin/plugin.json b/plugins/rust-skills/.codex-plugin/plugin.json index 3ed91de4c..927cc4186 100644 --- a/plugins/rust-skills/.codex-plugin/plugin.json +++ b/plugins/rust-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "rust-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Rust, Cargo, rustup, crate, workspace, CLI, library, package, CI, testing, linting, and formatting workflow skills.", "skills": "./skills/", "author": { diff --git a/plugins/server-side-jvm/.codex-plugin/plugin.json b/plugins/server-side-jvm/.codex-plugin/plugin.json index ad7a33351..3c513c3b5 100644 --- a/plugins/server-side-jvm/.codex-plugin/plugin.json +++ b/plugins/server-side-jvm/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "server-side-jvm", - "version": "10.0.0", + "version": "10.0.1", "description": "Codex skills for choosing, building, testing, and maintaining server-side JVM backend projects with Java and Scala as equal first-party languages and future Clojure support planned.", "author": { "name": "Gale", diff --git a/plugins/server-side-swift/.codex-plugin/plugin.json b/plugins/server-side-swift/.codex-plugin/plugin.json index 36cbf27a7..84bca8380 100644 --- a/plugins/server-side-swift/.codex-plugin/plugin.json +++ b/plugins/server-side-swift/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "server-side-swift", - "version": "10.0.0", + "version": "10.0.1", "description": "Codex skills for adding, building, testing, and operating server-side Swift components in canonical product workspaces, with native Homebrew local services and GitHub-only Linux builds and deployments.", "author": { "name": "Gale", diff --git a/plugins/swift-lang/.codex-plugin/plugin.json b/plugins/swift-lang/.codex-plugin/plugin.json index 5c0838539..ec5fbb8d0 100644 --- a/plugins/swift-lang/.codex-plugin/plugin.json +++ b/plugins/swift-lang/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "swift-lang", - "version": "10.0.0", + "version": "10.0.1", "description": "Shared Swift language and tooling skills for API style, errors, functional pipelines, formatting, source organization, SwiftSyntax, compiler inspection, SourceKit, indexing, SourceKit-LSP, and modernization.", "skills": "./skills/", "author": { diff --git a/plugins/swiftasb-skills/.codex-plugin/plugin.json b/plugins/swiftasb-skills/.codex-plugin/plugin.json index 49072656f..106a7a3f6 100644 --- a/plugins/swiftasb-skills/.codex-plugin/plugin.json +++ b/plugins/swiftasb-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "swiftasb-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Codex skills for explaining SwiftASB and building SwiftUI, AppKit, and Swift package integrations on top of it.", "author": { "name": "Gale", diff --git a/plugins/web-dev-skills/.codex-plugin/plugin.json b/plugins/web-dev-skills/.codex-plugin/plugin.json index 5a75a6885..9cb322c1b 100644 --- a/plugins/web-dev-skills/.codex-plugin/plugin.json +++ b/plugins/web-dev-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "web-dev-skills", - "version": "10.0.0", + "version": "10.0.1", "description": "Codex skills for focused web and Expo native-boundary workflows.", "author": { "name": "Gale", diff --git a/pyproject.toml b/pyproject.toml index f3c61ab7b..8c10e1057 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "socket-maintenance" -version = "10.0.0" +version = "10.0.1" description = "Root uv tooling baseline for the socket superproject." requires-python = ">=3.11" dependencies = [] diff --git a/uv.lock b/uv.lock index cb79e71ba..aa28db1f1 100644 --- a/uv.lock +++ b/uv.lock @@ -286,7 +286,7 @@ wheels = [ [[package]] name = "socket-maintenance" -version = "10.0.0" +version = "10.0.1" source = { virtual = "." } [package.dev-dependencies] From 39b16831b7be95c405d1855fd4df857c363215e9 Mon Sep 17 00:00:00 2001 From: Gale W Date: Thu, 20 Aug 2026 22:04:30 -0400 Subject: [PATCH 4/4] validation: remove unjustified Socket checks --- docs/maintainers/validation-and-test-audit.md | 186 ++++++++++++++++++ docs/releases/v10.0.1.md | 4 + .../.github/scripts/validate_repo_docs.sh | 63 ------ .../workflows/validate-docs-and-skills.yml | 23 --- ...t_apple_developer_provisioning_workflow.py | 4 +- ...est_core_animation_typography_workflows.py | 2 - ...test_customization_consolidation_review.py | 110 ----------- .../test_design_animation_symbol_workflows.py | 2 - .../test_devicecheck_app_attest_workflow.py | 2 - .../tests/test_media_audio_workflows.py | 2 - .../tests/test_repo_docs_validator.py | 26 --- .../test_swift_package_extension_workflow.py | 7 - .../tests/test_tipkit_workflow.py | 2 - .../tests/test_tvos_workflows.py | 2 - .../tests/test_xcode_localization_workflow.py | 5 +- .../.github/scripts/validate_repo_docs.sh | 5 - .../workflows/validate-python-skills.yml | 32 --- .../python-skills/tests/test_plugin_smoke.py | 7 +- .../tests/test_validate_repo_metadata.py | 13 -- scripts/export_hermes_skills.py | 15 +- .../test_bootstrap_skills_plugin_repo.py | 60 ------ .../tests/test_sync_skills_repo_guidance.py | 156 --------------- tests/test_release_version.py | 6 - tests/test_repository_docs_workflow.py | 40 +--- tests/test_root_agents_guidance.py | 17 -- .../test_unified_swift_workspace_contracts.py | 41 +--- tests/test_validate_socket.py | 40 +--- 27 files changed, 219 insertions(+), 653 deletions(-) create mode 100644 docs/maintainers/validation-and-test-audit.md delete mode 100644 plugins/apple-dev-skills/.github/workflows/validate-docs-and-skills.yml delete mode 100644 plugins/apple-dev-skills/tests/test_customization_consolidation_review.py delete mode 100644 plugins/apple-dev-skills/tests/test_repo_docs_validator.py delete mode 100755 plugins/python-skills/.github/scripts/validate_repo_docs.sh delete mode 100644 plugins/python-skills/.github/workflows/validate-python-skills.yml delete mode 100644 plugins/python-skills/tests/test_validate_repo_metadata.py delete mode 100644 skills/bootstrap-skills-plugin-repo/tests/test_bootstrap_skills_plugin_repo.py delete mode 100644 skills/sync-skills-repo-guidance/tests/test_sync_skills_repo_guidance.py delete mode 100644 tests/test_root_agents_guidance.py diff --git a/docs/maintainers/validation-and-test-audit.md b/docs/maintainers/validation-and-test-audit.md new file mode 100644 index 000000000..5db9f9b43 --- /dev/null +++ b/docs/maintainers/validation-and-test-audit.md @@ -0,0 +1,186 @@ +# Socket Validation and Test Audit + +## Decision Standard + +This audit covers every validation gate and collected test in Socket as of +v10.0.1. A check remains only when it protects at least one current, shipped +contract: + +- executable behavior or error handling; +- marketplace, plugin, skill, or consumer compatibility; +- a destructive-action, credential, privacy, security, or release boundary; +- source ownership, routing, or discovery that changes agent behavior; or +- generated payload integrity. + +A check is not justified when it only records completed history, proves that a +retired thing remains absent, repeats another live gate without isolating its +behavior, or copies source tests into a generated payload. No absence regression +test is required when a surface is permanently removed. + +Every retained test function is covered below by its owning file and exact test +count. The retained full profile collects 432 tests. + +## Live Validation Gates + +The repository has one CI entrypoint: `.github/workflows/validate-socket.yml`. +It runs the full profile on macOS because several shipped workflows and path +contracts are Apple-platform specific. The nested Apple and Python workflow +files were removed because GitHub does not load workflows below the repository +root and the root full profile already owns their checks. + +| Gate | Decision | Current contract | +| --- | --- | --- | +| Root marketplace metadata | Keep | Rejects malformed marketplace entries, missing local payloads, unsafe custom-agent permissions, invalid MCP transport, and broken interface assets. | +| Shared skill metadata | Keep | Enforces skill-directory identity and OpenAI interface shape across every authored plugin skill. | +| Root tests (160) | Keep | Exercises root release, export, compatibility, installer, safety, validator, and cross-plugin ownership behavior listed below. | +| Root mypy | Keep | Checks the typed root maintainer and release programs before they can mutate repository or release state. | +| Root Ruff | Keep | Rejects Python defects in the root scripts and their tests. | +| Hermes compatibility | Keep | Proves the generated skill tap, groupings, metadata limits, and translated MCP configuration are installable by Hermes. | +| Claude compatibility | Keep | Proves marketplace classification agrees with Claude/Cowork support and rejects unsupported local MCP claims. | +| Agent Engineering tests (12) | Keep | Protects the executable automation/evaluation workflow schemas, routing, and safety boundaries. | +| Agent Portability tests (13) | Keep | Protects protocol routing plus the two executable repository bootstrap/synchronization tools. | +| Agent Portability Ruff | Keep | Lints the shipped portability scripts, including files outside their focused tests. | +| Agent Portability mypy | Keep | Type-checks the shipped portability scripts and their structured payloads. | +| Apple docs/layout validator | Keep, pruned | Protects the active 58-skill inventory, discovery symlink, required skill structure, shared-snippet synchronization, docs-source order, repository-skill delegation, and skill-creator contract. Historical roadmap prose and retired-path assertions were removed. | +| Apple tests (227) | Keep | Exercises executable Apple workflow planners/configuration tools and safety-critical guidance contracts listed below. | +| Professional Skills tests (5) | Keep | Protects the Dice search workflow's request shaping, URL construction, filtering, and output behavior. | +| Python metadata | Keep | Validates the current Python plugin manifest, skill metadata, links, and agent interfaces. | +| Python tests (11) | Keep | Exercises all three scaffold generators and current agent/service/testing guidance contracts. | +| Python Ruff | Keep | Lints the shipped Python plugin scripts and tests. | +| Python mypy | Keep | Type-checks the shipped Python plugin scripts and structured inputs. | +| Cybersecurity metadata | Keep | Validates the current cybersecurity manifest, skill metadata, links, and agent interfaces. | +| Cybersecurity tests (2) | Keep | Protects routing between macOS threat work, platform controls, isolation, recovery, and reverse engineering. | +| Reverse Engineering metadata | Keep | Validates the current reverse-engineering manifest, skill metadata, links, and agent interfaces. | +| Reverse Engineering tests (2) | Keep | Protects public/private/runtime evidence separation and bounded-probe handoffs. | + +## Retained Root Tests + +| File | Tests | Justification | +| --- | ---: | --- | +| `tests/test_audit_skill_surfaces.py` | 7 | Exercises the shipped audit report in JSON/Markdown/file modes and enforces direct SwiftData guidance where a weak query-only answer changes implementation advice. | +| `tests/test_audit_xcode_plugin_compatibility.py` | 3 | Exercises plugin classification/reporting and proves every live marketplace entry is classified for Xcode compatibility. | +| `tests/test_check_acp_registry.py` | 3 | Exercises exact registry matching, schema loading, and the non-error missing-agent result used by the ACP workflow. | +| `tests/test_cleanup_legacy_socket_installs.py` | 5 | Protects deletion target selection, backup-before-remove behavior, cache exclusion, and reporting of foreign configuration. | +| `tests/test_cybersecurity_skill_contracts.py` | 14 | Protects authorization, isolation, evidence retention, non-binary verdicts, recovery, containment, detection fixtures, and specialist routing. | +| `tests/test_deployment_build_safety_contracts.py` | 3 | Protects clean GitHub builds, single-session build ownership, immutable deployment artifacts, and native-local/cloud-build separation. | +| `tests/test_macos_platform_security_forward_scenarios.py` | 1 | Parameterizes eight concrete TCC, sandbox, entitlement, threat, and private-evidence decisions that prevent unsafe platform advice. | +| `tests/test_macos_virtualization_forward_scenarios.py` | 1 | Parameterizes eight concrete VM/container/isolation decisions that prevent invalid macOS evidence claims. | +| `tests/test_macos_virtualization_skill_contracts.py` | 1 | Ensures every virtualization owner remains discoverable in both the Hermes export and public grouping. | +| `tests/test_model_lab_skill_contracts.py` | 9 | Protects current inventory/routing, authorization controls, experiment validation, paired comparison correctness, provenance stability, and version alignment. | +| `tests/test_release_version.py` | 4 | Exercises target discovery, aligned SemVer calculation, split-version rejection, and atomic manifest/lockfile updates. | +| `tests/test_release_workflow.py` | 10 | Exercises PR/check gating, branch ownership/accounting, version ordering, publication checks, evidence generation, and the single release CLI. | +| `tests/test_repository_docs_workflow.py` | 5 | Exercises coordinator selection, flag routing, responsibility drift detection, and structured error output. | +| `tests/test_repository_maintenance_workflow.py` | 21 | Exercises generated validation/release assets, profile installation, workspace dispatch, delayed GitHub state, prerelease metadata, notes selection, branch accounting, triggers, and preservation of repo-owned extensions. | +| `tests/test_spi_add_package.py` | 10 | Exercises canonical URL/form construction, live form validation, package readiness, tag/toolchain checks, and the explicit prohibition on unauthorized alternate submission writes. | +| `tests/test_swiftasb_skills_install.py` | 2 | Performs a real temporary Codex marketplace install and verifies the published .NET skill inventory. | +| `tests/test_unified_swift_workspace_contracts.py` | 7 | Protects positive package/workspace context, component ownership, target layout, native-local/cloud deployment, immutable artifacts, and Soto lifecycle policy. | +| `tests/test_validate_claude_compatibility.py` | 3 | Exercises acceptance and the two unsupported-classification failures enforced by the live Claude gate. | +| `tests/test_validate_hermes_compatibility.py` | 9 | Exercises exact export comparison, grouping, metadata size, MCP translation, placeholder documentation, and stale/missing payload failures. | +| `tests/test_validate_socket.py` | 8 | Protects profile composition, non-duplication, macOS CI routing, dry-run behavior, and shared skill metadata acceptance/rejection. | +| `tests/test_validate_socket_metadata.py` | 18 | Exercises every supported marketplace source/MCP/interface/agent shape and the corresponding unsafe or malformed rejection paths. | + +## Retained Apple Tests + +These files test the shipped guidance itself because guidance is the plugin's +runtime product: removing a required safety, ownership, or handoff statement +changes what an agent will do. + +| File | Tests | Justification | +| --- | ---: | --- | +| `test_app_extension_workflows.py` | 4 | Process/product boundaries, MailKit privacy, File Provider/Finder ownership, and discoverability. | +| `test_appkit_app_architecture_workflow.py` | 4 | AppKit/SwiftUI ownership, restoration, observation, menu/status surfaces, and handoffs. | +| `test_apple_developer_provisioning_workflow.py` | 5 | Portal/API limits, credential and mutation confirmation, CloudKit token safety, discovery, and customization behavior. | +| `test_apple_ui_accessibility_workflow.py` | 4 | Framework breadth, accessibility handoffs, verification limits, and semantic tree examples. | +| `test_arkit_spatial_face_body_workflows.py` | 5 | Tracking capability boundaries, authentication limits, privacy/device evidence, metadata, and handoffs. | +| `test_author_swift_docc_docs.py` | 5 | Executable task inference, docs lookup/generation handoffs, defer policy, and blocked ambiguity. | +| `test_camera_capture_depth_workflow.py` | 4 | Capture lifecycle, capability/pressure handling, calibrated depth synchronization, metadata, and handoffs. | +| `test_core_animation_typography_workflows.py` | 3 | Layer ownership, Dynamic Type/font licensing, handoffs, metadata, and discovery. | +| `test_customization_cli.py` | 2 | Apply/effective/reset behavior and invalid override rejection. | +| `test_customization_template_paths.py` | 6 | Canonical template location, partial merge semantics, and YAML/schema rejection paths. | +| `test_design_animation_symbol_workflows.py` | 3 | Rendering/motion/accessibility boundaries plus metadata and discovery. | +| `test_devicecheck_app_attest_workflow.py` | 5 | DeviceCheck/App Attest separation, server trust, entitlement/docs limits, handoffs, and discovery. | +| `test_explore_apple_swift_docs_workflow.py` | 13 | Source-order routing, user preference, open-source fallback, approval-gated Dash installation, and structured generation. | +| `test_format_swift_sources_export.py` | 3 | SwiftFormat option inference, deterministic export, and plist input loading. | +| `test_imaging_foundation_workflows.py` | 4 | Core Image/representation ownership, conversion evidence, metadata, and customization. | +| `test_macos_platform_security_workflows.py` | 5 | TCC, sandbox authorization lifetime, entitlement evidence, discovery, and metadata. | +| `test_macos_virtualization_workflows.py` | 5 | Shape selection, guest/state identity, evidence boundaries, metadata, and customization. | +| `test_media_audio_workflows.py` | 4 | Framework/type ownership, repair guidance, references, discovery, and validation inventory. | +| `test_media_expansion_audit.py` | 4 | Complete media workflow structure, non-overlapping framework owners, privacy/device evidence, and public inventory. | +| `test_milestone24_system_ui_workflows.py` | 5 | Current App Intents, Liquid Glass, telemetry/distribution, Help Viewer, and Feedback Assistant boundaries; the filename is historical but assertions protect live guidance. | +| `test_photos_library_editing_workflow.py` | 4 | Picker/authorization, typed asset delivery, transactional editing, metadata, customization, and handoffs. | +| `test_safari_extension_control_workflow.py` | 5 | Extension-shape ownership, bounded controls, messaging privacy, and handoffs. | +| `test_safari_mcp_workflow.py` | 3 | Runtime-versus-architecture ownership, scoped evidence, registration, and data boundaries. | +| `test_structure_swift_sources_file_headers.py` | 4 | Header reporting, license preservation, deterministic replacement, and inventory schema. | +| `test_structure_swift_sources_todo_fixme_ledgers.py` | 3 | TODO/FIXME parsing, apply behavior, link rendering, stable IDs, and CLI JSON. | +| `test_structure_swift_sources_workflow.py` | 6 | Task inference, owner handoffs, runtime customization, and SwiftUI file-structure rules. | +| `test_swift_cleanup_skill_boundaries.py` | 2 | Keeps DocC work with its owning workflow from both cleanup entrypoints. | +| `test_swift_package_build_run_workflow.py` | 8 | Build planning, nested roots, resources/Metal routing, Xcode coexistence, handoffs, and blocked ambiguity. | +| `test_swift_package_extension_workflow.py` | 5 | Trait/macro/plugin planning, toolchain floor, Xcode coexistence, references, and routing. | +| `test_swift_package_testing_workflow.py` | 9 | Test planning/context, build/extension handoffs, coverage, accessibility/model scheduling, and blocked ambiguity. | +| `test_swiftui_app_architecture_workflow.py` | 6 | Scene/focus architecture, component ownership, preview rules, and handoffs. | +| `test_swiftui_component_audit_workflow.py` | 1 | Enforces declarative component ownership rather than external view-model indirection. | +| `test_tipkit_workflow.py` | 4 | Setup/presentation ownership, eligibility/test lifecycle, docs/handoffs, and discovery. | +| `test_tvos_workflows.py` | 4 | Focus/input, playback ownership, accessibility/device evidence, handoffs, and discovery. | +| `test_video_codec_processing_workflow.py` | 5 | Codec lifecycle, pixel/color/HDR fidelity, diagnostics, type ownership, metadata, and handoffs. | +| `test_vision_recognition_workflows.py` | 4 | Vision/Core ML ownership, provenance/evaluation, coordinate/confidence rules, metadata, and handoffs. | +| `test_xcode_build_run_workflow.py` | 9 | Build routing, workspace inference, customization, pbxproj mutation policy, XcodeGen ownership, and dependency recovery. | +| `test_xcode_coding_intelligence_workflow.py` | 8 | Setup/execution ownership, dated beta claims, system paths, MCP/ACP capability boundaries, and permissions. | +| `test_xcode_device_window_telemetry_debugger_workflows.py` | 4 | Device, window, telemetry, debugger, AgentDeck, privacy, and beta-loader ownership boundaries. | +| `test_xcode_localization_workflow.py` | 3 | String Catalog workflow, human-review/provenance limits, metadata, and discovery. | +| `test_xcode_testing_workflow.py` | 11 | Test routing, plans/context, mutation policy, accessibility/device evidence, XcodeGen, coverage, and model scheduling. | +| `test_xcode_toolchain_selection_guidance.py` | 3 | Explicit Xcode selection authority and stable/beta application boundaries. | +| `test_xcode_workspace_workflows.py` | 23 | Executable create/adopt/add/align behavior, canonical layout, pre-write blockers, evidence-preserving adoption, target/service routing, and workspace visibility. | + +## Retained Other Child Tests + +| File | Tests | Justification | +| --- | ---: | --- | +| Agent Engineering `test_design_agent_automation_workflow.py` | 7 | Protects automation workflow shape, triggers, approvals, tools, validation, and generated output. | +| Agent Engineering `test_design_agent_eval_workflow.py` | 5 | Protects evaluation scope, dataset/metric controls, execution routing, and report artifacts. | +| Agent Portability `test_bootstrap_skills_plugin_repo.py` | 4 | Exercises repository bootstrap output, idempotence, validation, and safe refusal behavior. | +| Agent Portability `test_sync_skills_repo_guidance.py` | 6 | Exercises guidance synchronization, preservation, validation commands, and error paths. | +| Agent Portability `test_agent_protocol_workflows.py` | 3 | Protects A2A/ACP role, security, and routing distinctions. | +| Cybersecurity `test_macos_security_handoffs.py` | 2 | Protects macOS platform-control and reverse-engineering handoffs. | +| Professional `test_dice_job_search_workflow.py` | 5 | Exercises the shipped search workflow's request, filtering, URL, and result contracts. | +| Python `test_build_python_agent_service_skill.py` | 3 | Protects local-first framework choice, exact model/approval disclosure, uv use, and bounded tool access. | +| Python `test_plugin_smoke.py` | 8 | Protects manifest/discovery, host-provided FastMCP docs, current workflow inventory, shared bootstrap ownership, and three scaffold generators. | +| Reverse Engineering `test_research_macos_security_control.py` | 2 | Protects evidence hierarchy, bounded probes, and owner handoffs. | + +## Validation Assets That Are Not Socket Gates + +These remain because they are executable parts of shipped skills, not duplicate +Socket CI: + +- `plugins/model-lab-skills/.../validate_experiment_manifest.py` validates user + experiment manifests and is exercised by the model-lab tests. +- `plugins/repository-skills/.../validate-all.sh`, its component dispatcher, and + the generic/Apple GitHub workflow templates are installed into other + repositories by `maintain-project-repo`; the repository-maintenance tests + exercise their generation and behavior. +- The corresponding root `skills/` copies are required runtime files in the + Hermes export. Source `tests/` directories are deliberately excluded because + they are maintainer-only and were redundant in the exported payload. + +## Removed Checks + +| Removed item | Reason | +| --- | --- | +| Three branch-added tests for `maintain-project-api` absence and centralized child caches/tooling | The user prohibited new tests and permanent-absence regression tests. | +| `test_retired_standalone_and_sync_skills_are_absent` | Only froze retired paths. | +| `test_retired_server_local_environment_templates_are_absent` | Only froze deleted templates. | +| `test_release_version_module_has_no_release_choreography_entrypoint` | Only proved removed attributes stayed absent. | +| Duplicate unified-workspace profile inventory test | The repository-maintenance suite already tests the actual accepted profile API and installer behavior. | +| `tests/test_root_agents_guidance.py` | Only synchronized historical prose about a removed local mirror and asserted its absence. | +| `plugins/apple-dev-skills/tests/test_customization_consolidation_review.py` (4 tests) | Locked a historical review document, exact counts, and completed roadmap milestones instead of live behavior. | +| Apple `test_roadmap_marks_milestone_complete` | Only locked a completed milestone. | +| Apple docs-validator pytest wrapper | Re-ran the same shell validator immediately after the full profile had already run it. | +| Python metadata pytest wrapper | Re-ran the same metadata validator immediately after the full profile had already run it. | +| Roadmap milestone assertions inside nine retained Apple discovery tests | Milestone completion is history; the useful metadata/discovery assertions remain. | +| Retired-name/path assertions in Python and unified-workspace tests | Current positive inventory and context behavior remain; deletion history is not a contract. | +| Historical/retired-path blocks in the Apple docs validator | They checked roadmap prose or permanent absence rather than current layout and ownership. | +| Nested Apple and Python workflow YAML files | Inert in this monorepo and duplicates of the root full profile. | +| Python `.github/scripts/validate_repo_docs.sh` | One-line forwarding wrapper to the directly invoked metadata validator. | +| Exported `skills/*/tests` copies (2 directories, 10 duplicate definitions) | Not collected by Socket and not needed by Hermes consumers; authored source tests remain and still run. | + +The collected full-suite count changed from 447 to 432: 12 pre-existing +unjustified tests and the 3 newly added tests were removed. No test was added. diff --git a/docs/releases/v10.0.1.md b/docs/releases/v10.0.1.md index de9a62f7d..ec0fc3f9f 100644 --- a/docs/releases/v10.0.1.md +++ b/docs/releases/v10.0.1.md @@ -13,6 +13,9 @@ - Permanently removes `repository-skills:maintain-project-api` from the authored plugin, Hermes export, inventory, automation guidance, and coordinated docs dispatcher. +- Audits every Socket validation gate and test file, removes 12 unjustified + pre-existing tests, removes 3 tests introduced during this patch, and drops + inert nested CI plus maintainer-only tests from the Hermes payload. ## Breaking Changes @@ -34,3 +37,4 @@ cache paths. - `uv run python -B scripts/validate_socket.py --profile full` - Post-validation inventory confirmed no plugin-local `.venv`, `__pycache__`, `.pytest_cache`, `.mypy_cache`, or `.ruff_cache` directories were recreated. +- The reduced full profile passed all 22 gates and collected 432 tests. diff --git a/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh b/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh index 1c24c9dc7..ce3abf59d 100644 --- a/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh +++ b/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh @@ -15,26 +15,15 @@ require_contains() { grep -Fq -- "$needle" "$file" || fail "Missing required string in $file: $needle" } -require_not_contains() { - local file="$1" - local needle="$2" - ! grep -Fq -- "$needle" "$file" || fail "Unexpected stale string in $file: $needle" -} - -echo "Validating roadmap presence..." -[[ -f ROADMAP.md ]] || fail "Missing ROADMAP.md at repo root." - echo "Validating root docs presence..." [[ -f README.md ]] || fail "Missing README.md at repo root." [[ -f CONTRIBUTING.md ]] || fail "Missing CONTRIBUTING.md at repo root." [[ -f AGENTS.md ]] || fail "Missing AGENTS.md at repo root." [[ -f docs/maintainers/reality-audit.md ]] || fail "Missing docs/maintainers/reality-audit.md." -[[ -f docs/maintainers/customization-consolidation-review.md ]] || fail "Missing docs/maintainers/customization-consolidation-review.md." echo "Validating local discovery mirrors..." [[ -L ".agents/skills" ]] || fail "Expected .agents/skills to be a symlink to ../skills" [[ "$(readlink .agents/skills)" == "../skills" ]] || fail "Expected .agents/skills -> ../skills" -[[ ! -e "plugins/apple-dev-skills" ]] || fail "Did not expect a nested plugins/apple-dev-skills tree." echo "Validating root README contract..." require_contains "README.md" 'Treat `repository-skills` as the default baseline layer for general repo-doc and maintenance work' @@ -45,7 +34,6 @@ require_contains "README.md" 'Treat root [`skills/`](./skills/) as the canonical require_contains "README.md" 'Keep shared reusable assets in [`shared/`](./shared/)' require_contains "README.md" 'Run the repository test suite for skill and metadata changes:' require_contains "README.md" 'Use [`CONTRIBUTING.md`](./CONTRIBUTING.md) for maintainer workflow details' -require_not_contains "README.md" 'install-plugin-to-socket' echo "Validating CONTRIBUTING contract..." require_contains "CONTRIBUTING.md" 'Use this guide when preparing changes so the repository stays understandable, testable, and truthful about the Apple workflow surface it actually ships.' @@ -73,24 +61,6 @@ require_contains "$audit_doc" "## Reporting Shape" require_contains "$audit_doc" '`repository-skills` owns the reusable `maintain-project-repo` toolkit contract' require_contains "$audit_doc" 'standalone `apple-dev-skills` installs from installs that also include the `repository-skills` companion plugin' require_contains "$audit_doc" 'this repository owns only the Apple-specific profile selection and Xcode MCP registration contract' -require_contains "$audit_doc" 'Historical milestone planning decisions that no longer need standalone docs should live in `ROADMAP.md`' - -echo "Validating customization consolidation review..." -customization_review_doc="docs/maintainers/customization-consolidation-review.md" -require_contains "$customization_review_doc" "## Current State Summary" -require_contains "$customization_review_doc" "## Decision" -require_contains "$customization_review_doc" "## Knob Classification" -require_contains "$customization_review_doc" "## Shared Helper Decision" -require_contains "$customization_review_doc" "## Follow-Up Plan" -require_contains "$customization_review_doc" "Milestone 20 concludes that the repo should shrink the customization surface rather than expand it." -require_contains "$customization_review_doc" "## Sync Skill Simplification Decision" -require_contains "$customization_review_doc" 'implemented replacement: `writeMode`' -require_contains "ROADMAP.md" "Completed Milestones 22 and 23" -require_contains "ROADMAP.md" 'See `docs/maintainers/customization-consolidation-review.md`.' -require_contains "ROADMAP.md" "Completed Milestones 30 through 36" -require_contains "ROADMAP.md" "shrinking the customization surface" -require_contains "ROADMAP.md" "splitting execution workflows" -require_contains "ROADMAP.md" "preserving guidance through the refactor" echo "Validating skill directory layout..." active_skill_mds=( @@ -233,40 +203,7 @@ require_contains "$dash_skill_dir/references/dash_call_library.md" '## Dash MCP require_contains "$dash_skill_dir/references/dash_call_library.md" '## Dash Local HTTP Examples' require_contains "$dash_skill_dir/references/dash_call_library.md" '## High-Value Docset Targets' -echo "Validating stale installer and nested-packaging guidance is gone..." -for file in \ - "skills/swift-package-testing-workflow/SKILL.md" \ - "skills/swift-package-build-run-workflow/SKILL.md" \ - "skills/swift-package-extension-workflow/SKILL.md" \ - "skills/xcode-testing-workflow/SKILL.md" \ - "skills/xcode-build-run-workflow/SKILL.md" \ - "skills/xcode-coding-intelligence-workflow/SKILL.md" \ - "skills/author-swift-docc-docs/SKILL.md" \ - "skills/avfaudio-session-workflow/SKILL.md" \ - "skills/avaudio-engine-workflow/SKILL.md" \ - "skills/avfoundation-media-pipeline-workflow/SKILL.md" \ - "skills/coremedia-timing-samplebuffer-workflow/SKILL.md" \ - "skills/coreaudio-modernization-repair-workflow/SKILL.md" \ - "skills/core-animation-layer-workflow/SKILL.md" \ - "skills/apple-typography-workflow/SKILL.md" \ - "skills/sf-symbols-workflow/SKILL.md" \ - "skills/swiftui-animation-workflow/SKILL.md" \ - "skills/safari-extension-control-workflow/SKILL.md" \ - "skills/safari-mcp-workflow/SKILL.md" \ - "skills/app-extension-architecture-workflow/SKILL.md" \ - "skills/mailkit-workflow/SKILL.md" \ - "skills/file-provider-and-finder-sync-workflow/SKILL.md" \ - "skills/devicecheck-app-attest-workflow/SKILL.md" \ - "skills/appkit-app-architecture-workflow/SKILL.md" \ - "skills/swiftui-app-architecture-workflow/SKILL.md" \ - "ROADMAP.md" -do - require_not_contains "$file" 'install-plugin-to-socket' - require_not_contains "$file" 'plugins/apple-dev-skills/' -done - echo "Validating maintain-project-repo delegation..." -[[ ! -e "./shared/repo-maintenance-toolkit" ]] || fail "Did not expect apple-dev-skills to retain shared/repo-maintenance-toolkit." require_contains "./skills/bootstrap-xcode-workspace/SKILL.md" 'repository-skills:maintain-project-repo' require_contains "./skills/bootstrap-xcode-workspace/scripts/run_workflow.py" 'maintain-project-repo' diff --git a/plugins/apple-dev-skills/.github/workflows/validate-docs-and-skills.yml b/plugins/apple-dev-skills/.github/workflows/validate-docs-and-skills.yml deleted file mode 100644 index ce01e5c86..000000000 --- a/plugins/apple-dev-skills/.github/workflows/validate-docs-and-skills.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Validate Docs And Skills - -on: - pull_request: - push: - branches: - - main - - "codex/**" - -jobs: - validate: - runs-on: ubuntu-latest - steps: - - name: Check out repository - uses: actions/checkout@v6.0.2 - with: - fetch-depth: 0 - - - name: Set up uv - uses: astral-sh/setup-uv@v8.0.0 - - - name: Validate roadmap and skill layout - run: bash .github/scripts/validate_repo_docs.sh diff --git a/plugins/apple-dev-skills/tests/test_apple_developer_provisioning_workflow.py b/plugins/apple-dev-skills/tests/test_apple_developer_provisioning_workflow.py index 013587948..5cb0e0808 100644 --- a/plugins/apple-dev-skills/tests/test_apple_developer_provisioning_workflow.py +++ b/plugins/apple-dev-skills/tests/test_apple_developer_provisioning_workflow.py @@ -46,17 +46,15 @@ def test_cloudkit_paths_remain_local_and_token_safe(self) -> None: self.assertIn("pnpm add", cloudkit) self.assertIn("never put the token in source", cloudkit) - def test_inventory_metadata_and_roadmap_are_updated(self) -> None: + def test_inventory_and_metadata_are_updated(self) -> None: readme = self.read("README.md") plugin = self.read(".codex-plugin/plugin.json") validator = self.read(".github/scripts/validate_repo_docs.sh") - roadmap = self.read("ROADMAP.md") self.assertIn("apple-developer-provisioning-workflow", readme) self.assertIn("Apple Developer provisioning", plugin) self.assertIn("./skills/apple-developer-provisioning-workflow/SKILL.md", validator) self.assertIn("Expected exactly 58 active skills", validator) - self.assertIn("Milestone 54: Apple Developer Provisioning and CloudKit Workflow - Completed", roadmap) def test_customization_cli_preserves_shared_apply_and_reset_verbs(self) -> None: script = ROOT / "skills/apple-developer-provisioning-workflow/scripts/customization_config.py" diff --git a/plugins/apple-dev-skills/tests/test_core_animation_typography_workflows.py b/plugins/apple-dev-skills/tests/test_core_animation_typography_workflows.py index 211c84224..b25a996fc 100644 --- a/plugins/apple-dev-skills/tests/test_core_animation_typography_workflows.py +++ b/plugins/apple-dev-skills/tests/test_core_animation_typography_workflows.py @@ -90,14 +90,12 @@ def test_apple_typography_workflow_covers_dynamic_type_and_font_boundaries(self) def test_second_slice_inventory_is_wired_into_metadata_and_validation(self) -> None: readme = self.read("README.md") - roadmap = self.read("ROADMAP.md") validator = self.read(".github/scripts/validate_repo_docs.sh") plugin = self.read(".codex-plugin/plugin.json") for skill in ["core-animation-layer-workflow", "apple-typography-workflow"]: with self.subTest(skill=skill): self.assertIn(f"`{skill}`", readme) - self.assertIn(skill, roadmap) self.assertIn(f"./skills/{skill}/SKILL.md", validator) self.assertIn("Core Animation", plugin) diff --git a/plugins/apple-dev-skills/tests/test_customization_consolidation_review.py b/plugins/apple-dev-skills/tests/test_customization_consolidation_review.py deleted file mode 100644 index 5e2b2846e..000000000 --- a/plugins/apple-dev-skills/tests/test_customization_consolidation_review.py +++ /dev/null @@ -1,110 +0,0 @@ -from __future__ import annotations - -import re -import unittest -from pathlib import Path - - -ROOT = Path(__file__).resolve().parents[1] -REVIEW_DOC = ROOT / "docs/maintainers/customization-consolidation-review.md" -ROADMAP = ROOT / "ROADMAP.md" -TEMPLATE_PATHS = sorted(ROOT.glob("skills/*/references/customization.template.yaml")) -FLOW_PATHS = sorted(ROOT.glob("skills/*/references/customization-flow.md")) -SCRIPT_PATHS = sorted(ROOT.glob("skills/*/scripts/customization_config.py")) - - -def _count_template_knobs() -> int: - count = 0 - for path in TEMPLATE_PATHS: - for line in path.read_text(encoding="utf-8").splitlines(): - if line.startswith(" ") and ":" in line: - count += 1 - return count - - -def _count_statuses() -> tuple[int, int]: - runtime_enforced = 0 - policy_only = 0 - row_pattern = re.compile(r"^\| `[^`]+` \| `[^`]*` \| `([^`]+)` \|") - - for path in FLOW_PATHS: - for line in path.read_text(encoding="utf-8").splitlines(): - match = row_pattern.match(line) - if not match: - continue - status = match.group(1) - if status == "runtime-enforced": - runtime_enforced += 1 - elif status == "policy-only": - policy_only += 1 - else: - raise AssertionError(f"Unexpected customization status {status!r} in {path}") - - return runtime_enforced, policy_only - - -class CustomizationConsolidationReviewTests(unittest.TestCase): - def test_review_doc_exists_and_has_key_sections(self) -> None: - text = REVIEW_DOC.read_text(encoding="utf-8") - - for section in ( - "## Current State Summary", - "## Decision", - "## Knob Classification", - "## Sync Skill Simplification Decision", - "## Shared Helper Decision", - "## Follow-Up Plan", - "## Outcome", - ): - with self.subTest(section=section): - self.assertIn(section, text) - - def test_review_doc_counts_match_live_customization_surface(self) -> None: - text = REVIEW_DOC.read_text(encoding="utf-8") - template_count = len(TEMPLATE_PATHS) - script_count = len(SCRIPT_PATHS) - knob_count = _count_template_knobs() - runtime_enforced, policy_only = _count_statuses() - - self.assertEqual(template_count, 58) - self.assertEqual(script_count, 58) - self.assertEqual(knob_count, 12) - self.assertEqual(runtime_enforced, 11) - self.assertEqual(policy_only, 1) - - self.assertIn(f"The active skill surface ships `{template_count}` separate `references/customization.template.yaml` files.", text) - self.assertIn(f"The active skill surface ships `{script_count}` separate `scripts/customization_config.py` entrypoints.", text) - self.assertIn(f"The current templates expose `{knob_count}` knobs total:", text) - self.assertIn(f"- `{runtime_enforced}` are documented as `runtime-enforced`", text) - policy_line = ( - f"- `{policy_only}` is documented as `policy-only`" - if policy_only == 1 - else f"- `{policy_only}` are documented as `policy-only`" - ) - self.assertIn(policy_line, text) - - def test_review_doc_records_sync_write_mode_decision(self) -> None: - text = REVIEW_DOC.read_text(encoding="utf-8") - - self.assertIn("implemented replacement: `writeMode`", text) - for value in ( - "`sync-if-needed`", - "`create-missing-only`", - "`append-existing-only`", - "`report-only`", - ): - with self.subTest(value=value): - self.assertIn(value, text) - - def test_roadmap_marks_review_and_implementation_done(self) -> None: - text = ROADMAP.read_text(encoding="utf-8") - - self.assertIn("Completed Milestones 22 and 23", text) - self.assertIn("customization consolidation review", text) - self.assertIn("See `docs/maintainers/customization-consolidation-review.md`.", text) - self.assertIn("Completed Milestones 30 through 36", text) - self.assertIn("shrinking the customization surface", text) - - -if __name__ == "__main__": - unittest.main() diff --git a/plugins/apple-dev-skills/tests/test_design_animation_symbol_workflows.py b/plugins/apple-dev-skills/tests/test_design_animation_symbol_workflows.py index 055202c25..b33e862e5 100644 --- a/plugins/apple-dev-skills/tests/test_design_animation_symbol_workflows.py +++ b/plugins/apple-dev-skills/tests/test_design_animation_symbol_workflows.py @@ -92,14 +92,12 @@ def test_swiftui_animation_workflow_covers_motion_and_accessibility_boundaries(s def test_design_animation_inventory_is_wired_into_metadata_and_validation(self) -> None: readme = self.read("README.md") - roadmap = self.read("ROADMAP.md") validator = self.read(".github/scripts/validate_repo_docs.sh") plugin = self.read(".codex-plugin/plugin.json") for skill in ["sf-symbols-workflow", "swiftui-animation-workflow"]: with self.subTest(skill=skill): self.assertIn(f"`{skill}`", readme) - self.assertIn(skill, roadmap) self.assertIn(f"./skills/{skill}/SKILL.md", validator) self.assertIn("SF Symbols", plugin) diff --git a/plugins/apple-dev-skills/tests/test_devicecheck_app_attest_workflow.py b/plugins/apple-dev-skills/tests/test_devicecheck_app_attest_workflow.py index f42a86ac5..ba1416f92 100644 --- a/plugins/apple-dev-skills/tests/test_devicecheck_app_attest_workflow.py +++ b/plugins/apple-dev-skills/tests/test_devicecheck_app_attest_workflow.py @@ -65,14 +65,12 @@ def test_plugin_inventory_includes_devicecheck_workflow(self) -> None: readme = self.read("README.md") plugin = self.read(".codex-plugin/plugin.json") validator = self.read(".github/scripts/validate_repo_docs.sh") - roadmap = self.read("ROADMAP.md") self.assertIn("devicecheck-app-attest-workflow", readme) self.assertIn("DeviceCheck", plugin) self.assertIn("App Attest", plugin) self.assertIn("./skills/devicecheck-app-attest-workflow/SKILL.md", validator) self.assertIn("Expected exactly 58 active skills", validator) - self.assertIn("Milestone 53: DeviceCheck and App Attest Workflow - Completed", roadmap) if __name__ == "__main__": diff --git a/plugins/apple-dev-skills/tests/test_media_audio_workflows.py b/plugins/apple-dev-skills/tests/test_media_audio_workflows.py index eacd3e773..e65ec3aff 100644 --- a/plugins/apple-dev-skills/tests/test_media_audio_workflows.py +++ b/plugins/apple-dev-skills/tests/test_media_audio_workflows.py @@ -140,12 +140,10 @@ def test_reference_files_cover_repair_boundaries(self) -> None: def test_readme_and_validator_include_active_media_audio_skills(self) -> None: readme = self.read("README.md") validator = self.read(".github/scripts/validate_repo_docs.sh") - roadmap = self.read("ROADMAP.md") for skill in MEDIA_SKILLS: self.assertIn(f"`{skill}`", readme) self.assertIn(f"./skills/{skill}/SKILL.md", validator) - self.assertIn(skill, roadmap) def test_shared_media_type_ownership_contract_is_strict(self) -> None: text = self.read("shared/references/apple-media-type-ownership.md") diff --git a/plugins/apple-dev-skills/tests/test_repo_docs_validator.py b/plugins/apple-dev-skills/tests/test_repo_docs_validator.py deleted file mode 100644 index 98d8e77e0..000000000 --- a/plugins/apple-dev-skills/tests/test_repo_docs_validator.py +++ /dev/null @@ -1,26 +0,0 @@ -from __future__ import annotations - -import subprocess -import unittest -from pathlib import Path - - -ROOT = Path(__file__).resolve().parents[1] -VALIDATOR = ROOT / ".github/scripts/validate_repo_docs.sh" - - -class RepoDocsValidatorTests(unittest.TestCase): - def test_repo_docs_validator_passes(self) -> None: - proc = subprocess.run( - ["bash", str(VALIDATOR)], - cwd=ROOT, - capture_output=True, - text=True, - check=False, - ) - self.assertEqual(proc.returncode, 0, msg=proc.stderr or proc.stdout) - self.assertIn("All validation checks passed.", proc.stdout) - - -if __name__ == "__main__": - unittest.main() diff --git a/plugins/apple-dev-skills/tests/test_swift_package_extension_workflow.py b/plugins/apple-dev-skills/tests/test_swift_package_extension_workflow.py index f02f2b656..ca2d2186a 100644 --- a/plugins/apple-dev-skills/tests/test_swift_package_extension_workflow.py +++ b/plugins/apple-dev-skills/tests/test_swift_package_extension_workflow.py @@ -87,12 +87,5 @@ def test_existing_package_skills_route_extension_work(self) -> None: ): text = (ROOT / "skills" / name / "SKILL.md").read_text(encoding="utf-8") self.assertIn("swift-package-extension-workflow", text) - - def test_roadmap_marks_milestone_complete(self) -> None: - roadmap = (ROOT / "ROADMAP.md").read_text(encoding="utf-8") - self.assertIn("Milestone 41: Swift Package Extension Workflow - Completed", roadmap) - self.assertIn("Completed Milestone 41", roadmap) - - if __name__ == "__main__": unittest.main() diff --git a/plugins/apple-dev-skills/tests/test_tipkit_workflow.py b/plugins/apple-dev-skills/tests/test_tipkit_workflow.py index 7eb357a6d..7b17025bf 100644 --- a/plugins/apple-dev-skills/tests/test_tipkit_workflow.py +++ b/plugins/apple-dev-skills/tests/test_tipkit_workflow.py @@ -47,13 +47,11 @@ def test_inventory_and_metadata_include_tipkit(self) -> None: readme = self.read("README.md") plugin = self.read(".codex-plugin/plugin.json") validator = self.read(".github/scripts/validate_repo_docs.sh") - roadmap = self.read("ROADMAP.md") self.assertIn("tipkit-workflow", readme) self.assertIn("TipKit", plugin) self.assertIn("./skills/tipkit-workflow/SKILL.md", validator) self.assertIn("Expected exactly 58 active skills", validator) - self.assertIn("Milestone 55: TipKit Workflow", roadmap) if __name__ == "__main__": diff --git a/plugins/apple-dev-skills/tests/test_tvos_workflows.py b/plugins/apple-dev-skills/tests/test_tvos_workflows.py index b24a240be..028bc5717 100644 --- a/plugins/apple-dev-skills/tests/test_tvos_workflows.py +++ b/plugins/apple-dev-skills/tests/test_tvos_workflows.py @@ -60,13 +60,11 @@ def test_media_workflow_keeps_system_player_and_command_matrix_explicit(self) -> def test_discovery_and_portability_surfaces_list_both_workflows(self) -> None: readme = self.read("README.md") validator = self.read(".github/scripts/validate_repo_docs.sh") - roadmap = self.read("ROADMAP.md") for skill in ("tvos-app-experience-workflow", "tvos-media-playback-workflow"): with self.subTest(skill=skill): self.assertIn(f"`{skill}`", readme) self.assertIn(f"./skills/{skill}/SKILL.md", validator) - self.assertIn(skill, roadmap) if __name__ == "__main__": diff --git a/plugins/apple-dev-skills/tests/test_xcode_localization_workflow.py b/plugins/apple-dev-skills/tests/test_xcode_localization_workflow.py index 385f0d84f..800eb6ab9 100644 --- a/plugins/apple-dev-skills/tests/test_xcode_localization_workflow.py +++ b/plugins/apple-dev-skills/tests/test_xcode_localization_workflow.py @@ -43,13 +43,12 @@ def test_agent_translation_is_optional_and_beta_scoped(self) -> None: self.assertIn("optional beta-era workflow", agent) self.assertIn("machine-translation provenance", agent) - def test_inventory_metadata_and_roadmap_name_the_shipped_skill(self) -> None: + def test_inventory_and_metadata_name_the_shipped_skill(self) -> None: readme = self.read("README.md") manifest = self.read(".codex-plugin/plugin.json") - roadmap = self.read("ROADMAP.md") validator = self.read(".github/scripts/validate_repo_docs.sh") - for text in (readme, roadmap, validator): + for text in (readme, validator): self.assertIn("xcode-localization-workflow", text) self.assertIn("String Catalog localization", manifest) self.assertIn('"string-catalog"', manifest) diff --git a/plugins/python-skills/.github/scripts/validate_repo_docs.sh b/plugins/python-skills/.github/scripts/validate_repo_docs.sh deleted file mode 100755 index b489ab954..000000000 --- a/plugins/python-skills/.github/scripts/validate_repo_docs.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env zsh -set -euo pipefail - -cd "$(dirname "$0")/../.." -uv run scripts/validate_repo_metadata.py diff --git a/plugins/python-skills/.github/workflows/validate-python-skills.yml b/plugins/python-skills/.github/workflows/validate-python-skills.yml deleted file mode 100644 index fd6ec03e8..000000000 --- a/plugins/python-skills/.github/workflows/validate-python-skills.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Validate Python Skills - -on: - push: - branches: - - main - pull_request: - -jobs: - validate: - runs-on: ubuntu-latest - - steps: - - name: Check out repository - uses: actions/checkout@v6.0.2 - with: - fetch-depth: 0 - - - name: Set up uv - uses: astral-sh/setup-uv@v8.0.0 - - - name: Sync maintainer environment - run: uv sync --dev - - - name: Validate repo metadata - run: bash .github/scripts/validate_repo_docs.sh - - - name: Install zsh for scaffold smoke tests - run: sudo apt-get update && sudo apt-get install -y zsh - - - name: Run test suite - run: uv run pytest diff --git a/plugins/python-skills/tests/test_plugin_smoke.py b/plugins/python-skills/tests/test_plugin_smoke.py index 0db906d51..e5084b7bd 100644 --- a/plugins/python-skills/tests/test_plugin_smoke.py +++ b/plugins/python-skills/tests/test_plugin_smoke.py @@ -41,7 +41,6 @@ def test_plugin_manifest_and_marketplace_contract() -> None: assert ".codex-plugin/plugin.json" in agents_text assert "Root [`skills/`](./skills/) is the authored workflow surface" in agents_text - assert not (REPO_ROOT / "README.md").exists() def test_fastmcp_docs_tool_is_host_provided_not_packaged_dependency() -> None: @@ -54,7 +53,7 @@ def test_fastmcp_docs_tool_is_host_provided_not_packaged_dependency() -> None: assert "does not package that server" in skill -def test_service_and_testing_inventory_replaces_the_old_pytest_skill() -> None: +def test_service_and_testing_inventory_is_complete() -> None: expected = { "fastapi-service-workflow", "fastmcp-service-workflow", @@ -63,7 +62,6 @@ def test_service_and_testing_inventory_replaces_the_old_pytest_skill() -> None: actual = {path.parent.name for path in (REPO_ROOT / "skills").glob("*/SKILL.md")} assert expected <= actual - assert "uv-pytest-unit-testing" not in actual for skill_name in expected: assert (REPO_ROOT / "skills" / skill_name / "agents" / "openai.yaml").is_file() @@ -82,12 +80,11 @@ def test_bootstrap_skills_share_one_contract_reference() -> None: assert "../../shared/bootstrap-contract.md" in skill -def test_python_testing_scripts_use_the_replacement_profile_name() -> None: +def test_python_testing_scripts_use_the_shipped_profile_name() -> None: scripts_root = REPO_ROOT / "skills" / "python-testing-workflow" / "scripts" for script_name in ("bootstrap_pytest_uv.sh", "run_pytest_uv.sh"): script = (scripts_root / script_name).read_text() assert 'SKILL_NAME="python-testing-workflow"' in script - assert "uv-pytest-unit-testing" not in script def test_fastapi_scaffold_smoke(tmp_path: Path) -> None: diff --git a/plugins/python-skills/tests/test_validate_repo_metadata.py b/plugins/python-skills/tests/test_validate_repo_metadata.py deleted file mode 100644 index 74732b9d3..000000000 --- a/plugins/python-skills/tests/test_validate_repo_metadata.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import annotations - -from pathlib import Path -import sys - -sys.path.insert(0, str(Path(__file__).resolve().parents[1])) -from scripts.validate_repo_metadata import run - - -def test_repo_metadata_validation_passes() -> None: - repo_root = Path(__file__).resolve().parents[1] - findings = run(repo_root) - assert findings == [] diff --git a/scripts/export_hermes_skills.py b/scripts/export_hermes_skills.py index 4ec7e16d0..33fa5ff2f 100644 --- a/scripts/export_hermes_skills.py +++ b/scripts/export_hermes_skills.py @@ -198,6 +198,7 @@ + CLOUD_DEPLOYMENT_SKILLS + REPOSITORY_SKILLS ) +EXPORT_IGNORED_NAMES = ("tests", "__pycache__") class ExportError(RuntimeError): @@ -265,7 +266,11 @@ def write_export( staged_root = Path(temp_dir) / "skills" staged_root.mkdir() for skill_name in EXPORTED_SKILLS: - shutil.copytree(sources[skill_name], staged_root / skill_name) + shutil.copytree( + sources[skill_name], + staged_root / skill_name, + ignore=shutil.ignore_patterns(*EXPORT_IGNORED_NAMES, "*.pyc"), + ) if export_root.exists(): shutil.rmtree(export_root) staged_root.replace(export_root) @@ -282,8 +287,14 @@ def has_exact_export( export_names = {path.name for path in export_root.iterdir()} if set(sources) != set(EXPORTED_SKILLS) or export_names != set(EXPORTED_SKILLS): return False + if any(path.name in EXPORT_IGNORED_NAMES for path in export_root.rglob("*")): + return False for skill_name in EXPORTED_SKILLS: - comparison = filecmp.dircmp(sources[skill_name], export_root / skill_name) + comparison = filecmp.dircmp( + sources[skill_name], + export_root / skill_name, + ignore=list(EXPORT_IGNORED_NAMES), + ) if comparison.left_only or comparison.right_only or comparison.funny_files: return False for _, mismatches, errors in _walk_comparison(comparison): diff --git a/skills/bootstrap-skills-plugin-repo/tests/test_bootstrap_skills_plugin_repo.py b/skills/bootstrap-skills-plugin-repo/tests/test_bootstrap_skills_plugin_repo.py deleted file mode 100644 index 2f0065218..000000000 --- a/skills/bootstrap-skills-plugin-repo/tests/test_bootstrap_skills_plugin_repo.py +++ /dev/null @@ -1,60 +0,0 @@ -from __future__ import annotations - -import importlib.util -import os -import sys -from pathlib import Path - - -def _load_module(): - module_path = Path(__file__).resolve().parents[1] / "scripts" / "bootstrap_skills_plugin_repo.py" - spec = importlib.util.spec_from_file_location("bootstrap_skills_plugin_repo", module_path) - assert spec is not None - assert spec.loader is not None - mod = importlib.util.module_from_spec(spec) - sys.modules[spec.name] = mod - spec.loader.exec_module(mod) - return mod - - -m = _load_module() - - -def test_audit_repo_flags_missing_paths(tmp_path: Path) -> None: - findings = m.audit_repo(tmp_path, "example-skills") - - issue_ids = {finding.issue_id for finding in findings} - assert "missing-path" in issue_ids - assert "missing-symlink" in issue_ids - - -def test_apply_repo_creates_expected_discovery_mirrors(tmp_path: Path) -> None: - actions, created_paths = m.apply_repo(tmp_path, "example-skills") - - assert any(action["action"] == "create-symlink" for action in actions) - assert (tmp_path / ".agents" / "skills").is_symlink() - assert os.readlink(tmp_path / ".agents" / "skills") == "../skills" - assert "README.md" in created_paths - assert "AGENTS.md" in created_paths - agents_text = (tmp_path / "AGENTS.md").read_text(encoding="utf-8") - assert "check the current OpenAI Codex docs" in agents_text - assert "`hooks/`" in agents_text - assert "Default user-facing Codex plugin install and update guidance to Git-backed marketplace sources" in agents_text - assert "Resolve shared project dependencies only from GitHub repository URLs" in agents_text - assert "Machine-local dependency paths are expressly prohibited" in agents_text - - -def test_audit_repo_flags_forbidden_nested_plugin_dir(tmp_path: Path) -> None: - (tmp_path / "plugins").mkdir(parents=True) - - findings = m.audit_repo(tmp_path, "example-skills") - - assert any(finding.issue_id == "forbidden-path" for finding in findings) - - -def test_skill_routes_github_settings_to_repo_maintenance_owner() -> None: - skill_text = (Path(__file__).resolve().parents[1] / "SKILL.md").read_text(encoding="utf-8") - - assert "repository-skills:maintain-github-repository" in skill_text - assert "web commit sign-off when DCO applies" in skill_text - assert "documented maintainer direct-push workflow" in skill_text diff --git a/skills/sync-skills-repo-guidance/tests/test_sync_skills_repo_guidance.py b/skills/sync-skills-repo-guidance/tests/test_sync_skills_repo_guidance.py deleted file mode 100644 index 978b40f5c..000000000 --- a/skills/sync-skills-repo-guidance/tests/test_sync_skills_repo_guidance.py +++ /dev/null @@ -1,156 +0,0 @@ -from __future__ import annotations - -import importlib.util -import os -import sys -from pathlib import Path - - -def _load_module(): - module_path = Path(__file__).resolve().parents[1] / "scripts" / "sync_skills_repo_guidance.py" - spec = importlib.util.spec_from_file_location("sync_skills_repo_guidance", module_path) - assert spec is not None - assert spec.loader is not None - mod = importlib.util.module_from_spec(spec) - sys.modules[spec.name] = mod - spec.loader.exec_module(mod) - return mod - - -m = _load_module() - - -def _write_repo(repo_root: Path, _plugin_name: str) -> None: - (repo_root / "skills" / "example-skill").mkdir(parents=True) - (repo_root / "docs" / "maintainers").mkdir(parents=True) - (repo_root / "README.md").write_text( - "\n".join( - [ - "Installable maintainer skills for skills-export and plugin-export repositories.", - "OpenAI's documented Codex plugin system exposes repo-visible plugins through marketplace catalogs and does not document a richer repo-private scoping model beyond that.", - "codex plugin marketplace add gaelic-ghost/socket", - "codex plugin marketplace upgrade socket", - "`agent-portability-skills` entry points at `./plugins/agent-portability-skills`", - "Git-backed marketplace sources", - "dev dependencies in `pyproject.toml`", - "`pytest`, `ruff`, and `mypy`", - "`\"skills\": \"./skills/\"`", - "Only `plugin.json` belongs in `.codex-plugin/`", - "refresh the official OpenAI docs", - ] - ) - + "\n", - encoding="utf-8", - ) - (repo_root / "AGENTS.md").write_text( - "\n".join( - [ - "canonical authored and exported surface", - 'manifest points to bundled skills with `"skills": "./skills/"`', - "`hooks/`", - "Resolve shared project dependencies only from GitHub repository URLs, package managers, package registries, or other real remote repositories", - "Machine-local dependency paths are expressly prohibited in any project that is public or intended to be shared publicly", - "Default user-facing install and update guidance to Git-backed marketplace sources", - "`skills/install-plugin-to-socket`", - "`skills/validate-plugin-install-surfaces`", - "check the current OpenAI Codex docs", - ] - ) - + "\n", - encoding="utf-8", - ) - (repo_root / ".gitignore").write_text(".venv/\n", encoding="utf-8") - (repo_root / ".codex-plugin").mkdir() - (repo_root / ".codex-plugin" / "plugin.json").write_text('{"skills": "./skills/"}\n', encoding="utf-8") - (repo_root / "docs" / "maintainers" / "reality-audit.md").write_text( - "\n".join( - [ - "This repository ships root `.codex-plugin` packaging and does not track a nested staged plugin directory for itself.", - 'Its plugin manifest must declare `"skills": "./skills/"`', - "user installs normally come through the Git-backed `socket` marketplace", - "This repository does not ship `install-plugin-to-socket`.", - "This repository does not ship `validate-plugin-install-surfaces`.", - ] - ) - + "\n", - encoding="utf-8", - ) - (repo_root / "docs" / "maintainers" / "codex-plugin-install-surfaces.md").write_text( - "\n".join( - [ - "only `plugin.json` belongs in `.codex-plugin/`", - 'plugin manifests point to bundled skill folders with a root-relative `"skills": "./skills/"` field', - "Tracked marketplace source", - "Preferred User Install And Update Path", - "codex plugin marketplace add gaelic-ghost/socket", - "codex plugin marketplace upgrade socket", - "Documented plugin path: `~/.codex/config.toml`", - "If you mention project-scoped `.codex/config.toml`, label it as a general Codex config capability rather than part of the documented plugin install-surface map.", - "first route through the Codex harness surfaces that are already available in the current session", - "install the plugin through Codex's plugin directory for future sessions", - ] - ) - + "\n", - encoding="utf-8", - ) - (repo_root / ".agents").mkdir() - os.symlink("../skills", repo_root / ".agents" / "skills") - - -def test_audit_repo_accepts_expected_repo_shape(tmp_path: Path) -> None: - _write_repo(tmp_path, "example-skills") - - findings = m.audit_repo(tmp_path, "example-skills") - - assert findings == [] - - -def test_audit_repo_flags_missing_guidance_and_forbidden_path(tmp_path: Path) -> None: - (tmp_path / "plugins").mkdir(parents=True) - (tmp_path / "AGENTS.md").write_text("", encoding="utf-8") - (tmp_path / "docs" / "maintainers").mkdir(parents=True) - (tmp_path / "docs" / "maintainers" / "reality-audit.md").write_text("", encoding="utf-8") - - findings = m.audit_repo(tmp_path, "example-skills") - - issue_ids = {finding.issue_id for finding in findings} - assert "agents-missing-snippet" in issue_ids - assert "missing-symlink" in issue_ids - assert "forbidden-path" in issue_ids - assert "missing-plugin-manifest" in issue_ids - - -def test_audit_repo_accepts_missing_readme(tmp_path: Path) -> None: - _write_repo(tmp_path, "example-skills") - (tmp_path / "README.md").unlink() - - findings = m.audit_repo(tmp_path, "example-skills") - - assert findings == [] - - -def test_audit_repo_does_not_require_optional_maintainer_docs(tmp_path: Path) -> None: - _write_repo(tmp_path, "example-skills") - (tmp_path / "docs" / "maintainers" / "reality-audit.md").unlink() - (tmp_path / "docs" / "maintainers" / "codex-plugin-install-surfaces.md").unlink() - - findings = m.audit_repo(tmp_path, "example-skills") - - assert findings == [] - - -def test_audit_repo_flags_manifest_without_skills_component(tmp_path: Path) -> None: - _write_repo(tmp_path, "example-skills") - (tmp_path / ".codex-plugin" / "plugin.json").write_text("{}\n", encoding="utf-8") - - findings = m.audit_repo(tmp_path, "example-skills") - - assert any(finding.issue_id == "missing-skills-component" for finding in findings) - - -def test_skill_audits_github_settings_through_repo_maintenance_owner() -> None: - skill_text = (Path(__file__).resolve().parents[1] / "SKILL.md").read_text(encoding="utf-8") - - assert "repository-skills:maintain-github-repository" in skill_text - assert "Keep this audit read-only unless the user requested settings changes" in skill_text - assert "visibility changes" in skill_text diff --git a/tests/test_release_version.py b/tests/test_release_version.py index ed42f2188..2b8cf0f48 100644 --- a/tests/test_release_version.py +++ b/tests/test_release_version.py @@ -89,9 +89,3 @@ def test_apply_version_updates_manifests_and_adjacent_lockfiles(tmp_path: Path) assert "plugins/example/.codex-plugin/plugin.json" in changed assert "plugins/example/uv.lock" in changed assert 'version = "2.0.0"' in (root / "plugins/example/uv.lock").read_text() - - -def test_release_version_module_has_no_release_choreography_entrypoint() -> None: - assert not hasattr(release_version, "main") - assert not hasattr(release_version, "render_patch_refresh") - assert not hasattr(release_version, "release_notes") diff --git a/tests/test_repository_docs_workflow.py b/tests/test_repository_docs_workflow.py index cb8a24874..85e1a17f0 100644 --- a/tests/test_repository_docs_workflow.py +++ b/tests/test_repository_docs_workflow.py @@ -9,10 +9,7 @@ ROOT = Path(__file__).resolve().parents[1] -SCRIPT = ( - ROOT - / "plugins/repository-skills/skills/maintain-project-docs/scripts/maintain_project_docs.py" -) +SCRIPT = ROOT / "plugins/repository-skills/skills/maintain-project-docs/scripts/maintain_project_docs.py" spec = importlib.util.spec_from_file_location("maintain_project_docs", SCRIPT) assert spec is not None and spec.loader is not None @@ -22,22 +19,10 @@ class MaintainProjectDocsTests(unittest.TestCase): - def test_retired_api_skill_is_absent_from_source_export_and_dispatch(self) -> None: - self.assertFalse( - (ROOT / "plugins/repository-skills/skills/maintain-project-api").exists() - ) - self.assertFalse((ROOT / "skills/maintain-project-api").exists()) - self.assertNotIn( - "api", {workflow.key for workflow in module.DOCUMENT_WORKFLOWS} - ) - def test_select_workflows_preserves_canonical_order(self) -> None: selected, errors = module.select_workflows(None, None) self.assertEqual(errors, []) - self.assertEqual( - [workflow.key for workflow in selected], - ["readme", "contributing", "agents", "roadmap"], - ) + self.assertEqual([workflow.key for workflow in selected], ["readme", "contributing", "agents", "roadmap"]) def test_select_workflows_reports_unknown_keys(self) -> None: selected, errors = module.select_workflows("readme,unknown", "roadmap") @@ -57,12 +42,8 @@ def test_build_child_command_passes_ticket_flags_only_to_roadmap(self) -> None: "owner/repo", ] ) - readme_command = module.build_child_command( - args, module.DOCUMENT_WORKFLOWS[0], Path("/tmp/demo") - ) - roadmap_command = module.build_child_command( - args, module.DOCUMENT_WORKFLOWS[-1], Path("/tmp/demo") - ) + readme_command = module.build_child_command(args, module.DOCUMENT_WORKFLOWS[0], Path("/tmp/demo")) + roadmap_command = module.build_child_command(args, module.DOCUMENT_WORKFLOWS[-1], Path("/tmp/demo")) self.assertNotIn("--collect-source-tickets", readme_command) self.assertIn("--collect-source-tickets", roadmap_command) self.assertIn("--collect-github-issues", roadmap_command) @@ -71,16 +52,9 @@ def test_build_child_command_passes_ticket_flags_only_to_roadmap(self) -> None: def test_responsibility_audit_flags_cross_doc_drift(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) - (root / "README.md").write_text( - "# Demo\n\n## Contribution Workflow\n\nDo all the things.\n", - encoding="utf-8", - ) - (root / "ROADMAP.md").write_text( - "# Roadmap\n\n## Safety Boundaries\n\nDo not.\n", encoding="utf-8" - ) - issues = module.audit_responsibility_boundaries( - root, module.DOCUMENT_WORKFLOWS - ) + (root / "README.md").write_text("# Demo\n\n## Contribution Workflow\n\nDo all the things.\n", encoding="utf-8") + (root / "ROADMAP.md").write_text("# Roadmap\n\n## Safety Boundaries\n\nDo not.\n", encoding="utf-8") + issues = module.audit_responsibility_boundaries(root, module.DOCUMENT_WORKFLOWS) issue_ids = {issue["issue_id"] for issue in issues} self.assertIn("readme-contains-maintainer-workflow", issue_ids) self.assertIn("roadmap-contains-procedural-guidance", issue_ids) diff --git a/tests/test_root_agents_guidance.py b/tests/test_root_agents_guidance.py deleted file mode 100644 index 908cf4b51..000000000 --- a/tests/test_root_agents_guidance.py +++ /dev/null @@ -1,17 +0,0 @@ -from pathlib import Path - - -ROOT = Path(__file__).resolve().parents[1] - - -def test_live_docs_agree_that_speak_swiftly_has_no_local_mirror() -> None: - contributing = (ROOT / "CONTRIBUTING.md").read_text() - roadmap = (ROOT / "ROADMAP.md").read_text() - subtree_workflow = ( - ROOT / "docs" / "maintainers" / "subtree-workflow.md" - ).read_text() - - assert "does not keep a local `plugins/SpeakSwiftlyServer` mirror" in contributing - assert "retiring the local `plugins/SpeakSwiftlyServer/` mirror" in roadmap - assert "no longer imports the standalone source tree" in subtree_workflow - assert not (ROOT / "plugins" / "SpeakSwiftlyServer").exists() diff --git a/tests/test_unified_swift_workspace_contracts.py b/tests/test_unified_swift_workspace_contracts.py index 441ef6712..60f0ad158 100644 --- a/tests/test_unified_swift_workspace_contracts.py +++ b/tests/test_unified_swift_workspace_contracts.py @@ -10,50 +10,13 @@ def read(relative: str) -> str: return (ROOT / relative).read_text(encoding="utf-8") -def test_retired_standalone_and_sync_skills_are_absent() -> None: - for relative in ( - "plugins/apple-dev-skills/skills/bootstrap-swift-package", - "plugins/apple-dev-skills/skills/migrate-xcode-project-to-xcodegen", - "plugins/apple-dev-skills/skills/swift-package-workflow", - "plugins/apple-dev-skills/skills/xcode-app-project-workflow", - "plugins/apple-dev-skills/skills/sync-swift-package-guidance", - "plugins/server-side-swift/skills/apple-containerization-workflow", - "plugins/server-side-swift/skills/bootstrap-hummingbird-service", - "plugins/server-side-swift/skills/bootstrap-vapor-service", - "plugins/server-side-swift/skills/sync-hummingbird-service-guidance", - ): - assert not (ROOT / relative / "SKILL.md").exists() - - -def test_retired_server_local_environment_templates_are_absent() -> None: - template_root = ROOT / "plugins/server-side-swift/templates/codex-local-environments" - for name in ("hummingbird.toml", "vapor.toml", "swift-server-package.toml"): - assert not (template_root / name).exists() - - -def test_repository_maintenance_exposes_only_generic_and_workspace_profiles() -> None: - for relative in ( - "plugins/repository-skills/skills/maintain-project-repo/scripts/run_workflow.py", - "plugins/repository-skills/skills/maintain-project-repo/scripts/install_maintain_project_repo.py", - ): - text = read(relative) - assert '"generic"' in text - assert '"xcode-workspace"' in text - assert '"swift-package"' not in text - assert '"xcode-app"' not in text - - -def test_package_workflows_have_no_repo_classifier_or_mixed_root_opt_in() -> None: +def test_package_workflows_expose_package_context() -> None: for skill in ( "swift-package-build-run-workflow", "swift-package-testing-workflow", "swift-package-extension-workflow", ): script = read(f"plugins/apple-dev-skills/skills/{skill}/scripts/run_workflow.py") - assert "mixed_root" not in script - assert "mixed-root-opt-in" not in script - assert "repo_shape" not in script - assert "xcode_markers" not in script assert "package_context" in script @@ -67,8 +30,6 @@ def test_workspace_entrypoint_owns_all_component_roots() -> None: "ensure_services_surface", ): assert phrase in script - for retired_classifier in ("repo_shape", "mixed_root", "xcode_markers"): - assert retired_classifier not in script def test_active_xcode_guidance_uses_apps_peer_targets() -> None: diff --git a/tests/test_validate_socket.py b/tests/test_validate_socket.py index a6cc39f0f..91c38e240 100644 --- a/tests/test_validate_socket.py +++ b/tests/test_validate_socket.py @@ -54,33 +54,6 @@ def test_full_profile_adds_compatibility_and_child_checks_once() -> None: assert "release readiness" not in names -def test_child_python_checks_use_root_runtime_and_central_caches() -> None: - checks = [ - check for check in validate_socket.CHILD_CHECKS if check.command[0] != "bash" - ] - - assert checks - assert all(check.command[0] == sys.executable for check in checks) - assert all("uv" not in check.command for check in checks) - rendered = "\n".join(" ".join(check.command) for check in checks) - assert ".codex/.cache" in rendered - - -def test_monorepo_children_do_not_own_python_tooling_roots() -> None: - child_names = ( - "agent-engineering-skills", - "agent-portability-skills", - "apple-dev-skills", - "professional-skills", - "python-skills", - ) - - for child_name in child_names: - child_root = ROOT / "plugins" / child_name - assert not (child_root / "pyproject.toml").exists() - assert not (child_root / "uv.lock").exists() - - def test_validation_profiles_do_not_duplicate_release_choreography() -> None: source = (ROOT / "scripts" / "validate_socket.py").read_text(encoding="utf-8") @@ -111,9 +84,7 @@ def unexpected_run(*args: object, **kwargs: object) -> None: assert not called -def test_shared_skill_validator_accepts_valid_plugin( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> None: +def test_shared_skill_validator_accepts_valid_plugin(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: plugin_root = tmp_path / "plugins" / "example-skills" write(plugin_root / ".codex-plugin" / "plugin.json", "{}\n") write(plugin_root / "AGENTS.md", "# Guidance\n") @@ -151,10 +122,7 @@ def test_shared_skill_validator_rejects_directory_name_drift( [ ("interface: {}\n", "non-empty interface"), ("interface:\n default_prompt: Use this skill.\n", "invocation token"), - ( - "interface:\n default_prompt: Use $example-skill.\n display_name: ''\n", - "display_name", - ), + ("interface:\n default_prompt: Use $example-skill.\n display_name: ''\n", "display_name"), ], ) def test_shared_skill_validator_rejects_invalid_openai_interface( @@ -171,9 +139,7 @@ def test_shared_skill_validator_rejects_invalid_openai_interface( plugin_root / "skills" / "example-skill" / "SKILL.md", "---\nname: example-skill\ndescription: A valid skill.\n---\n", ) - write( - plugin_root / "skills" / "example-skill" / "agents" / "openai.yaml", interface - ) + write(plugin_root / "skills" / "example-skill" / "agents" / "openai.yaml", interface) monkeypatch.setattr(validate_skill_metadata, "REPO_ROOT", tmp_path) with pytest.raises(SystemExit):