Compare commits
12 Commits
660ca88e47
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 098a2418ee | |||
| 7f672b9c8f | |||
| 3c5efc6018 | |||
| 0b2d45d419 | |||
| 406fd63a07 | |||
| 323f02102d | |||
| 906bba427b | |||
| 06d5fc18f2 | |||
| 38edc4ac36 | |||
| c73771c2f4 | |||
| 33144da02f | |||
| 0a9dadd5a8 |
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: New Skill Configuration
|
||||
description: Route docs/skills edits to the Personal MCP new-skill resource.
|
||||
applyTo: 'docs/skills/**/*.md'
|
||||
---
|
||||
|
||||
When editing files under `docs/skills/`, use `resource://skills/new-skill/document` as the primary guidance source for skill structure and authoring decisions.
|
||||
|
||||
Execution pattern:
|
||||
|
||||
1. Load `resource://skills/new-skill/document` first.
|
||||
2. Apply only the portions relevant to the file being edited (`SKILL.md` or `references/*.md`).
|
||||
3. Keep edits minimal and aligned with repository skill conventions.
|
||||
4. Include source-document links for any feature-level recommendation.
|
||||
|
||||
If task intent is ambiguous, ask one clarifying question before editing.
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: Pytest Scaffolding Guidance
|
||||
description: Route tests edits to the Personal MCP pytest-scaffolding resource.
|
||||
applyTo: 'tests/**'
|
||||
---
|
||||
|
||||
When editing files under `tests/`, use `resource://skills/pytest-scaffolding/document` as the primary guidance source for test scaffolding and pytest authoring decisions.
|
||||
|
||||
Execution pattern:
|
||||
|
||||
1. Load `resource://skills/pytest-scaffolding/document` first.
|
||||
2. Apply only the portions relevant to the file being edited.
|
||||
3. Keep tests focused, deterministic, and aligned with repository conventions.
|
||||
4. Include source-document links for any feature-level recommendation.
|
||||
|
||||
If task intent is ambiguous, ask one clarifying question before editing.
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: VS Code Configuration
|
||||
description: Route .vscode edits to the Personal MCP VS Code configuration skill resource.
|
||||
applyTo: '.vscode/**'
|
||||
---
|
||||
|
||||
When editing files under `.vscode/`, use `resource://skills/vscode-configuration/document` as the primary guidance source.
|
||||
|
||||
Execution pattern:
|
||||
|
||||
1. Load `resource://skills/vscode-configuration/document` first.
|
||||
2. Select only the matching reference page for the current file type:
|
||||
- `launch.json` -> debug launch configurations.
|
||||
- `tasks.json` -> tasks.json project tasks.
|
||||
- `mcp.json` -> mcp.json MCP server configuration.
|
||||
3. Prefer the smallest safe config change and keep settings explicit.
|
||||
4. Include source-document links for any feature-level recommendation.
|
||||
|
||||
If task intent is ambiguous, ask one clarifying question before editing.
|
||||
@@ -8,7 +8,7 @@ Create a docs-first FastMCP architecture where all Markdown remains in docs/ as
|
||||
3. Phase 1: Define URI contract with explicit break-and-replace policy. Recommend resource://catalog/skills_index, resource://catalog/skills/{skill_id}, resource://skills/{skill_id}/document, resource://skills/{skill_id}/references/{ref_id}, and resource://docs/{path*}. Evolving URIs and reference ids requires direct replacement, with no aliases or compatibility shims. Depends on steps 1-2. Deliverable: update the current docs/ directory with the finalized URI contract and break-and-replace policy from this step.
|
||||
4. Phase 2: Build a docs registry loader that reads packaged docs via importlib.resources.files(...) Traversable APIs, parses SKILL.md frontmatter, validates schema, and creates an in-memory registry keyed by skill_id. Fail fast for duplicate ids, missing files, broken reference mappings, or invalid depends_on. Depends on steps 2-3.
|
||||
5. Phase 2: Register FastMCP resources from the registry using RFC6570 templates (including wildcard paths where appropriate), read-only/idempotent annotations, explicit mime types, and on_duplicate_resources="error" for startup safety. Depends on step 4.
|
||||
6. Phase 2: Add discovery surfaces as resources first, then tool fallback. Keep catalog discovery in resources, then add ResourcesAsTools for tool-only clients. Add thin discovery tools only for parity and optional BM25/regex tool search when catalog/tool volume grows enough to affect token efficiency. Depends on step 5.
|
||||
6. Phase 2: Add discovery surfaces as resources first, then tool fallback. Keep catalog discovery in resources, then add ResourcesAsTools for tool-only clients. Add thin discovery tools only for parity and optional BM25/regex tool search when catalog/tool volume grows enough to affect token efficiency. Define canonical fallback tool names (`list_resources`, `read_resource`, `search_patterns`, `get_pattern_by_id`, `get_skill_document_by_id`), research host-specific naming behavior for GitHub Copilot, Cursor, Claude Desktop, and generic MCP clients, and require client-side name mapping or intentionally documented aliases when providers expose namespaced wrappers. Depends on step 5.
|
||||
7. Phase 3: Implement packaging so docs/ is copied into package resource space at build time (wheel + sdist) while docs/ remains canonical in source control. Use importlib.resources at runtime only; avoid direct filesystem assumptions. Depends on steps 4-6.
|
||||
8. Phase 3: Remove materialization coupling between skill source modules and docs. The website build reads docs/ directly, while MCP reads packaged docs resources from the installed package. This preserves one authored source with two distribution surfaces. Depends on step 7.
|
||||
9. Phase 4: Add validation and CI gates: frontmatter schema checks, URI uniqueness checks, reference integrity checks, docs build check, package content check, and stdio smoke checks that read representative skill/document resources from an installed wheel. Depends on steps 5-8.
|
||||
@@ -31,7 +31,7 @@ Create a docs-first FastMCP architecture where all Markdown remains in docs/ as
|
||||
2. Run uv run pytest -q with tests that validate frontmatter parsing, URI generation, reference mapping, and catalog responses.
|
||||
3. Run a packaging integrity check using importlib.resources.files(...) to confirm packaged docs resources exist and are readable from an installed wheel.
|
||||
4. Run a stdio MCP smoke test that lists resources and reads at least one skill document and one reference document.
|
||||
5. Run fallback-client smoke tests verifying list_resources/read_resource tools work and return expected metadata for both static and templated resources.
|
||||
5. Run fallback-client smoke tests verifying list_resources/read_resource tools work and return expected metadata for both static and templated resources, and that GitHub Copilot, Cursor, Claude Desktop, and protocol-level SDK tests use canonical tool names or documented mapped aliases.
|
||||
|
||||
**Decisions**
|
||||
- Anthropic compatibility: strict skill directory pattern with SKILL.md and references subtree.
|
||||
|
||||
@@ -23,6 +23,41 @@ Normative conclusions from those sources:
|
||||
3. Resources and tools must resolve to the same canonical authored markdown.
|
||||
4. Fallback behavior should keep context bounded and deterministic.
|
||||
|
||||
### FastMCP Source Baseline (Authoritative References)
|
||||
|
||||
Step 6 fallback behavior and compatibility-layer expectations align with:
|
||||
|
||||
1. [FastMCP server concepts](https://gofastmcp.com/servers/server)
|
||||
2. [FastMCP resources and resource templates](https://gofastmcp.com/servers/resources)
|
||||
3. [FastMCP resources-as-tools transform](https://gofastmcp.com/servers/transforms/resources-as-tools)
|
||||
4. [MCP specification: resources](https://modelcontextprotocol.io/specification/latest/server/resources)
|
||||
|
||||
Applied conclusions for this step:
|
||||
|
||||
1. Resource contracts remain canonical and should be surfaced directly when clients support resource attachment.
|
||||
2. Tool-first compatibility layers should wrap canonical resource reads rather than creating alternate authored-content stores.
|
||||
3. URI-template-backed resource identity remains stable across direct-resource and tool-compatibility access paths.
|
||||
|
||||
### Client Tool-Naming Research Baseline
|
||||
|
||||
Authoritative and client-specific references to verify during implementation:
|
||||
|
||||
1. [MCP specification: tools](https://modelcontextprotocol.io/specification/latest/server/tools)
|
||||
2. [MCP client concepts](https://modelcontextprotocol.io/docs/learn/client-concepts)
|
||||
3. [FastMCP tools](https://gofastmcp.com/servers/tools)
|
||||
4. [FastMCP resources-as-tools transform](https://gofastmcp.com/servers/transforms/resources-as-tools)
|
||||
5. [VS Code MCP servers](https://code.visualstudio.com/docs/agent-customization/mcp-servers)
|
||||
6. [VS Code MCP configuration reference](https://code.visualstudio.com/docs/agents/reference/mcp-configuration)
|
||||
7. [Cursor MCP documentation](https://docs.cursor.com/context/model-context-protocol)
|
||||
8. [Claude Desktop local MCP server setup](https://support.anthropic.com/en/articles/10949351-getting-started-with-local-mcp-servers-on-claude-desktop)
|
||||
|
||||
Baseline naming conclusions:
|
||||
|
||||
1. MCP protocol tool identity is the server-advertised `name` returned by `tools/list` and used in `tools/call`.
|
||||
2. FastMCP tool identity should be treated as the canonical server contract unless a tool is intentionally registered with an explicit alternate name.
|
||||
3. Clients and host integrations may display, namespace, or internally route tool names with provider-specific prefixes, but those wrappers are not canonical server tool names.
|
||||
4. Compatibility should be validated by observed `tools/list` and successful `tools/call` behavior in each target client rather than by assuming one global host naming convention.
|
||||
|
||||
### Discovery Priority Contract (Normative)
|
||||
|
||||
Preferred sequence for skill discovery and loading:
|
||||
@@ -58,6 +93,19 @@ The fallback tool surface includes:
|
||||
4. `get_pattern_by_id`
|
||||
5. `get_skill_document_by_id`
|
||||
|
||||
Canonical naming rule:
|
||||
|
||||
1. The server-level tool contract uses the exact registered FastMCP tool names above.
|
||||
2. Clients that expose provider-prefixed names (for example, namespaced wrappers) must map those names to the canonical server tool name before invocation.
|
||||
3. `catalog_get_skill_document_by_id` is not a canonical server tool name for this contract unless an explicit alias is intentionally registered.
|
||||
|
||||
Compatibility alias policy:
|
||||
|
||||
1. Prefer canonical server tool names over aliases.
|
||||
2. Add server-side aliases only when a major client cannot reliably map its wrapper name back to the canonical name.
|
||||
3. Any alias must be read-only, delegate to the same payload builder as the canonical tool, and be documented as compatibility-only.
|
||||
4. If aliases are added, canonical and alias tools must return byte-for-byte equivalent payloads for the same input.
|
||||
|
||||
Fallback order:
|
||||
|
||||
1. call `list_resources` to inspect canonical static/template resource surfaces
|
||||
@@ -70,6 +118,40 @@ Tool behavior requirements:
|
||||
2. deterministic ordering and bounded pagination
|
||||
3. explicit not-found responses (`found: false` style) where applicable
|
||||
4. payloads remain schema-aligned with catalog resources
|
||||
5. tool invocation examples and Copilot guidance must use canonical server tool names to avoid unknown-tool errors
|
||||
|
||||
### Major Client Compatibility Plan
|
||||
|
||||
Target clients and expected validation:
|
||||
|
||||
1. GitHub Copilot in VS Code
|
||||
- primary path: attach MCP resources when `MCP Resources...` is available
|
||||
- fallback path: call `list_resources`, `read_resource`, then canonical thin tools only when needed
|
||||
- validation: confirm Copilot-visible tool inventory includes or can invoke `list_resources`, `read_resource`, `search_patterns`, `get_pattern_by_id`, and `get_skill_document_by_id`
|
||||
- compatibility risk: host-generated wrapper names may differ from canonical FastMCP names; document any observed wrapper-to-canonical mapping
|
||||
2. Cursor
|
||||
- primary path: use the client MCP server configuration and resource/tool surfaces supported by the active Cursor version
|
||||
- fallback path: prefer resource-backed tools first, then canonical thin tools
|
||||
- validation: capture Cursor `tools/list` equivalent behavior and verify the canonical tool names or required host mappings
|
||||
- compatibility risk: Cursor may present MCP tools through its own UI labels or internal routing names
|
||||
3. Claude Desktop
|
||||
- primary path: configure the local MCP server and inspect advertised tools/resources in Claude Desktop
|
||||
- fallback path: invoke canonical server tool names exactly as returned by `tools/list`
|
||||
- validation: run a local smoke prompt that reads `resource://catalog/skills_index` and loads one skill document through `read_resource` or `get_skill_document_by_id`
|
||||
- compatibility risk: local server configuration and transport setup may fail before tool-name compatibility is tested
|
||||
4. Generic MCP clients and SDK-based tests
|
||||
- primary path: protocol-level `resources/list`, `resources/read`, `tools/list`, and `tools/call`
|
||||
- fallback path: none beyond the canonical tool contract
|
||||
- validation: automated smoke tests assert exact tool names returned by `tools/list` and successful calls for canonical names
|
||||
- compatibility risk: SDK/client libraries may expose helper names that differ from raw protocol names
|
||||
|
||||
Implementation checklist:
|
||||
|
||||
1. Capture each target client's advertised tool names before adding aliases.
|
||||
2. Prefer fixing documentation or client-side mapping when the server already advertises canonical names correctly.
|
||||
3. Add a server-side alias only for a confirmed major-client incompatibility.
|
||||
4. Add regression tests for canonical names, resource-backed tools, and any intentionally supported aliases.
|
||||
5. Keep public examples centered on `list_resources`/`read_resource` and canonical thin tool names.
|
||||
|
||||
### Resources-As-Tools Compatibility Layer
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
name: New Skill Bootstrap
|
||||
description: Create and fully implement a new docs-first skill in this repository.
|
||||
argument-hint: skill-id and goal for the new skill
|
||||
agent: agent
|
||||
---
|
||||
|
||||
# New Skill Bootstrap
|
||||
|
||||
Use the canonical bootstrap guidance in [docs/skills/new-skill/SKILL.md](../../docs/skills/new-skill/SKILL.md).
|
||||
|
||||
If the request is to create or implement a new skill, load that skill document and follow it as the source of truth.
|
||||
@@ -124,7 +124,7 @@ Repository indexing metadata is declared in `x-personal-mcp`:
|
||||
3. tags
|
||||
4. capabilities
|
||||
5. depends_on
|
||||
6. references map (ref id to relative path and optional metadata)
|
||||
6. optional references map (for nested entries, overrides, and aliases)
|
||||
|
||||
No `metadata.yaml` sidecar is part of the end-state contract.
|
||||
|
||||
@@ -143,7 +143,9 @@ For the full URI semantics, parameter validation rules, and compatibility policy
|
||||
Validation rules:
|
||||
|
||||
1. `skill_id` is lowercase kebab-case and must satisfy the stable skill id contract.
|
||||
2. `ref_id` is lowercase kebab-case and must be declared in the skill references manifest.
|
||||
2. `ref_id` is lowercase kebab-case and must resolve from either:
|
||||
- top-level auto-discovery of `references/*.md` filename stems, or
|
||||
- an explicit `x-personal-mcp.references` entry.
|
||||
3. `path*` resolves only to normalized markdown paths under `docs/`.
|
||||
|
||||
### Resource Registration Contract
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ Rules:
|
||||
|
||||
1. Skill metadata is embedded in YAML frontmatter in `SKILL.md`.
|
||||
2. No `metadata.yaml` sidecar exists in the end state.
|
||||
3. Reference lookup metadata, including reference id to relative path mappings, is declared from `SKILL.md` frontmatter rather than inferred as a hidden global convention.
|
||||
3. Reference lookup metadata is documented and explicit: top-level `references/*.md` are auto-discovered from filenames, while `SKILL.md` frontmatter declares overrides and nested mappings when needed.
|
||||
|
||||
## Skill Id Contract
|
||||
|
||||
|
||||
@@ -69,6 +69,15 @@ When resource attachment is unavailable in the active session, use ResourcesAsTo
|
||||
4. `get_pattern_by_id`
|
||||
5. `get_skill_document_by_id`
|
||||
|
||||
Canonical naming policy:
|
||||
|
||||
1. Prefer the five canonical tool names above in prompts and instructions.
|
||||
2. For compatibility with clients that emit `catalog_*` naming, the server also exposes:
|
||||
- `catalog_search_patterns`
|
||||
- `catalog_get_pattern_by_id`
|
||||
- `catalog_get_skill_document_by_id`
|
||||
3. Canonical and compatibility alias tools return equivalent payloads for the same input.
|
||||
|
||||
The first two are generated from the canonical resource surface and should be preferred in tool-only clients.
|
||||
|
||||
These should stay read-only, minimal, and schema-aligned with catalog resources.
|
||||
|
||||
@@ -107,6 +107,28 @@ Reference entry rules:
|
||||
6. `title` is an optional display label.
|
||||
7. Renaming `ref-id` values is allowed when needed; optional aliases may be used during transitions.
|
||||
|
||||
## Auto-Generated Reference IDs
|
||||
|
||||
Top-level markdown files directly under `references/` are auto-registered as MCP references even when `x-personal-mcp.references` is empty.
|
||||
|
||||
How `ref-id` is derived:
|
||||
|
||||
1. Start from the filename stem (without `.md`).
|
||||
2. Normalize to lowercase kebab-case.
|
||||
3. Publish at `resource://skills/<skill-id>/references/<ref-id>`.
|
||||
|
||||
Examples:
|
||||
|
||||
1. `references/ruff-docs.md` -> `ref-id: ruff-docs`
|
||||
2. `references/Ruff Integrations.md` -> `ref-id: ruff-integrations`
|
||||
3. `references/python_logging_docs.md` -> `ref-id: python-logging-docs`
|
||||
|
||||
When to use explicit `x-personal-mcp.references` entries:
|
||||
|
||||
1. The file is nested, for example `references/guides/ci.md`.
|
||||
2. You need to override defaults (`title`, `mime_type`, or custom `ref-id`).
|
||||
3. You need compatibility aliases during a rename.
|
||||
|
||||
## Validation Models
|
||||
|
||||
The normative model uses Pydantic v2 with change-friendly validation:
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
---
|
||||
icon: lucide/file-plus
|
||||
---
|
||||
|
||||
# Hooking Up a New Skill
|
||||
|
||||
Use this checklist to add a new skill in the docs-first model.
|
||||
|
||||
For the full contract details, see [Content Contract](./content.md), [Frontmatter Contract](./frontmatter.md), and [URI Contract](./uris.md).
|
||||
|
||||
## Canonical Skill Shape
|
||||
|
||||
Create one skill directory under `docs/skills/`:
|
||||
|
||||
```text
|
||||
docs/
|
||||
skills/
|
||||
<skill-id>/
|
||||
SKILL.md
|
||||
references/
|
||||
... (optional markdown files, nested folders allowed)
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
1. `SKILL.md` is required.
|
||||
2. All skill-specific supporting docs live under `references/`.
|
||||
3. Skill directories are ownership boundaries; no cross-skill writes.
|
||||
4. `skill-id` is lowercase kebab-case and should remain stable.
|
||||
|
||||
## SKILL.md Frontmatter
|
||||
|
||||
`SKILL.md` frontmatter is authoritative for metadata.
|
||||
|
||||
Required top-level fields:
|
||||
|
||||
1. `name`
|
||||
2. `description`
|
||||
3. `x-personal-mcp`
|
||||
|
||||
Required `x-personal-mcp` fields:
|
||||
|
||||
1. `id`
|
||||
2. `version`
|
||||
3. `capabilities`
|
||||
|
||||
Optional `x-personal-mcp` fields:
|
||||
|
||||
1. `tags`
|
||||
2. `depends_on`
|
||||
3. `references`
|
||||
|
||||
Canonical frontmatter template:
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: <skill-id>
|
||||
description: <what this skill does and when to use it>
|
||||
|
||||
x-personal-mcp:
|
||||
id: <skill-id>
|
||||
version: 1.0.0
|
||||
tags: []
|
||||
capabilities:
|
||||
- resource://skills/<skill-id>/document
|
||||
depends_on: []
|
||||
# Optional: only for nested references or metadata overrides.
|
||||
references:
|
||||
<ref-id>:
|
||||
path: references/<file>.md
|
||||
mime_type: text/markdown
|
||||
title: <optional short title>
|
||||
---
|
||||
```
|
||||
|
||||
Reference manifest rules:
|
||||
|
||||
1. `ref-id` is lowercase kebab-case.
|
||||
2. `path` is skill-relative and must stay under `references/`.
|
||||
3. Top-level `references/*.md` files are auto-discovered, and `ref-id` is derived from a normalized filename stem.
|
||||
4. Nested `references/**` markdown files must be declared explicitly.
|
||||
5. Reference paths are markdown files.
|
||||
|
||||
No `metadata.yaml` sidecar is part of this model.
|
||||
|
||||
## URI Surface
|
||||
|
||||
Canonical resource URIs for a skill:
|
||||
|
||||
1. `resource://skills/<skill_id>/document`
|
||||
2. `resource://skills/<skill_id>/references/<ref_id>`
|
||||
|
||||
Canonical discovery URIs:
|
||||
|
||||
1. `resource://catalog/skills_index`
|
||||
2. `resource://catalog/skills/{skill_id}`
|
||||
|
||||
Docs passthrough URI:
|
||||
|
||||
1. `resource://docs/{path*}`
|
||||
|
||||
Compatibility rule:
|
||||
|
||||
1. Keep URI families unversioned by default.
|
||||
2. For breaking changes, update clients to the canonical replacement URIs directly.
|
||||
|
||||
## Checklist
|
||||
|
||||
1. Create `docs/skills/<skill-id>/SKILL.md`.
|
||||
2. Add optional references under `docs/skills/<skill-id>/references/`.
|
||||
3. Populate frontmatter with `name`, `description`, and `x-personal-mcp` metadata.
|
||||
4. Ensure `x-personal-mcp.id` equals `name` and directory `<skill-id>`.
|
||||
5. Ensure `capabilities` includes `resource://skills/<skill-id>/document`.
|
||||
6. Add supporting docs under `references/`; top-level markdown files are exposed automatically.
|
||||
7. Declare `x-personal-mcp.references` only for nested paths or to override defaults.
|
||||
|
||||
## Quick Validation
|
||||
|
||||
1. Confirm docs build succeeds:
|
||||
|
||||
```bash
|
||||
uv run zensical build
|
||||
```
|
||||
|
||||
2. Confirm tests succeed:
|
||||
|
||||
```bash
|
||||
uv run pytest -q
|
||||
```
|
||||
@@ -30,7 +30,12 @@ Use this skill when a task is about changing how GitHub Copilot or VS Code agent
|
||||
|
||||
## When to Use
|
||||
|
||||
- Creating or updating `.github/copilot-instructions.md`, `AGENTS.md`, `CLAUDE.md`, or `*.instructions.md` files.
|
||||
- Creating or updating:
|
||||
- `.github/copilot-instructions.md`
|
||||
- `AGENTS.md`
|
||||
- `CLAUDE.md`
|
||||
- `*.instructions.md` files
|
||||
- `*.prompt.md` files
|
||||
- Creating prompt files, custom agents, hooks, or Agent Skills.
|
||||
- Deciding whether behavior belongs in instructions, prompts, skills, agents, hooks, MCP servers, or agent plugins.
|
||||
- Debugging why a customization is not discovered, loaded, or invoked.
|
||||
@@ -55,6 +60,60 @@ If the request is ambiguous, ask only for the missing axis that changes the file
|
||||
|
||||
Use [VS Code customization references](./references/vscode-customization.md) for official-source details about locations, frontmatter, discovery behavior, priority, and troubleshooting.
|
||||
|
||||
## Repo Shim Pattern For Personal MCP
|
||||
|
||||
Use a shim when you want another repository to consume this server as a preference and documentation source without duplicating methodology content.
|
||||
|
||||
### What the shim does
|
||||
|
||||
1. Tells the agent when to consult this MCP server.
|
||||
2. Tells the agent how to retrieve relevant guidance.
|
||||
3. Keeps repo-local behavior thin while canonical guidance stays in Personal MCP resources.
|
||||
|
||||
### Shim formats
|
||||
|
||||
Use either:
|
||||
|
||||
1. A repo instruction file (`*.instructions.md`) for always-on or file-scoped behavior.
|
||||
2. A prompt file (`*.prompt.md`) for explicit, on-demand guidance retrieval.
|
||||
|
||||
### Retrieval strategies
|
||||
|
||||
Choose one of these patterns:
|
||||
|
||||
1. Direct URI strategy:
|
||||
- Reference known resources directly, such as:
|
||||
- `resource://catalog/skills_index`
|
||||
- `resource://catalog/skills/{skill_id}`
|
||||
- `resource://skills/<skill-id>/document`
|
||||
- `resource://skills/<skill-id>/references/<ref-id>`
|
||||
2. Discovery-first strategy:
|
||||
- Start at catalog discovery (`resource://catalog/skills_index`), select the best skill match, then load the skill document and only the minimal references needed.
|
||||
|
||||
### Authoring guidance for shims
|
||||
|
||||
1. Keep shim content short and procedural; avoid copying large guidance blocks from Personal MCP.
|
||||
2. State trigger conditions clearly (for example: "when creating a new skill" or "when editing docs contracts").
|
||||
3. Specify whether to use direct URIs or discovery for that repo's common workflows.
|
||||
4. Prefer loading only the most relevant skill document first; expand to references only when needed.
|
||||
5. For stable repeated workflows, use explicit URIs. For broader or ambiguous requests, use discovery-first.
|
||||
|
||||
### Minimal shim examples
|
||||
|
||||
Instruction-style shim intent:
|
||||
|
||||
1. "For markdown edits (`applyTo: '**/*.md'`), load `resource://skills/zensical-docs/document` and apply Zensical-native documentation conventions unless they conflict with expected MkDocs compatibility."
|
||||
|
||||
Prompt-style shim intent:
|
||||
|
||||
1. "For docs authoring tasks, consult `resource://skills/zensical-docs/document`, summarize the relevant authoring constraints, then propose the smallest markdown change for this repository."
|
||||
|
||||
### Validation for shim implementation
|
||||
|
||||
1. Confirm the shim triggers in expected contexts.
|
||||
2. Confirm resource loading path is unambiguous (direct URI or discovery).
|
||||
3. Confirm repo-local customization remains thin and references Personal MCP as source of truth.
|
||||
|
||||
## Workspace Customization Workflow
|
||||
|
||||
1. Identify the customization primitive and scope.
|
||||
@@ -66,20 +125,6 @@ Use [VS Code customization references](./references/vscode-customization.md) for
|
||||
7. For skills, make the folder name match the `name` field exactly and reference any extra files from `SKILL.md` with relative links.
|
||||
8. Validate placement, YAML frontmatter, discovery settings, and whether the customization should be workspace or user scoped.
|
||||
|
||||
## Repo Integration Workflow
|
||||
|
||||
When adding a new skill to this `personal-mcp` repo, follow the resource-first pattern:
|
||||
|
||||
1. Search the catalog for `new skill` and load `resource://skills/new-skill/document`.
|
||||
2. Create authored docs under `docs/skills/<skill-id>/SKILL.md`, with optional nested `references/` markdown files.
|
||||
3. Keep `skill-id` stable and consistent across directory name, `name`, and `x-personal-mcp.id`.
|
||||
4. Put discovery metadata in `SKILL.md` frontmatter under `x-personal-mcp`.
|
||||
5. Declare `resource://skills/<skill-id>/document` in `x-personal-mcp.capabilities`.
|
||||
6. Declare references in `x-personal-mcp.references` as `ref-id -> references/<file>.md` mappings.
|
||||
7. Validate with the registry loader and `uv run zensical build`.
|
||||
|
||||
Keep runtime implementation registry-driven in `src/personal_mcp/mcp.py`; do not add per-skill Python server modules.
|
||||
|
||||
## Quality Checks
|
||||
|
||||
Before finishing:
|
||||
|
||||
@@ -19,14 +19,125 @@ x-personal-mcp:
|
||||
|
||||
# New Skill Bootstrap
|
||||
|
||||
Use this skill to bootstrap a new skill in the docs-first architecture.
|
||||
Use this skill to bootstrap a new skill in the docs-first architecture. Try to use the `/create-skill` where possible to structure the output, but place it alongside the other skills in this repo.
|
||||
|
||||
## Inputs
|
||||
|
||||
1. New skill id (lowercase kebab-case)
|
||||
2. One-sentence capability statement (what it does and when to use it)
|
||||
3. Optional list of references to include under `references/`
|
||||
|
||||
## Source of Truth and Required References
|
||||
|
||||
1. Use this file as the baseline template for new skill authoring.
|
||||
2. Read and follow these docs before implementing a new skill:
|
||||
- [docs/architecture.md](../../architecture.md)
|
||||
- [docs/content.md](../../content.md)
|
||||
- [docs/frontmatter.md](../../frontmatter.md)
|
||||
- [docs/mcp_layout.md](../../mcp_layout.md)
|
||||
- [docs/uris.md](../../uris.md)
|
||||
|
||||
## Canonical Skill Shape
|
||||
|
||||
Create one skill directory under `docs/skills/`:
|
||||
|
||||
```text
|
||||
docs/
|
||||
skills/
|
||||
<skill-id>/
|
||||
SKILL.md
|
||||
references/
|
||||
... (optional markdown files, nested folders allowed)
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
1. `SKILL.md` is required.
|
||||
2. All skill-specific supporting docs live under `references/`.
|
||||
3. Skill directories are ownership boundaries; no cross-skill writes.
|
||||
4. `skill-id` is lowercase kebab-case and should remain stable.
|
||||
|
||||
### Framing
|
||||
|
||||
Phrasing and language in the skills should reflect the intent of providing preferences and reference documentation, rather than being for a migration or transition. When a particular resource is brought in, it should focus the general way something is done.
|
||||
|
||||
## SKILL.md Frontmatter Contract
|
||||
|
||||
`SKILL.md` frontmatter is authoritative for skill metadata.
|
||||
|
||||
Required top-level fields:
|
||||
|
||||
1. `name`
|
||||
2. `description`
|
||||
3. `x-personal-mcp`
|
||||
|
||||
Required `x-personal-mcp` fields:
|
||||
|
||||
1. `id`
|
||||
2. `version`
|
||||
3. `capabilities`
|
||||
|
||||
Optional `x-personal-mcp` fields:
|
||||
|
||||
1. `tags`
|
||||
2. `depends_on`
|
||||
3. `references`
|
||||
|
||||
Canonical frontmatter template:
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: <skill-id>
|
||||
description: <what this skill does and when to use it>
|
||||
|
||||
x-personal-mcp:
|
||||
id: <skill-id>
|
||||
version: 1.0.0
|
||||
tags: []
|
||||
capabilities:
|
||||
- resource://skills/<skill-id>/document
|
||||
depends_on: []
|
||||
# Optional: only for nested references or metadata overrides.
|
||||
references:
|
||||
<ref-id>:
|
||||
path: references/<file>.md
|
||||
mime_type: text/markdown
|
||||
title: <optional short title>
|
||||
---
|
||||
```
|
||||
|
||||
Reference manifest rules:
|
||||
|
||||
1. `ref-id` is lowercase kebab-case.
|
||||
2. `path` is skill-relative and must stay under `references/`.
|
||||
3. Top-level `references/*.md` files are auto-discovered, and `ref-id` is derived from a normalized filename stem.
|
||||
4. Nested `references/**` markdown files must be declared explicitly.
|
||||
5. Reference paths are markdown files.
|
||||
|
||||
## URI Surface
|
||||
|
||||
Canonical resource URIs for a skill:
|
||||
|
||||
1. `resource://skills/<skill_id>/document`
|
||||
2. `resource://skills/<skill_id>/references/<ref_id>`
|
||||
|
||||
Canonical discovery URIs:
|
||||
|
||||
1. `resource://catalog/skills_index`
|
||||
2. `resource://catalog/skills/{skill_id}`
|
||||
|
||||
Compatibility rule:
|
||||
|
||||
1. Keep URI families unversioned by default.
|
||||
2. For breaking changes, update clients to the canonical replacement URIs directly.
|
||||
|
||||
## Scope
|
||||
|
||||
1. Create docs under docs/skills/<skill-id>/.
|
||||
2. Define SKILL frontmatter with Anthropic and x-personal-mcp fields.
|
||||
3. Declare references via x-personal-mcp.references when needed.
|
||||
4. Validate the docs build and MCP resource reads.
|
||||
3. Treat top-level `references/*.md` as auto-discovered references with `ref-id` generated from filename.
|
||||
4. Declare `x-personal-mcp.references` only when you need overrides or nested `references/**` entries.
|
||||
5. Validate the docs build and MCP resource reads.
|
||||
|
||||
## Authoring Checklist
|
||||
|
||||
@@ -35,8 +146,26 @@ Use this skill to bootstrap a new skill in the docs-first architecture.
|
||||
3. Keep skill id and directory name aligned.
|
||||
4. Keep frontmatter name equal to x-personal-mcp.id.
|
||||
5. Include resource://skills/<skill-id>/document in capabilities.
|
||||
6. For each top-level `references/<name>.md`, expect `resource://skills/<skill-id>/references/<name>` (normalized to lowercase kebab-case).
|
||||
7. Add explicit `x-personal-mcp.references` entries only for nested paths or metadata overrides.
|
||||
|
||||
## Required Outcomes
|
||||
|
||||
1. Create `docs/skills/<skill-id>/SKILL.md` with valid frontmatter and a practical skill body.
|
||||
2. Create and populate `docs/skills/<skill-id>/references/` with any needed markdown references.
|
||||
3. Ensure frontmatter follows repository contract, including `x-personal-mcp` fields and canonical capabilities.
|
||||
4. Keep URI and reference mapping consistent with repository conventions.
|
||||
5. Reconcile all updates with repository implementation and avoid introducing parallel metadata systems.
|
||||
|
||||
## Validation
|
||||
|
||||
1. uv run zensical build
|
||||
2. uv run pytest -q
|
||||
|
||||
## Output Contract
|
||||
|
||||
Return:
|
||||
|
||||
1. Files created or updated
|
||||
2. Validation results
|
||||
3. Follow-up suggestions for improving the skill
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: pytest-scaffolding
|
||||
description: "Scaffold a maintainable, hierarchical pytest suite with fast defaults and clear escalation paths for FastAPI and SQLAlchemy tests. Use when creating or reorganizing tests, defining fixture/marker boundaries, or making test strategy progressively discoverable."
|
||||
description: "Reference hub for pytest suite structure, naming, markers, and stack-specific testing patterns. Use when you need best-practice guidance and source links for core pytest, FastAPI testing, and SQLAlchemy testing."
|
||||
argument-hint: "Target scope plus stack details (pure Python, FastAPI, SQLAlchemy sync, SQLAlchemy async, or mixed)"
|
||||
x-personal-mcp:
|
||||
id: pytest-scaffolding
|
||||
@@ -16,111 +16,67 @@ x-personal-mcp:
|
||||
|
||||
# Pytest Scaffolding
|
||||
|
||||
Create test scaffolding that stays fast for daily work and scales safely as dependencies increase.
|
||||
This skill is a collection of best-practice references and source-documentation links for building and maintaining pytest suites.
|
||||
|
||||
This skill is optimized for progressive discoverability:
|
||||
1. Start with the shortest path in this file.
|
||||
2. Load exactly one deeper reference only when a decision requires it.
|
||||
3. Continue only as far as needed for the current task.
|
||||
Use it to quickly find the right guidance for:
|
||||
1. Baseline pytest structure and marker strategy.
|
||||
2. Naming conventions and test hierarchy organization.
|
||||
3. FastAPI route, dependency override, and lifespan testing patterns.
|
||||
4. SQLAlchemy transaction and session testing patterns.
|
||||
|
||||
Repository defaults:
|
||||
- `uv run pytest` is the canonical invocation.
|
||||
- pytest settings live in `pyproject.toml` under `[tool.pytest.ini_options]`.
|
||||
- strict marker checking is expected (`--strict-markers`).
|
||||
|
||||
## Discovery Ladder
|
||||
## Reference Map
|
||||
|
||||
### Level 0: Scope And Stack Triage (always)
|
||||
Collect:
|
||||
1. Target scope (repo, package, module).
|
||||
2. Stack shape (pure Python, FastAPI, SQLAlchemy sync, SQLAlchemy async, or mixed).
|
||||
3. Speed target (what must stay instant).
|
||||
4. CI gate policy (which marker groups block merge).
|
||||
Use this map to open only the reference that matches your immediate need.
|
||||
|
||||
If any are missing, ask concise clarifying questions before scaffolding.
|
||||
- Core pytest practices and command patterns: [pytest-docs.md](./references/pytest-docs.md)
|
||||
- Naming conventions and hierarchy organization: [naming-and-organization.md](./references/naming-and-organization.md)
|
||||
- FastAPI-specific testing patterns: [fastapi-testing.md](./references/fastapi-testing.md)
|
||||
- SQLAlchemy-specific testing patterns: [sqlalchemy-testing.md](./references/sqlalchemy-testing.md)
|
||||
|
||||
### Level 1: Core pytest scaffold (default)
|
||||
Use this for all stacks first:
|
||||
1. Mirror `src/` into `tests/` with one starter file per core module.
|
||||
2. Classify test intent by cost:
|
||||
- `unit`: no DB/network/filesystem side effects.
|
||||
- `integration`: framework, DB, or multi-layer contracts.
|
||||
- `smoke`: thin critical-path checks.
|
||||
3. Scaffold each new module with:
|
||||
- one happy-path test,
|
||||
- one failure/edge test,
|
||||
- TODO anchors for deeper assertions.
|
||||
4. Keep fixtures layered:
|
||||
- global lightweight fixtures in `tests/conftest.py`,
|
||||
- domain fixtures in subtree `conftest.py` only when needed.
|
||||
5. Register markers early: `unit`, `integration`, `smoke`, `slow`, `external`.
|
||||
6. Validate in order:
|
||||
- `uv run pytest --collect-only -q`
|
||||
- `uv run pytest -m unit -q`
|
||||
- `uv run pytest -q` when dependencies are available.
|
||||
## Baseline Best Practices
|
||||
|
||||
Load next reference only if needed:
|
||||
- Baseline details and rationale: [pytest-docs.md](./references/pytest-docs.md)
|
||||
These are stable defaults regardless of stack:
|
||||
|
||||
### Level 2: FastAPI branch (only for HTTP/dependency/lifespan concerns)
|
||||
Escalate here when testing API routes, dependency injection boundaries, or app lifespan behavior.
|
||||
1. Mirror `src/` into `tests/` so ownership and coverage are obvious.
|
||||
2. Keep fixtures explicit and layered (`tests/conftest.py` globally, subtree `conftest.py` for domain-specific fixtures).
|
||||
3. Register markers up front (`unit`, `integration`, `smoke`, `slow`, `external`) and keep strict marker checks enabled.
|
||||
4. Separate fast feedback (`-m unit`) from broader integration/external lanes.
|
||||
5. Validate structure early with collection checks before expanding assertions.
|
||||
|
||||
Apply these defaults:
|
||||
1. Prefer `TestClient` with sync `def` tests for route behavior.
|
||||
2. Use `AsyncClient` + `@pytest.mark.anyio` only when test logic must await other async work.
|
||||
3. Prefer `app.dependency_overrides` over patching internals.
|
||||
4. Reset dependency overrides in teardown after every test/fixture.
|
||||
5. For startup/shutdown semantics:
|
||||
- use `TestClient` as context manager, or
|
||||
- use `LifespanManager` with async client.
|
||||
## Stack-Specific Guidance
|
||||
|
||||
Marker intent in FastAPI-heavy suites:
|
||||
- `unit`: service logic without HTTP/DB.
|
||||
- `integration`: route + DI + DB contract checks.
|
||||
- `smoke`: one request per critical user path.
|
||||
- For FastAPI, prefer dependency overrides and clear lifecycle handling; see [fastapi-testing.md](./references/fastapi-testing.md).
|
||||
- For SQLAlchemy, prefer transaction-safe session fixtures and explicit async loading strategy; see [sqlalchemy-testing.md](./references/sqlalchemy-testing.md).
|
||||
- For naming and tree organization, use the conventions in [naming-and-organization.md](./references/naming-and-organization.md).
|
||||
|
||||
Reference: [fastapi-testing.md](./references/fastapi-testing.md)
|
||||
## Source Documentation Entry Points
|
||||
|
||||
### Level 3: SQLAlchemy branch (only for DB transaction/session design)
|
||||
Escalate here when session lifecycle, transaction isolation, or async ORM behavior matters.
|
||||
Primary upstream docs are curated in each reference page. Start with:
|
||||
|
||||
Apply these defaults:
|
||||
1. Create engine once per test session.
|
||||
2. Open connection + outer transaction per test.
|
||||
3. Bind session with `join_transaction_mode="create_savepoint"`.
|
||||
4. Allow code under test to call `commit()` safely; rollback outer transaction at test end.
|
||||
5. Keep unit tests DB-free; DB tests belong under `integration`.
|
||||
1. Pytest good practices: [pytest docs](https://docs.pytest.org/en/stable/explanation/goodpractices.html)
|
||||
2. Pytest fixtures: [fixture how-to](https://docs.pytest.org/en/stable/how-to/fixtures.html)
|
||||
3. Pytest markers: [marker examples](https://docs.pytest.org/en/stable/example/markers.html)
|
||||
4. FastAPI testing: [FastAPI testing tutorial](https://fastapi.tiangolo.com/tutorial/testing/)
|
||||
5. SQLAlchemy transaction testing: [SQLAlchemy external transaction pattern](https://docs.sqlalchemy.org/en/20/orm/session_transaction.html#joining-a-session-into-an-external-transaction-such-as-for-test-suites)
|
||||
|
||||
Async additions:
|
||||
- use async fixtures and `@pytest.mark.anyio`.
|
||||
- set `expire_on_commit=False` for `AsyncSession`.
|
||||
- avoid implicit lazy IO; use eager loading (`selectinload`) or explicit refresh.
|
||||
## Quick Validation Commands
|
||||
|
||||
SQLite in-memory with threaded test clients:
|
||||
- use `StaticPool` when required by thread/connection sharing.
|
||||
Use these commands to check structure and execution lanes:
|
||||
|
||||
Reference: [sqlalchemy-testing.md](./references/sqlalchemy-testing.md)
|
||||
|
||||
## Branching Logic Summary
|
||||
- If pure logic can be faked cleanly, keep in `unit`.
|
||||
- If framework/DB contract is the behavior under test, use `integration`.
|
||||
- If external service credentials/network is required, gate behind `external`.
|
||||
- If suite slows down, split by marker before broadening fixture scope.
|
||||
- If async relationship access raises `MissingGreenlet`, switch to eager loading strategy.
|
||||
|
||||
## Completion Checks
|
||||
A scaffold pass is complete when all are true:
|
||||
1. Core source areas map to clear test modules.
|
||||
2. Fast path (`-m unit`) is deterministic and quick.
|
||||
3. Integration and external paths are isolated by fixtures and markers.
|
||||
4. No unregistered-marker failures occur.
|
||||
5. Structure is understandable without extra oral context.
|
||||
6. Clear TODO extension points exist for deeper assertions.
|
||||
1. `uv run pytest --collect-only -q`
|
||||
2. `uv run pytest -m unit -q`
|
||||
3. `uv run pytest -m "not external" -q`
|
||||
4. `uv run pytest -q`
|
||||
|
||||
## Output Contract
|
||||
When this skill is applied, return:
|
||||
1. Proposed test tree diff.
|
||||
2. Marker and fixture plan.
|
||||
3. Exact fast-path and full-path commands.
|
||||
4. Which reference level was loaded and why.
|
||||
5. Risks or open questions before expanding assertions.
|
||||
1. Which references were consulted.
|
||||
2. Recommended structure, naming, fixture, and marker decisions.
|
||||
3. Exact validation commands.
|
||||
4. Relevant source-doc links for any non-trivial recommendation.
|
||||
5. Risks, assumptions, or open questions.
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
# Pytest Naming Conventions and Test Organization
|
||||
|
||||
!!! info "Primary sources"
|
||||
- [Good integration practices](https://docs.pytest.org/en/stable/explanation/goodpractices.html)
|
||||
- [Changing standard (Python) test discovery](https://docs.pytest.org/en/stable/example/pythoncollection.html)
|
||||
- [How to use fixtures](https://docs.pytest.org/en/stable/how-to/fixtures.html)
|
||||
- [How to parametrize fixtures and test functions](https://docs.pytest.org/en/stable/how-to/parametrize.html)
|
||||
- [Marker examples](https://docs.pytest.org/en/stable/example/markers.html)
|
||||
|
||||
## Agent Quick Path
|
||||
Use this when creating or reorganizing test modules so naming and hierarchy stay predictable.
|
||||
|
||||
1. Mirror the product domain structure in `tests/` so ownership is obvious.
|
||||
2. Encode broad context in module and class names (`test_*.py`, `Test*`).
|
||||
3. Keep leaf test names short and behavior-focused (`test_*`).
|
||||
4. Use `class Test<Subject>:` only for grouping related scenarios.
|
||||
5. Place fixtures in the nearest `conftest.py` needed by scope.
|
||||
6. Separate expensive tests with markers first, directories second.
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
### File and directory naming
|
||||
- Use lowercase snake_case for test file names: `test_user_service.py`.
|
||||
- Keep directories domain-oriented and stable over time: `tests/orders/`, `tests/billing/`.
|
||||
- Prefer descriptive test names over internal ticket numbers or implementation details.
|
||||
|
||||
### Test function naming
|
||||
- Prefer hierarchical naming: put broad context in folder/module/class, and keep the function name focused on the final assertion.
|
||||
- Keep pytest discovery prefixes intact:
|
||||
- modules start with `test_`
|
||||
- classes start with `Test`
|
||||
- functions start with `test_`
|
||||
- Start with user-visible behavior or contract, not private helper names.
|
||||
|
||||
Recommended pattern:
|
||||
- module: `test_<subject>.py`
|
||||
- class: `Test<Operation>` or `Test<Scenario>`
|
||||
- function: `test_<expected_outcome>`
|
||||
|
||||
Examples:
|
||||
- Flat (still valid): `test_create_order_rejects_invalid_currency`
|
||||
- Class-context: `TestOrder -> TestCreate -> test_rejects_invalid_currency`
|
||||
- Module-context: `test_order.py -> TestCreate -> test_rejects_invalid_currency`
|
||||
- Module + class context can similarly shorten:
|
||||
- `test_token.py -> TestRefresh -> test_rotates_session_id`
|
||||
- `test_user_list.py -> TestListUsers -> test_returns_empty_for_new_tenant`
|
||||
|
||||
### Test class naming
|
||||
- Use `class Test<SubjectOrScenario>:` for scenario grouping and context reduction.
|
||||
- Keep class names noun-focused (`TestOrderService`) rather than action-focused.
|
||||
- Avoid xUnit style setup inheritance when fixtures can express dependencies directly.
|
||||
|
||||
## Hierarchy and Organization Patterns
|
||||
|
||||
Two patterns work well; choose one and apply it consistently.
|
||||
|
||||
### Pattern A: Source-mirror hierarchy (default for product code ownership)
|
||||
|
||||
```text
|
||||
src/
|
||||
app/
|
||||
orders/service.py
|
||||
billing/invoice.py
|
||||
|
||||
tests/
|
||||
app/
|
||||
orders/test_service.py
|
||||
billing/test_invoice.py
|
||||
```
|
||||
|
||||
Use this when teams own modules by source path and want direct test-to-source mapping.
|
||||
|
||||
### Pattern B: Cost-lane hierarchy (default for CI policy clarity)
|
||||
|
||||
```text
|
||||
tests/
|
||||
unit/
|
||||
orders/test_service.py
|
||||
integration/
|
||||
api/test_orders.py
|
||||
persistence/test_order_repository.py
|
||||
smoke/
|
||||
test_health.py
|
||||
```
|
||||
|
||||
Use this when CI gating is based on cost lanes and marker filtering.
|
||||
|
||||
### Hybrid rule (recommended)
|
||||
- Keep a source-mirror tree for local ownership.
|
||||
- Add markers (`unit`, `integration`, `smoke`, `external`) for runtime policy.
|
||||
- Avoid duplicating both trees unless the repository already requires it.
|
||||
|
||||
## Fixture Placement Strategy
|
||||
- Put universal lightweight fixtures in `tests/conftest.py`.
|
||||
- Put domain fixtures in subtree `conftest.py` files close to where they are used.
|
||||
- Keep fixtures composable and explicit; avoid large fixture "god objects".
|
||||
- Use `yield` fixtures for teardown so cleanup is always paired with setup.
|
||||
|
||||
## Parametrize and ID Naming
|
||||
- Use `pytest.mark.parametrize` for behavior matrices instead of copy/paste tests.
|
||||
- Provide explicit `ids=` labels when case names are not obvious.
|
||||
- Keep IDs business-meaningful (`"expired-token"`, `"zero-balance"`) so failures are readable.
|
||||
|
||||
## Collection and Structure Checks
|
||||
Use these checks after introducing new test files or renaming modules:
|
||||
|
||||
- `uv run pytest --collect-only -q`
|
||||
- `uv run pytest -m unit -q`
|
||||
- `uv run pytest -m "not external" -q`
|
||||
|
||||
If collection surprises appear, verify file names, marker registration, and directory placement first.
|
||||
|
||||
## Common Anti-Patterns
|
||||
- Mixed naming styles (`testFoo.py`, `test_foo.py`, `foo_test.py`) in one repository.
|
||||
- Deep fixture chains that hide setup behavior.
|
||||
- Test names that encode implementation details instead of behavior.
|
||||
- Moving slow tests into `unit` directories without marker updates.
|
||||
- Sharing mutable module-level state across tests.
|
||||
@@ -19,6 +19,7 @@ Use this file when you need fast pytest scaffolding defaults without framework-s
|
||||
Load other references only when needed:
|
||||
- FastAPI routes/dependency injection/lifespan: `fastapi-testing.md`
|
||||
- SQLAlchemy sessions/transactions/DB fixtures: `sqlalchemy-testing.md`
|
||||
- Naming conventions and test hierarchy: `naming-and-organization.md`
|
||||
|
||||
## Practical Guidance For This Skill
|
||||
- Use src-aligned test layout and keep test discovery conventional.
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
---
|
||||
name: ruff-linting-formating
|
||||
description: "Reference-first Ruff skill for repository preferences, baseline defaults, and source links. Use to pick consistent Ruff conventions and integration references, not to run migration playbooks."
|
||||
argument-hint: "Which Ruff preferences or integrations are you deciding (rules, formatting, pre-commit, GitHub Actions)?"
|
||||
x-personal-mcp:
|
||||
id: ruff-linting-formating
|
||||
version: 1.0.0
|
||||
tags:
|
||||
- ruff
|
||||
- linting
|
||||
- formatting
|
||||
- python
|
||||
- ci
|
||||
capabilities:
|
||||
- resource://skills/ruff-linting-formating/document
|
||||
depends_on: []
|
||||
---
|
||||
|
||||
# Ruff Preferences and References
|
||||
|
||||
Use this skill as a reference index for Ruff preferences, conventions, and source documentation.
|
||||
|
||||
This document is intentionally not a migration or transition playbook.
|
||||
|
||||
Load references only when needed:
|
||||
- Ruff core documentation: [Ruff docs](./references/ruff-docs.md)
|
||||
- Tooling integrations (pre-commit and GitHub Actions): [Ruff integrations](./references/ruff-integrations.md)
|
||||
|
||||
## When To Use
|
||||
|
||||
- You want canonical Ruff preferences for this repository context.
|
||||
- You need source links for rule selection, formatter behavior, and integrations.
|
||||
- You are deciding configuration defaults, not planning a migration sequence.
|
||||
|
||||
## Preference Baseline
|
||||
|
||||
Use these as default preferences unless the target repository states otherwise:
|
||||
|
||||
1. Keep linting and formatting both enabled.
|
||||
2. Keep imports sorted via Ruff (`I` rules) rather than a separate import tool.
|
||||
3. Prefer explicit, small rule-family selection first (`E`, `F`, `I`, `UP`) and expand deliberately.
|
||||
4. Keep line length, target Python, and formatter settings aligned to repository policy.
|
||||
5. Keep local and CI execution behavior equivalent.
|
||||
|
||||
### Rule Link Requirement
|
||||
|
||||
When adding a specific rule or ruleset to `ruff.toml`, search for the authoritative Ruff documentation page for that rule or ruleset and include a link to it. You may add the URL as a nearby comment in `ruff.toml` or record it in the repository docs (for example in a CONTRIBUTING or linting section). Prefer links to the official [Ruff rules reference](https://docs.astral.sh/ruff/rules/).
|
||||
|
||||
### Version Discovery Requirement
|
||||
|
||||
When integrating Ruff or any third-party Action for the first time, always search for the latest stable release of:
|
||||
|
||||
- the `ruff` package ([releases](https://github.com/astral-sh/ruff/releases))
|
||||
- the `astral-sh/ruff-pre-commit` hook ([releases](https://github.com/astral-sh/ruff-pre-commit/releases))
|
||||
- the `astral-sh/ruff-action` ([releases](https://github.com/astral-sh/ruff-action/releases))
|
||||
- the `astral-sh/setup-uv` action ([releases](https://github.com/astral-sh/setup-uv/releases))
|
||||
|
||||
Document the version you chose in the example snippet or in a nearby docs file and prefer pinning to a released tag in CI examples. If you intentionally use `latest`, note the reason and the associated risk in repo docs.
|
||||
|
||||
## Decision Inputs
|
||||
|
||||
Collect only the minimum context needed for preference decisions:
|
||||
|
||||
1. Supported Python versions.
|
||||
2. Existing `pyproject.toml` constraints.
|
||||
3. CI provider and required checks.
|
||||
4. Whether pre-commit is in use.
|
||||
|
||||
## Template
|
||||
|
||||
[Full template ruff.toml](https://gitea.john-stream.com/john/python-template/src/branch/main/project/ruff.toml)
|
||||
|
||||
```toml title="Preferred Baseline"
|
||||
line-length = 120
|
||||
indent-width = 4
|
||||
target-version = "py313"
|
||||
|
||||
exclude = [
|
||||
".git",
|
||||
".venv",
|
||||
".devenv",
|
||||
]
|
||||
|
||||
[lint]
|
||||
extend-fixable = ["ALL"]
|
||||
extend-select = [
|
||||
"C4", # https://docs.astral.sh/ruff/rules/#flake8-comprehensions-c4
|
||||
"E", "W", # https://docs.astral.sh/ruff/rules/#pycodestyle-e-w
|
||||
"F", # https://docs.astral.sh/ruff/rules/#pyflakes-f
|
||||
"FURB", # https://docs.astral.sh/ruff/rules/#refurb-furb
|
||||
"I", # https://docs.astral.sh/ruff/rules/#isort-i
|
||||
"N", # https://docs.astral.sh/ruff/rules/#pep8-naming-n
|
||||
"PD", # https://docs.astral.sh/ruff/rules/#pandas-vet-pd
|
||||
"PTH", # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth
|
||||
"UP", # https://docs.astral.sh/ruff/rules/#pyupgrade-up
|
||||
"SIM", # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim
|
||||
]
|
||||
|
||||
[lint.isort]
|
||||
force-single-line = true
|
||||
|
||||
[format]
|
||||
quote-style = "double"
|
||||
indent-style = "space"
|
||||
skip-magic-trailing-comma = false
|
||||
line-ending = "auto"
|
||||
```
|
||||
|
||||
## Reference Map
|
||||
|
||||
1. Rules and settings source of truth: [Ruff docs](./references/ruff-docs.md)
|
||||
2. pre-commit and GitHub Actions examples: [Ruff integrations](./references/ruff-integrations.md)
|
||||
3. Template to copy from or compare against: [python-template ruff.toml](https://gitea.john-stream.com/john/python-template/src/branch/main/project/ruff.toml)
|
||||
|
||||
## Non-Goals
|
||||
|
||||
This skill does not define:
|
||||
|
||||
1. Step-by-step migration phases.
|
||||
2. Rollout modes or cutover timelines.
|
||||
3. Mechanical rewrite plans for legacy tooling.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Ruff Source Documentation
|
||||
|
||||
Use this reference when implementing or tuning Ruff in repositories.
|
||||
|
||||
## Core Docs
|
||||
|
||||
- [Ruff overview](https://docs.astral.sh/ruff/)
|
||||
- [Rules reference](https://docs.astral.sh/ruff/rules/)
|
||||
- [Settings reference](https://docs.astral.sh/ruff/settings/)
|
||||
- [Formatter docs](https://docs.astral.sh/ruff/formatter/)
|
||||
- [The Ruff linter](https://docs.astral.sh/ruff/linter/)
|
||||
|
||||
## Migration And Integration
|
||||
|
||||
- [Migrating from Black](https://docs.astral.sh/ruff/formatter/#migrating-from-black)
|
||||
- [Migrating from Flake8](https://docs.astral.sh/ruff/linter/#migrating-from-flake8)
|
||||
- [Migrating from isort](https://docs.astral.sh/ruff/formatter/#sorting-imports)
|
||||
- [Pre-commit integration](https://docs.astral.sh/ruff/integrations/#pre-commit)
|
||||
- [GitHub Actions integration](https://docs.astral.sh/ruff/integrations/#github-actions)
|
||||
|
||||
## Python Packaging Context
|
||||
|
||||
- [PEP 621 project metadata in pyproject.toml](https://peps.python.org/pep-0621/)
|
||||
- [uv project and workflow docs](https://docs.astral.sh/uv/)
|
||||
|
||||
## Suggested Reading Order
|
||||
|
||||
1. Overview and settings.
|
||||
2. Rules and linter behavior.
|
||||
3. Formatter and migration references.
|
||||
4. CI and pre-commit integration notes.
|
||||
@@ -0,0 +1,128 @@
|
||||
# Ruff Integrations: Tooling Patterns
|
||||
|
||||
Use this page when wiring Ruff into local developer workflows and CI.
|
||||
|
||||
## Scope
|
||||
|
||||
This reference covers:
|
||||
|
||||
1. [pre-commit](https://pre-commit.com/) hooks for local and pre-push enforcement.
|
||||
2. [GitHub Actions](https://docs.github.com/en/actions) checks for pull request and branch protection gates.
|
||||
|
||||
For Ruff-specific flags and settings, see [Ruff docs](./ruff-docs.md).
|
||||
|
||||
## pre-commit Integration
|
||||
|
||||
### Why use it
|
||||
|
||||
Use pre-commit when you want fast feedback before code reaches CI and consistent checks across contributors.
|
||||
|
||||
### Add hooks
|
||||
|
||||
Create or update [.pre-commit-config.yaml](https://pre-commit.com/#2-add-a-pre-commit-configuration) with Ruff hooks from [astral-sh/ruff-pre-commit](https://github.com/astral-sh/ruff-pre-commit):
|
||||
|
||||
```yaml title=".pre-commit-config.yaml"
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.15.18
|
||||
hooks:
|
||||
- id: ruff-check
|
||||
args: [--fix]
|
||||
- id: ruff-format
|
||||
```
|
||||
|
||||
Pin the hook revision and update intentionally during dependency maintenance.
|
||||
|
||||
### Install and run
|
||||
|
||||
```bash
|
||||
uv run pre-commit install
|
||||
uv run pre-commit run --all-files
|
||||
```
|
||||
|
||||
If the project does not manage pre-commit via uv, use your standard Python environment installation path.
|
||||
|
||||
### Recommended policy
|
||||
|
||||
1. Keep auto-fix enabled locally with ruff-check --fix.
|
||||
2. Keep CI in check-only mode so violations fail loudly.
|
||||
3. Run hooks on all files in migration PRs to avoid drift.
|
||||
|
||||
## GitHub Actions Integration
|
||||
|
||||
### Why use it
|
||||
|
||||
Use GitHub Actions when you need required status checks on pull requests and a single source of truth for lint and format gates.
|
||||
|
||||
### Minimal workflow
|
||||
|
||||
Create [.github/workflows/ruff.yml](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions):
|
||||
|
||||
```yaml title=".github/workflows/ruff.yml"
|
||||
name: Ruff
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
ruff:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v8.2.0
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Install project dependencies
|
||||
run: uv sync --dev
|
||||
|
||||
- name: Ruff lint
|
||||
run: uv run ruff check .
|
||||
|
||||
- name: Ruff format check
|
||||
run: uv run ruff format --check .
|
||||
```
|
||||
|
||||
### Alternative: official Ruff action
|
||||
|
||||
If you want an action-focused setup, see [Ruff GitHub Actions integration](https://docs.astral.sh/ruff/integrations/#github-actions). The official Ruff action is commonly used pinned at `astral-sh/ruff-action@v4.0.0`. Keep behavior equivalent to local commands so results do not diverge.
|
||||
|
||||
## Alignment Checklist
|
||||
|
||||
Keep local hooks and CI checks aligned:
|
||||
|
||||
1. Same rule set from pyproject.toml.
|
||||
2. Same target Python version and dependency graph.
|
||||
3. Clear developer remediation command in docs:
|
||||
- uv run ruff check . --fix
|
||||
- uv run ruff format .
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Hook passes locally but CI fails
|
||||
|
||||
1. Ensure CI uses the same pyproject.toml and not a stale cache.
|
||||
2. Confirm matching Ruff versions in local and CI environments.
|
||||
3. Verify CI is not running on a different Python target than local config.
|
||||
|
||||
### CI is slow
|
||||
|
||||
1. Keep Ruff in a dedicated job so failures return early.
|
||||
2. Use dependency caching from your package workflow.
|
||||
3. Avoid running both legacy linters and Ruff after migration completion.
|
||||
|
||||
## Source Links
|
||||
|
||||
- [Ruff integrations](https://docs.astral.sh/ruff/integrations/)
|
||||
- [Ruff pre-commit docs](https://docs.astral.sh/ruff/integrations/#pre-commit)
|
||||
- [Ruff GitHub Actions docs](https://docs.astral.sh/ruff/integrations/#github-actions)
|
||||
- [pre-commit official docs](https://pre-commit.com/)
|
||||
- [GitHub Actions documentation](https://docs.github.com/en/actions)
|
||||
@@ -22,12 +22,20 @@ x-personal-mcp:
|
||||
|
||||
Use this skill to design or repair repeatable VS Code workspace configuration for local development workflows.
|
||||
|
||||
Primary VS Code source docs:
|
||||
|
||||
- [Python debugging in VS Code](https://code.visualstudio.com/docs/python/debugging)
|
||||
- [Debug configuration (`launch.json`)](https://code.visualstudio.com/docs/debugtest/debugging-configuration)
|
||||
- [Tasks (`tasks.json`)](https://code.visualstudio.com/docs/editor/tasks)
|
||||
- [MCP servers in VS Code](https://code.visualstudio.com/docs/agent-customization/mcp-servers)
|
||||
|
||||
## When to Use
|
||||
|
||||
- You need to create or fix `.vscode/launch.json` debug profiles.
|
||||
- You need robust Python debugging with `debugpy`.
|
||||
- You need FastAPI-specific launch profiles (app module, host/port, reload options, env files).
|
||||
- You need `.vscode/tasks.json` build/test/run tasks and optional debug pre-launch integration.
|
||||
- You need `.vscode/mcp.json` workspace or user profile MCP server configuration.
|
||||
- You need consistent workspace onboarding where users can run and debug from VS Code with minimal manual setup.
|
||||
|
||||
## Progressive References
|
||||
@@ -37,6 +45,7 @@ Load only the page that matches the current request:
|
||||
- Launch profile mechanics and debugpy patterns: [debug launch configurations](./references/debug-launch-configurations.md)
|
||||
- FastAPI-focused debug profiles using debugpy: [FastAPI + debugpy launch patterns](./references/fastapi-debugpy-launch.md)
|
||||
- Task runner setup in VS Code: [tasks.json project tasks](./references/tasks-json-configuration.md)
|
||||
- MCP server setup in VS Code: [mcp.json MCP server configuration](./references/mcp-server-configuration.md)
|
||||
|
||||
## Procedure
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Debug Launch Configurations in VS Code
|
||||
|
||||
This reference focuses on Python debugging through `debugpy` using `.vscode/launch.json`.
|
||||
This reference focuses on Python debugging through [`debugpy`](https://github.com/microsoft/debugpy) using [`.vscode/launch.json`](https://code.visualstudio.com/docs/debugtest/debugging-configuration).
|
||||
|
||||
## Core Structure
|
||||
|
||||
@@ -15,15 +15,15 @@ A minimal launch file:
|
||||
|
||||
Useful fields for Python configs:
|
||||
|
||||
- `type`: Use `debugpy`.
|
||||
- `type`: Use [`debugpy`](https://code.visualstudio.com/docs/python/debugging).
|
||||
- `request`: Usually `launch`, sometimes `attach`.
|
||||
- `name`: Friendly profile name shown in the Run and Debug panel.
|
||||
- `program`: Script path for script-based entry.
|
||||
- `module`: Module name for `python -m ...` style launches.
|
||||
- `args`: CLI arguments.
|
||||
- `cwd`: Working directory.
|
||||
- `env` / `envFile`: Environment variables.
|
||||
- `console`: `integratedTerminal` is usually most practical.
|
||||
- `cwd`: Working directory (supports [variable substitution](https://code.visualstudio.com/docs/editor/variables-reference)).
|
||||
- `env` / `envFile`: Environment variables (commonly from [environment variable definitions files](https://code.visualstudio.com/docs/python/environments#_environment-variable-definitions-file)).
|
||||
- `console`: `integratedTerminal` is usually most practical ([launch options](https://code.visualstudio.com/docs/debugtest/debugging-configuration#_launchjson-attributes)).
|
||||
- `justMyCode`: `true` by default; set `false` when stepping into dependencies.
|
||||
|
||||
## Launch vs Attach
|
||||
@@ -46,7 +46,7 @@ Attach profile example:
|
||||
}
|
||||
```
|
||||
|
||||
Remote process side command example:
|
||||
Remote process side command example (from [debugpy CLI usage](https://code.visualstudio.com/docs/python/debugging#_command-line-debugging)):
|
||||
|
||||
```bash
|
||||
python -m debugpy --listen 5678 -m your_package.main
|
||||
@@ -90,6 +90,13 @@ Prefer module mode when imports depend on package layout.
|
||||
- Keep secrets out of committed launch configs.
|
||||
- Ensure the selected VS Code interpreter matches project tooling.
|
||||
|
||||
## Source Documentation
|
||||
|
||||
- [Python debugging in VS Code](https://code.visualstudio.com/docs/python/debugging)
|
||||
- [Debug configuration and launch.json](https://code.visualstudio.com/docs/debugtest/debugging-configuration)
|
||||
- [Variables reference](https://code.visualstudio.com/docs/editor/variables-reference)
|
||||
- [debugpy project](https://github.com/microsoft/debugpy)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If breakpoints do not hit:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# FastAPI Debug Launch with debugpy
|
||||
|
||||
This reference provides practical `.vscode/launch.json` patterns for FastAPI applications started with uvicorn.
|
||||
This reference provides practical [`.vscode/launch.json`](https://code.visualstudio.com/docs/debugtest/debugging-configuration) patterns for [FastAPI](https://fastapi.tiangolo.com/) applications started with [uvicorn](https://www.uvicorn.org/).
|
||||
|
||||
## Launch FastAPI via Module
|
||||
|
||||
@@ -57,9 +57,11 @@ If app is created via factory function:
|
||||
}
|
||||
```
|
||||
|
||||
Factory mode is powered by uvicorn's [`--factory`](https://www.uvicorn.org/settings/#application) option.
|
||||
|
||||
## Attach to an Existing FastAPI Process
|
||||
|
||||
If the app is launched externally, start with debugpy:
|
||||
If the app is launched externally, start with [`debugpy`](https://code.visualstudio.com/docs/python/debugging#_command-line-debugging):
|
||||
|
||||
```bash
|
||||
python -m debugpy --listen 5678 -m uvicorn your_package.main:app --host 127.0.0.1 --port 8000 --reload
|
||||
@@ -96,3 +98,10 @@ A profile is considered valid when:
|
||||
2. A breakpoint inside an endpoint is hit on request.
|
||||
3. A breakpoint in startup/lifespan logic is hit at app boot.
|
||||
4. Terminal output appears in integrated terminal with expected log level.
|
||||
|
||||
## Source Documentation
|
||||
|
||||
- [FastAPI docs](https://fastapi.tiangolo.com/)
|
||||
- [Uvicorn settings and CLI options](https://www.uvicorn.org/settings/)
|
||||
- [Python debugging in VS Code](https://code.visualstudio.com/docs/python/debugging)
|
||||
- [Debug configuration and launch.json](https://code.visualstudio.com/docs/debugtest/debugging-configuration)
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
# Configure MCP Servers in VS Code
|
||||
|
||||
Use this reference to configure MCP servers for GitHub Copilot chat in VS Code with `.vscode/mcp.json` (workspace) or profile-level `mcp.json` (user scope).
|
||||
|
||||
## Where Configuration Lives
|
||||
|
||||
VS Code supports two MCP configuration locations:
|
||||
|
||||
- Workspace scope: `.vscode/mcp.json` in the repository.
|
||||
- User profile scope: open with the `MCP: Open User Configuration` command.
|
||||
|
||||
Use workspace scope for shared team configuration, and user scope for personal or machine-specific servers.
|
||||
|
||||
## Minimal mcp.json
|
||||
|
||||
```json
|
||||
{
|
||||
"servers": {
|
||||
"github": {
|
||||
"type": "http",
|
||||
"url": "https://api.githubcopilot.com/mcp"
|
||||
},
|
||||
"playwright": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@microsoft/mcp-server-playwright"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `servers` object keys are logical server names shown in VS Code MCP management surfaces.
|
||||
|
||||
## Add Servers Through VS Code UI
|
||||
|
||||
1. Run `MCP: Add Server` from the Command Palette.
|
||||
2. Choose Workspace or Global target.
|
||||
3. Review generated config in `mcp.json`.
|
||||
4. Start or restart the server from `MCP: List Servers`.
|
||||
|
||||
This guided flow is usually safer than manual edits when onboarding teammates.
|
||||
|
||||
## Security and Secrets
|
||||
|
||||
1. Do not hardcode tokens or API keys in `mcp.json`.
|
||||
2. Prefer input variables or environment-file patterns supported by the MCP configuration schema.
|
||||
3. Start only trusted servers, because local servers can execute code on your machine.
|
||||
4. Use trust prompts as a checkpoint instead of bypassing review.
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. Apply least privilege by default.
|
||||
2. Keep workspace `mcp.json` limited to team-safe, non-secret configuration.
|
||||
3. Keep personal credentials and machine-specific settings in user-scope configuration, not repository files.
|
||||
4. Prefer explicit allowlists for filesystem writes and outbound network access when sandboxing is enabled.
|
||||
5. Use one server per trust boundary instead of one large multi-purpose server.
|
||||
6. Review server `command` and `args` as code during pull requests.
|
||||
7. Disable or uninstall unused MCP servers to reduce attack surface.
|
||||
8. Use HTTPS endpoints for remote MCP servers whenever available.
|
||||
9. Pin server packages or versions where practical to avoid accidental supply-chain drift.
|
||||
10. Reset trust and re-review configuration after major server changes.
|
||||
|
||||
### Operational Guardrails
|
||||
|
||||
1. Treat MCP resources as publishable unless an explicit access control layer exists.
|
||||
2. Capture server logs during onboarding so failures and suspicious behavior are easier to detect.
|
||||
3. Define ownership for each server entry, including who approves changes and who rotates secrets.
|
||||
4. Document upgrade triggers: if a server starts reading private data or executing side-effectful actions, require stronger access controls before rollout.
|
||||
|
||||
### Team Review Checklist
|
||||
|
||||
Use this checklist before merging workspace MCP configuration changes:
|
||||
|
||||
1. No plaintext secrets in `mcp.json`.
|
||||
2. `command` and `args` are from trusted publishers and expected binaries.
|
||||
3. Server scope is correct (workspace vs user profile).
|
||||
4. Sandboxing is enabled for local `stdio` servers when supported.
|
||||
5. Sandbox allowlists are narrow (minimum paths and domains).
|
||||
6. The change includes an owner and rollback path.
|
||||
|
||||
## Sandbox Local stdio Servers (Linux/macOS)
|
||||
|
||||
For local `stdio` servers, enable sandboxing when possible:
|
||||
|
||||
```json
|
||||
{
|
||||
"servers": {
|
||||
"myServer": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@example/mcp-server"],
|
||||
"sandboxEnabled": true
|
||||
}
|
||||
},
|
||||
"sandbox": {
|
||||
"filesystem": {
|
||||
"allowWrite": ["${workspaceFolder}"]
|
||||
},
|
||||
"network": {
|
||||
"allowedDomains": ["api.example.com"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Sandboxing is currently available on Linux and macOS, not Windows.
|
||||
|
||||
## Troubleshooting Checklist
|
||||
|
||||
1. Open server logs from `MCP: List Servers` -> `Show Output`.
|
||||
2. Confirm trust state (or run `MCP: Reset Trust` if needed).
|
||||
3. Confirm server command and arguments run outside VS Code.
|
||||
4. Confirm workspace-vs-user scope matches where you expect the server to run.
|
||||
5. If using remote development, configure the server in the remote scope when needed.
|
||||
|
||||
## Source Documentation
|
||||
|
||||
- [Add and manage MCP servers in VS Code](https://code.visualstudio.com/docs/agent-customization/mcp-servers)
|
||||
- [MCP configuration reference](https://code.visualstudio.com/docs/agents/reference/mcp-configuration)
|
||||
- [Input variables for sensitive data](https://code.visualstudio.com/docs/agents/reference/mcp-configuration#_input-variables-for-sensitive-data)
|
||||
- [Sandbox configuration reference](https://code.visualstudio.com/docs/agents/reference/mcp-configuration#_sandbox-configuration)
|
||||
- [AI security guidance in VS Code](https://code.visualstudio.com/docs/agents/security)
|
||||
- [Model Context Protocol overview](https://modelcontextprotocol.io/docs/getting-started/intro)
|
||||
@@ -1,6 +1,6 @@
|
||||
# Configure Project Tasks in tasks.json
|
||||
|
||||
Use `.vscode/tasks.json` to define repeatable project commands and optional hooks for debugging.
|
||||
Use [`.vscode/tasks.json`](https://code.visualstudio.com/docs/editor/tasks) to define repeatable project commands and optional hooks for debugging.
|
||||
|
||||
## Minimal File
|
||||
|
||||
@@ -14,12 +14,12 @@ Use `.vscode/tasks.json` to define repeatable project commands and optional hook
|
||||
## Task Fields You Will Use Most
|
||||
|
||||
- `label`: Task name shown in VS Code.
|
||||
- `type`: Usually `shell`.
|
||||
- `type`: Usually [`shell`](https://code.visualstudio.com/docs/editor/tasks#_custom-tasks).
|
||||
- `command`: Executable to run.
|
||||
- `args`: Command arguments.
|
||||
- `options.cwd`: Working directory.
|
||||
- `group`: Mark default build or test tasks.
|
||||
- `problemMatcher`: Parse errors into the Problems panel.
|
||||
- `options.cwd`: Working directory (supports [variable substitution](https://code.visualstudio.com/docs/editor/variables-reference)).
|
||||
- `group`: Mark default build or test tasks ([task groups](https://code.visualstudio.com/docs/editor/tasks#_grouping-tasks)).
|
||||
- `problemMatcher`: Parse errors into the Problems panel ([problem matchers](https://code.visualstudio.com/docs/editor/tasks#_defining-a-problem-matcher)).
|
||||
- `isBackground`: `true` for long-running tasks (for example dev server watch).
|
||||
|
||||
## Python Project Example
|
||||
@@ -56,7 +56,7 @@ Use `.vscode/tasks.json` to define repeatable project commands and optional hook
|
||||
|
||||
## Connect Tasks to Debug Profiles
|
||||
|
||||
In `launch.json`, you can run a task first:
|
||||
In [`launch.json`](https://code.visualstudio.com/docs/debugtest/debugging-configuration), you can run a task first with [`preLaunchTask`](https://code.visualstudio.com/docs/debugtest/debugging-configuration#_launchjson-attributes):
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -90,3 +90,10 @@ If a task fails unexpectedly:
|
||||
3. Confirm tool availability in environment path.
|
||||
4. Confirm quoting and argument boundaries in `args`.
|
||||
5. Confirm the task is not blocked by an outdated background process.
|
||||
|
||||
## Source Documentation
|
||||
|
||||
- [VS Code Tasks (official)](https://code.visualstudio.com/docs/editor/tasks)
|
||||
- [Tasks Appendix (schema and interfaces)](https://code.visualstudio.com/docs/reference/tasks-appendix)
|
||||
- [Variables Reference](https://code.visualstudio.com/docs/editor/variables-reference)
|
||||
- [Debug configuration and launch.json](https://code.visualstudio.com/docs/debugtest/debugging-configuration)
|
||||
|
||||
@@ -204,6 +204,14 @@ Preferred tool fallback order:
|
||||
4. `get_pattern_by_id`
|
||||
5. `get_skill_document_by_id`
|
||||
|
||||
Compatibility aliases for clients that use `catalog_*` naming are also available:
|
||||
|
||||
1. `catalog_search_patterns`
|
||||
2. `catalog_get_pattern_by_id`
|
||||
3. `catalog_get_skill_document_by_id`
|
||||
|
||||
Use canonical names first; aliases exist only to preserve interoperability when a client emits non-canonical names.
|
||||
|
||||
If confidence is low after discovery, ask one clarifying question before loading more context.
|
||||
```
|
||||
|
||||
|
||||
@@ -26,3 +26,8 @@ dev = [
|
||||
"pre-commit>=4.6.0",
|
||||
"ruff>=0.15.18",
|
||||
]
|
||||
test = [
|
||||
"pytest>=9.1.1",
|
||||
"pytest-asyncio>=1.4.0",
|
||||
"pytest-cov>=7.1.0",
|
||||
]
|
||||
|
||||
@@ -185,4 +185,32 @@ def get_skill_document_by_id(skill_id: str) -> dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
@mcp.tool
|
||||
def catalog_search_patterns(
|
||||
query: str = "",
|
||||
tags: list[str] | None = None,
|
||||
skip: int = 0,
|
||||
limit: int = 20,
|
||||
) -> dict[str, Any]:
|
||||
"""Compatibility alias for clients expecting catalog_* tool naming."""
|
||||
return search_patterns(
|
||||
query=query,
|
||||
tags=tags,
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
)
|
||||
|
||||
|
||||
@mcp.tool
|
||||
def catalog_get_pattern_by_id(id: str) -> dict[str, Any]:
|
||||
"""Compatibility alias for clients expecting catalog_* tool naming."""
|
||||
return get_pattern_by_id(id)
|
||||
|
||||
|
||||
@mcp.tool
|
||||
def catalog_get_skill_document_by_id(skill_id: str) -> dict[str, Any]:
|
||||
"""Compatibility alias for clients expecting catalog_* tool naming."""
|
||||
return get_skill_document_by_id(skill_id)
|
||||
|
||||
|
||||
_install_tool_fallback_transforms()
|
||||
|
||||
@@ -208,6 +208,75 @@ 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 = "coverage"
|
||||
version = "7.14.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/9c/a3/3834a5564fe8f32154cd7032400d3c2f9c565b2a373fa671f2bbdad6f634/coverage-7.14.2.tar.gz", hash = "sha256:7a2da3d81cfe17c18038c6d98e6592aa9147d596d056119b0ee612c3c8bd5230", size = 923982, upload-time = "2026-06-20T14:49:30.885Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/d9/bdd141aa2c605096a8ef63b8435fd4f5fec78946a3cb7b9145840ec78291/coverage-7.14.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:37c94712e533ea06f0b1e4d934811c520b1914ce0e4da3916220717aa7a86bc6", size = 220528, upload-time = "2026-06-20T14:47:49.652Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/97/d24ae7d2afc62c54a36313d4dedb655c9afbba3003f0f7f1ae81e97af31f/coverage-7.14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c050bbc7bba94c77e4ed7438f4fda1babe98ab145691d80aa6f60df934a1468b", size = 220883, upload-time = "2026-06-20T14:47:51.036Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/0e/d8f00efd3df0d63e6843ebcbade9e4119d60f5376753c9705d84b014c775/coverage-7.14.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a7af571767a2ee342a171c16fc1b1a07a0bf511606d381703fb7cf397fe49d46", size = 252395, upload-time = "2026-06-20T14:47:52.627Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/1c/ab9510dfe1a16a35a10f90efad0d9a9cf61b9876973752968f2ba882f73f/coverage-7.14.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8b4910cce599cd2438f8da65f5ef199a70a1cdb6ab314926df78271ca5954240", size = 255131, upload-time = "2026-06-20T14:47:54.235Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/dd/70171e9371003b33dc6b20f527ac216ff91bbe5c1088e754eb8950d79193/coverage-7.14.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c33e9e4878972f430b0cc06de3bf2a28d054a9efb4f8426d27de0d9cb81396ff", size = 256246, upload-time = "2026-06-20T14:47:55.61Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/80/a68b1dd81d5c011e17fd6ab0d707d33297df1d0c618114b9b750a2219c80/coverage-7.14.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e7967ea55c6dea6becba4d5870e2fa0aa4915a8be7ebff1bb79e6207aa75ce8d", size = 258504, upload-time = "2026-06-20T14:47:56.979Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/7b/40baaa946189f5317cd77d484e39b9b0727d02ebada0a12162374f2faee2/coverage-7.14.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d1322f237c2979b84096f4239c17828ff17fea6b3bbe96c44381c5f587c44c26", size = 252808, upload-time = "2026-06-20T14:47:58.418Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/05/b19517b09c43d1e8591de6c13178b0c03166c31e1adbebda378e64c66b9a/coverage-7.14.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:77849525340c99f516d793dddbcee16b18d50af892ac43c8de1a6f343d41e3b5", size = 254166, upload-time = "2026-06-20T14:48:00.004Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/f5/6e65da5957e041d2094a9b97736628dd80160f1cc007a50790bbb2668c1a/coverage-7.14.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ef11695493ec3f06f7b2678ca274bcabb4ca04057317df268ddbfd8b05f661a8", size = 252310, upload-time = "2026-06-20T14:48:01.458Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/de/01b5274f0db63175b04d9354eff68d2d268b8b57a1b2db7d3dcb1f2c9dbb/coverage-7.14.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8134f0e0723e080d1c27bbe8fc149f0162e429fa1852482150015d0fce83eaf1", size = 256379, upload-time = "2026-06-20T14:48:02.981Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/d6/9a2ffbca41e2f8f86f61e8b78b86afa433ec8cdeac4908ace93a28fe3ff0/coverage-7.14.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:914eead2b843fc357f733b3fe39cc94f1b53d466e8cfe03080b1ed9d24ccfc73", size = 251880, upload-time = "2026-06-20T14:48:04.463Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/ff/20bd54a43c88c08f474e6cb355a97e024e38412873ef0a581629abe1e26f/coverage-7.14.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e4b2d5e847fb7958583b74910cc19e5ec4ece514487385677b26433b2546116e", size = 253753, upload-time = "2026-06-20T14:48:05.99Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/2a/2b3482c30d8344f301d8df6ff232a321f2ab87d5ac97ba21891a68638131/coverage-7.14.2-cp312-cp312-win32.whl", hash = "sha256:e753db9e40dda7302e0ac3e1e6e1325fb7f7b4694f87a7314ab15dd5d57911a7", size = 222584, upload-time = "2026-06-20T14:48:07.361Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/5e/83934ffff147edd313fe925db426e8f7ccad9e4663262eb5c4db4e345658/coverage-7.14.2-cp312-cp312-win_amd64.whl", hash = "sha256:d32e5ca5f16dafb269ee50b60d32b00c704b3f6f78e238105f1d94a3a5f24bf5", size = 223118, upload-time = "2026-06-20T14:48:08.837Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/ee/616b4f38a34f076f3045d3eedfa764d34d82e6a6cc6b300acb0f1ff22a98/coverage-7.14.2-cp312-cp312-win_arm64.whl", hash = "sha256:dc366f158e2fb2add9d4e57338ca48f12611024278688ee657eb0b853fcb5de5", size = 222504, upload-time = "2026-06-20T14:48:10.436Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/09/b5b334c27960e7aac0003b96491bada7838dc641099fa64a1a598abf33cd/coverage-7.14.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e5f077641a6713ce9d38df9e85d4fb9e008677fc0775cbaeb32ddfc3b319d4ca", size = 220552, upload-time = "2026-06-20T14:48:11.847Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/20/879a000c319b4df7b50e4d688c0f7c0f6b5ac9d7b18848cbc00eabf26efe/coverage-7.14.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0907f39b49ae818fe8af50aaa0f19afbc8ca164aea0865181ca7af17a3ac690b", size = 220919, upload-time = "2026-06-20T14:48:13.397Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/b7/326dded4371bab60f42215797944a356e4d81a3cee106121c7f7dd531604/coverage-7.14.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5734d47669118d75c28981e562d4530ceb77342d31ffef6def5edd5ad4f05d7b", size = 251917, upload-time = "2026-06-20T14:48:14.931Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/14/b3232ba218a0d1a70883d2675f18ff465de9e8e5e3346e81dc2b079838bd/coverage-7.14.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1d9a1b5813d00ea6151f6ccf64d1fa16892771dfdda12ba87162d15ec4ea3e1e", size = 254515, upload-time = "2026-06-20T14:48:16.545Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/7a/d77bcbee1cad71b42776574114b462225cc9125b4982f43da1b66adc850f/coverage-7.14.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f0a80f4c8ac3f774210b1cc1bc0e31e75502f2818dda9a144ff90e702c4d91d", size = 255749, upload-time = "2026-06-20T14:48:18.214Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/86/97377937b29e9e44a1529bb20cb74dbcf80ed9006d87d7e742ff69e44b67/coverage-7.14.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2e66f3f22d6c1515ce70f2e7c3e9c6f3ff0ff33480125c9f9c53e8f6508e30f", size = 257882, upload-time = "2026-06-20T14:48:19.7Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/a4/0fc8fe68bc505450bb068a2823ac7797bd8495240ccb8b4a5a1da1ee7e62/coverage-7.14.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6a2c37c3114f87ca7f10113756026eecb49656514debad600dcbec21f355ccea", size = 252144, upload-time = "2026-06-20T14:48:21.176Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/4a/450094ddc41ab0d2eb4a0457b3856400ea3329568d1303696e85de099ae6/coverage-7.14.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3b16a7959d04b1497281c062c180413565c3f3469211d78799ad5b9a75f67796", size = 253882, upload-time = "2026-06-20T14:48:22.701Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/28/2f6ae6d98265d9aa6bac311c4a93403675905b03aca95dc4373080279d75/coverage-7.14.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6466c6999545cf00c4c142dfcbbf2db396dc735f005dcf8f91d57e351a79472b", size = 251846, upload-time = "2026-06-20T14:48:24.295Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/6e/707281468400794d52874e8fb5e38ff7578a0ff32ed49fe4fe85f192d0fc/coverage-7.14.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c60915ebb8f562317ba5ff6b8c32e25c0882289b201a9f2fb2987f91efd95d8", size = 256002, upload-time = "2026-06-20T14:48:26.015Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/83/5e963120de4011257a950ce4cfb7fc833ddf3fee19db495268d3dec28154/coverage-7.14.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:33b830850488acbcd358c78a4fecfafe7031667b4da8ddff5546295dc962cdeb", size = 251665, upload-time = "2026-06-20T14:48:27.654Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/78/66b482cd525083bcc0bc894c16db79dabac37490065b53b07d6e8ab77202/coverage-7.14.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d0f845539230b8269aec902bc978b0cc403f52f002d18a04492efc943404d0bc", size = 253435, upload-time = "2026-06-20T14:48:29.354Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/61/0663fb8cb530c8b11819b920109694eee95a3b22960a9495be0200f657f1/coverage-7.14.2-cp313-cp313-win32.whl", hash = "sha256:a8ac51a2e441e9119b9395f4d893fbc4934c64c8ba58be9b9eaa85591249e548", size = 222591, upload-time = "2026-06-20T14:48:31.142Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/47/1536d2b009c2848c3682500f497053f4645e70911afe02f594000997831a/coverage-7.14.2-cp313-cp313-win_amd64.whl", hash = "sha256:039b264cdb31c44b48f9821e2afbf8f37df49e0fb837e24a942918b36c567e31", size = 223134, upload-time = "2026-06-20T14:48:32.696Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/9a/33ba4f335dd60bb34350318283d784f46018070e67b7d4df7c910ec9d9a0/coverage-7.14.2-cp313-cp313-win_arm64.whl", hash = "sha256:7f2ef591e381cc36b8e53334e1b842c760c520c8a52d01e8626209400e93fe6a", size = 222529, upload-time = "2026-06-20T14:48:34.237Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/bc/120390669817ede714ab141ae0a2a73240fd7354aac992c41dc0bd19570f/coverage-7.14.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7a0d1f026b72d627fa5c8a57cbc86ad209b64aa2a65833c83b290ace5cbee126", size = 220593, upload-time = "2026-06-20T14:48:35.755Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/a3/7f1cfacd76af91e585f7ad689d7168002b444ed2a8ce59f2daaff10089b5/coverage-7.14.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4d2b86f81c1c9310a7e774e3cc9e927a3d0bf583ecbfa01498dd626930025428", size = 220925, upload-time = "2026-06-20T14:48:37.35Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/10/6514b2525bb672eb8b43703e46d061d694111db21efe7609db722df2233f/coverage-7.14.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d76bdc1f9396ae70a55d050cf9743d88141c62ce0a22a3f627fab1d11c2f8bc6", size = 251974, upload-time = "2026-06-20T14:48:39.109Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/b4/4533091541c6620ecd68115bbfa1c61265b775618adef3a5fd137f4582e9/coverage-7.14.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cda36d8e7bfd63b3e44e75163265429caa5d935b672b00f71bccc8c010518c64", size = 254479, upload-time = "2026-06-20T14:48:40.871Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/af/e251a143d5d106385dbca696c553afab6b69f7f6bc376a34e089cc0b8b32/coverage-7.14.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0904f3b79d7b845bef0715afe1900da634d12b97f05b9479cb472880ca07cb9c", size = 255824, upload-time = "2026-06-20T14:48:42.608Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/53/9e5876e60efbaa79d743d1948a5015ddc05b808db1cd62228acf83e87d43/coverage-7.14.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b6795ca4198d6cb7fc2c6163214f6555a6bc5f0ae1e268e76139dec4b37c4499", size = 258139, upload-time = "2026-06-20T14:48:44.263Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/5a/d35a4f431fb594e46b81cad4a13b470b017e918f347c1c0b260f7494fa1e/coverage-7.14.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c41e9b60fc0fa57f5d73306417d2f9d668202cca6944f9435878c55a5e7ae213", size = 252002, upload-time = "2026-06-20T14:48:45.961Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/e2/f5b304c8139c606c4f1b230d3a257d0c88edfbbdf06c58364f07625dc45c/coverage-7.14.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:419d2aadd5746efc2e9df0f33c05570d8192e6f6a6098ab05acce586f44ce8a5", size = 253832, upload-time = "2026-06-20T14:48:47.582Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/bc/bbbd283daa6be4f68aad4ad4066fd39ae98e4174db8c03ab26c5803d6234/coverage-7.14.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:1c5d273c5f1411c0d26c4f066c398d4a434b1f97bb5fa409189bedce86d4add4", size = 251799, upload-time = "2026-06-20T14:48:49.42Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/8d/0745fceb89c9e5f7dd8ed243d97dc8561b7a95545741e2409d2b34654824/coverage-7.14.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5fe465bc691264adce601527a972990c1174075d86bcbe9968fd20c95e0b1948", size = 256075, upload-time = "2026-06-20T14:48:51.065Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/a0/441d9a5255cf021ab41ee00c014a4607d1c72d5e5bef0a4fdaa5be86a907/coverage-7.14.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:6fbb61617af1c56f95d53170ae9fa6c9aef6de1abd02fcc50064bfc672efb18d", size = 251612, upload-time = "2026-06-20T14:48:52.653Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/37/3d19c5e32d4a529c068eb296abfa3e455bd2c0f9311ecf26280f408ff8e0/coverage-7.14.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e1eff22b831dfd5694989cc1f0789980f18391f614ac67c851af9a8e6d25e9ba", size = 253270, upload-time = "2026-06-20T14:48:54.3Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/b0/54dd13937297518da6d092cc2c39d9340ec2194bdfa92e0a64694d643e23/coverage-7.14.2-cp314-cp314-win32.whl", hash = "sha256:58e91be0a233adef698d3e6be54f10401bb91fd7854c0d4c4d50e0d3711e72f1", size = 222796, upload-time = "2026-06-20T14:48:56.084Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/45/7a10e0909919686e335fdd95869cfb222d55243ebff27dc5cf59ca259a1f/coverage-7.14.2-cp314-cp314-win_amd64.whl", hash = "sha256:d8429bf97906bfe6c61f9dbfb3342e0d88120da61939da8bd04f830cc3eab3b8", size = 223285, upload-time = "2026-06-20T14:48:57.729Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/03/9cb197eb4b3d1a2eccb2537c226a93c80522c5b8afc5dd93e1993d7bb021/coverage-7.14.2-cp314-cp314-win_arm64.whl", hash = "sha256:13609d9d77249447aa73357b14831b0f3b95f275026c9ff20dd105f981f53a0c", size = 222712, upload-time = "2026-06-20T14:48:59.413Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/3c/e59f498511080d20bf866b0af9eeab820feb91547dae2084cb9bb7fb0e58/coverage-7.14.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9818486c2bac88ae931df7e04905ee29bef49fd218c00f5f02bed4855254a101", size = 221325, upload-time = "2026-06-20T14:49:01.447Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/37/8d7955f7e701e69198bd0a0132ea76518c078a635b930a4924e2ccfa70f0/coverage-7.14.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:58055adffabfa243516a197aa9f85f0dd56d905b0fba1a10193269759c29ccb0", size = 221594, upload-time = "2026-06-20T14:49:03.13Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/7a/6738e1e1533ce8ec4e2e472696eefdd4723864d7efaa140e433053bf576a/coverage-7.14.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:535747dbc200349d7fb434cffcb28e770f0290f69b225f56dc3803aa7210cdea", size = 262957, upload-time = "2026-06-20T14:49:04.829Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/c4/d1be863cd39e0955904315fece67c5c23e046563f5eea0ceac16c547a759/coverage-7.14.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:420c66e35d85c0ca5dc6a38147d83ef239762542900e5921ebbdb89333c540ea", size = 265081, upload-time = "2026-06-20T14:49:07.018Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/7f/412df3c3c251284a11834287fd6f7e3bb98c528c53e030589e9344a3ef80/coverage-7.14.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f2cf17b33773be446a588551ea6a746b2d70dd0bc90dc31f1dd7648975a63c6b", size = 267500, upload-time = "2026-06-20T14:49:08.709Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/68/7d0764e83459455384d5c04179ce2d2a837bef01b9ba463079c6e8b31361/coverage-7.14.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:adb4a5fef041f7179bb264203add873c147d169cf2f8d0adae89ff2e51271bac", size = 268619, upload-time = "2026-06-20T14:49:10.405Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/68/1292164ac70cbcc86ac3982da31a6fbb42bb4bcebf6e5cf73c99cfcfd50d/coverage-7.14.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9c012ec357dec9408a83dad5541172a63c5cfa1421709f2e5811480d31ae1b28", size = 262066, upload-time = "2026-06-20T14:49:12.257Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/44/fd6fdf3f63b6e00a1a9230022d072ded5189576001685706aa6524187c65/coverage-7.14.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:dacd0ecd08fda3cb2f85b60cabea7da326dcb2fc15fbb23a88830a80144cc9f2", size = 264953, upload-time = "2026-06-20T14:49:14.13Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/29/e803fea3da89eaeb5b6b41b3ccd039fe9f3300a900e3803baac1a998529f/coverage-7.14.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f27e980f2feba5dfe7a32b22b125470de69c0bd113c75e16165de909a777f512", size = 262555, upload-time = "2026-06-20T14:49:15.803Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/3c/b360e48ac68e3236c04cb83658382e7f5be7efbbec2e1faae3dcca432783/coverage-7.14.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:105c00efb65c863630b2b63cbf7b8267e4da2d44b62284efbb19a03b04c337d4", size = 266289, upload-time = "2026-06-20T14:49:17.962Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/12/1ed6d9274d599c586e2d1aa9818765dcdae6bb52aa88afa2fcd868398191/coverage-7.14.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:571173fa04c8e8d6235ab32ae67fecca97777e2e1b4a1a30f3022c34e397c1c1", size = 261402, upload-time = "2026-06-20T14:49:19.708Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/17/eb6cf12a4538cda937aefbeabb15377a8a30b377b484e63d31c9da790966/coverage-7.14.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e532f34d42d1a421fa00ed6b7735d14ac2e340256c1bad26a5e1dc1252b0bed7", size = 263715, upload-time = "2026-06-20T14:49:21.427Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/ca/4bafdb9d372ab05d6ed3a63e7f00d3195d169d0afea00f617c026e386c19/coverage-7.14.2-cp314-cp314t-win32.whl", hash = "sha256:243971550fb46c3039257f75e65610002d84304c505f609bbd9779e20a653a0a", size = 223103, upload-time = "2026-06-20T14:49:23.24Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/cb/0765dbd9011d2e47315f1da31e62c5fe231f04a6ec8da213e64c4505896d/coverage-7.14.2-cp314-cp314t-win_amd64.whl", hash = "sha256:60fb0ca084a92da96474b8b405a7ea76dfecac3c68db54383e7934b6f3871169", size = 223934, upload-time = "2026-06-20T14:49:25.347Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/ce/373dde027ecd0ae54511430fe7569f838d3a0376b70333ba9fd20c76b836/coverage-7.14.2-cp314-cp314t-win_arm64.whl", hash = "sha256:36a0a3f42ed7dfdbca2a69a541519ffd5064a5692152fc0018109e74370d7345", size = 223249, upload-time = "2026-06-20T14:49:27.241Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/5e/a8ba14ceb014f39bd5e3f7077150718c7de61c01ce326bfe7e8eae9b19b2/coverage-7.14.2-py3-none-any.whl", hash = "sha256:04d92589e481a8b68a005a5a1e0646a91c76f322c397c4635298c57cf63699b5", size = 212325, upload-time = "2026-06-20T14:49:28.991Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "49.0.0"
|
||||
@@ -531,6 +600,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" },
|
||||
]
|
||||
|
||||
[[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 = "jaraco-classes"
|
||||
version = "3.4.0"
|
||||
@@ -852,6 +930,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", size = 22743, upload-time = "2026-05-28T03:32:52.175Z" },
|
||||
]
|
||||
|
||||
[[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 = "pre-commit"
|
||||
version = "4.6.0"
|
||||
@@ -886,6 +973,11 @@ dev = [
|
||||
{ name = "pre-commit" },
|
||||
{ name = "ruff" },
|
||||
]
|
||||
test = [
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "pytest-cov" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
@@ -902,6 +994,11 @@ dev = [
|
||||
{ name = "pre-commit", specifier = ">=4.6.0" },
|
||||
{ name = "ruff", specifier = ">=0.15.18" },
|
||||
]
|
||||
test = [
|
||||
{ name = "pytest", specifier = ">=9.1.1" },
|
||||
{ name = "pytest-asyncio", specifier = ">=1.4.0" },
|
||||
{ name = "pytest-cov", specifier = ">=7.1.0" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "py-key-value-aio"
|
||||
@@ -1091,6 +1188,49 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" },
|
||||
]
|
||||
|
||||
[[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 = "pytest-asyncio"
|
||||
version = "1.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pytest" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-cov"
|
||||
version = "7.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "coverage" },
|
||||
{ name = "pluggy" },
|
||||
{ name = "pytest" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-discovery"
|
||||
version = "1.4.2"
|
||||
|
||||
+5
-1
@@ -55,7 +55,6 @@ nav = [
|
||||
{ "Copilot" = "copilot.md" },
|
||||
{ "Usage" = "usage.md" },
|
||||
{ "Future Work" = "future_work.md" },
|
||||
{ "New Skill" = "new_skill.md" },
|
||||
{ "Security" = "securing.md" },
|
||||
] },
|
||||
{ "Skills" = [
|
||||
@@ -108,6 +107,11 @@ nav = [
|
||||
{ "Overview" = "skills/python-logging-dictconfig/SKILL.md" },
|
||||
{ "Docs" = "skills/python-logging-dictconfig/references/python-logging-docs.md" },
|
||||
] },
|
||||
{ "Ruff" = [
|
||||
{ "Overview" = "skills/ruff-linting-formating/SKILL.md" },
|
||||
{ "Docs" = "skills/ruff-linting-formating/references/ruff-docs.md" },
|
||||
{ "Integrations" = "skills/ruff-linting-formating/references/ruff-integrations.md" },
|
||||
] },
|
||||
{ "Zensical" = [
|
||||
{ "Overview" = "skills/zensical-docs/SKILL.md" },
|
||||
{ "Map" = "skills/zensical-docs/references/index.md" },
|
||||
|
||||
Reference in New Issue
Block a user