diff --git a/.github/agents/pharaoh.activity-diagram-draft.agent.md b/.github/agents/pharaoh.activity-diagram-draft.agent.md new file mode 100644 index 0000000..36bba5e --- /dev/null +++ b/.github/agents/pharaoh.activity-diagram-draft.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when drafting one activity diagram showing control flow (actions, decisions, forks/joins, swimlanes) for one procedure or algorithm. Typical ASPICE usage — SWE.3 Software Detailed Design. Renderer tailored via `pharaoh.toml`. Does NOT emit other diagram kinds. Status — PLANNED (design-only scaffold; invoking returns sentinel FAIL until implemented). +handoffs: [] +--- + +# @pharaoh.activity-diagram-draft + +Use when drafting one activity diagram showing control flow (actions, decisions, forks/joins, swimlanes) for one procedure or algorithm. Typical ASPICE usage — SWE.3 Software Detailed Design. Renderer tailored via `pharaoh.toml`. Does NOT emit other diagram kinds. Status — PLANNED (design-only scaffold; invoking returns sentinel FAIL until implemented). + +See [`skills/pharaoh-activity-diagram-draft/SKILL.md`](../../skills/pharaoh-activity-diagram-draft/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.api-coverage-check.agent.md b/.github/agents/pharaoh.api-coverage-check.agent.md new file mode 100644 index 0000000..0fac840 --- /dev/null +++ b/.github/agents/pharaoh.api-coverage-check.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when verifying that a source file is covered by the need catalogue on two axes — (1) at least one CREQ declares the file as its `:source_doc:`, and (2) every project-defined exception class raised in the file is named by some CREQ's title or content. Exception classes not defined in the project source tree (stdlib, third-party deps) are reported as `external` and do not fail the axis. Classifies non-behavioral files (constants, type aliases, bare re-exports) as skipped. Language-parametric via the shared regex table in `skills/shared/public-symbol-patterns.md` (python / rust / typescript / go / c / cpp / java). Single mechanical structural check. +handoffs: [] +--- + +# @pharaoh.api-coverage-check + +Use when verifying that a source file is covered by the need catalogue on two axes — (1) at least one CREQ declares the file as its `:source_doc:`, and (2) every project-defined exception class raised in the file is named by some CREQ's title or content. Exception classes not defined in the project source tree (stdlib, third-party deps) are reported as `external` and do not fail the axis. Classifies non-behavioral files (constants, type aliases, bare re-exports) as skipped. Language-parametric via the shared regex table in `skills/shared/public-symbol-patterns.md` (python / rust / typescript / go / c / cpp / java). Single mechanical structural check. + +See [`skills/pharaoh-api-coverage-check/SKILL.md`](../../skills/pharaoh-api-coverage-check/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.arch-draft.agent.md b/.github/agents/pharaoh.arch-draft.agent.md new file mode 100644 index 0000000..ccb3362 --- /dev/null +++ b/.github/agents/pharaoh.arch-draft.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when drafting a single sphinx-needs architecture element from one parent requirement. The artefact type is parameterised via `target_level` (any catalog-declared architecture type — e.g. `arch`, `swarch`, `sys-arch`, `module`, `component`, `interface`). Emits an RST directive block linking back to the parent via `:satisfies:`. +handoffs: [] +--- + +# @pharaoh.arch-draft + +Use when drafting a single sphinx-needs architecture element from one parent requirement. The artefact type is parameterised via `target_level` (any catalog-declared architecture type — e.g. `arch`, `swarch`, `sys-arch`, `module`, `component`, `interface`). Emits an RST directive block linking back to the parent via `:satisfies:`. + +See [`skills/pharaoh-arch-draft/SKILL.md`](../../skills/pharaoh-arch-draft/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.arch-review.agent.md b/.github/agents/pharaoh.arch-review.agent.md new file mode 100644 index 0000000..ac6afb0 --- /dev/null +++ b/.github/agents/pharaoh.arch-review.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when auditing a single architecture element against the 10 ISO 26262-8 §6 axes plus arch-specific axes (traceability back to requirement). Emits structured findings JSON. +handoffs: [] +--- + +# @pharaoh.arch-review + +Use when auditing a single architecture element against the 10 ISO 26262-8 §6 axes plus arch-specific axes (traceability back to requirement). Emits structured findings JSON. + +See [`skills/pharaoh-arch-review/SKILL.md`](../../skills/pharaoh-arch-review/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.audit-fanout.agent.md b/.github/agents/pharaoh.audit-fanout.agent.md new file mode 100644 index 0000000..c314f96 --- /dev/null +++ b/.github/agents/pharaoh.audit-fanout.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when running a full project audit in parallel by dispatching 5 atomic audit skills, each writing findings to a shared Papyrus workspace via pharaoh-finding-record for automatic deduplication. Emits the aggregated deduplicated findings list. +handoffs: [] +--- + +# @pharaoh.audit-fanout + +Use when running a full project audit in parallel by dispatching 5 atomic audit skills, each writing findings to a shared Papyrus workspace via pharaoh-finding-record for automatic deduplication. Emits the aggregated deduplicated findings list. + +See [`skills/pharaoh-audit-fanout/SKILL.md`](../../skills/pharaoh-audit-fanout/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.author.agent.md b/.github/agents/pharaoh.author.agent.md new file mode 100644 index 0000000..e1a8e7c --- /dev/null +++ b/.github/agents/pharaoh.author.agent.md @@ -0,0 +1,19 @@ +--- +description: Use when authoring or modifying a single sphinx-needs artefact (requirement, architecture element, test case, decision) by routing to the matching atomic drafting skill based on the project's artefact catalog. Returns the drafted RST directive with an ID, file placement suggestion, and parent link. +handoffs: + - label: Verify Authored Need + agent: pharaoh.verify + prompt: Check that the authored artefact addresses the substance of its parent + - label: Review Drafted Requirement + agent: pharaoh.req-review + prompt: Audit the drafted requirement against the ISO 26262 §6 axes + - label: Trace the Authored Need + agent: pharaoh.trace + prompt: Trace the new artefact through all link types +--- + +# @pharaoh.author + +Use when authoring or modifying a single sphinx-needs artefact (requirement, architecture element, test case, decision) by routing to the matching atomic drafting skill based on the project's artefact catalog. Returns the drafted RST directive with an ID, file placement suggestion, and parent link. + +See [`skills/pharaoh-author/SKILL.md`](../../skills/pharaoh-author/SKILL.md) for the full atomic specification — inputs, dispatch table, and composition patterns. diff --git a/.github/agents/pharaoh.block-diagram-draft.agent.md b/.github/agents/pharaoh.block-diagram-draft.agent.md new file mode 100644 index 0000000..99e72ac --- /dev/null +++ b/.github/agents/pharaoh.block-diagram-draft.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when drafting one SysML-style block diagram — Block Definition Diagram (BDD) showing block structure and composition, or Internal Block Diagram (IBD) showing ports, flows, and part interconnections. Typical ASPICE usage — SYS.2/SYS.3 for system-level architecture, and SWE.2 for software architecture on SysML-heavy projects. Renderer tailored via `pharaoh.toml`. Status — PLANNED (design-only scaffold; invoking returns sentinel FAIL until implemented). +handoffs: [] +--- + +# @pharaoh.block-diagram-draft + +Use when drafting one SysML-style block diagram — Block Definition Diagram (BDD) showing block structure and composition, or Internal Block Diagram (IBD) showing ports, flows, and part interconnections. Typical ASPICE usage — SYS.2/SYS.3 for system-level architecture, and SWE.2 for software architecture on SysML-heavy projects. Renderer tailored via `pharaoh.toml`. Status — PLANNED (design-only scaffold; invoking returns sentinel FAIL until implemented). + +See [`skills/pharaoh-block-diagram-draft/SKILL.md`](../../skills/pharaoh-block-diagram-draft/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.bootstrap.agent.md b/.github/agents/pharaoh.bootstrap.agent.md new file mode 100644 index 0000000..213249a --- /dev/null +++ b/.github/agents/pharaoh.bootstrap.agent.md @@ -0,0 +1,13 @@ +--- +description: Use when a Sphinx project has no sphinx-needs configured and you need minimum viable scaffolding — adding the extension and declaring need types — so that sphinx-build produces a valid needs.json for downstream Pharaoh skills. +handoffs: + - label: Detect and scaffold Pharaoh + agent: pharaoh.setup + prompt: Detect the freshly configured sphinx-needs project and scaffold pharaoh.toml +--- + +# @pharaoh.bootstrap + +Use when a Sphinx project has no sphinx-needs configured and you need minimum viable scaffolding — adding the extension and declaring need types — so that sphinx-build produces a valid needs.json for downstream Pharaoh skills. + +See [`skills/pharaoh-bootstrap/SKILL.md`](../../skills/pharaoh-bootstrap/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.change.agent.md b/.github/agents/pharaoh.change.agent.md new file mode 100644 index 0000000..9ffa829 --- /dev/null +++ b/.github/agents/pharaoh.change.agent.md @@ -0,0 +1,131 @@ +--- +description: Use when analyzing the impact of changing a requirement, specification, or any sphinx-needs item, including traceability to code via codelinks +handoffs: + - label: Author the affected needs + agent: pharaoh.author + prompt: Author the needs flagged in this change analysis + - label: Verify the affected needs + agent: pharaoh.verify + prompt: Verify the authored needs against their parents and review axes + - label: MECE Check + agent: pharaoh.mece + prompt: Check the affected area for gaps and redundancies + - label: Trace Requirement + agent: pharaoh.trace + prompt: Trace the changed requirement through all levels +--- + +# @pharaoh.change: Change Impact Analysis + +Analyze the full impact of a proposed change to any sphinx-needs item. Trace through ALL link types -- standard `links`, `extra_links` (implements, tests, etc.), and sphinx-codelinks -- to produce a structured Change Document listing every affected need and code file with a recommended action. + +## Data Access + +Use the best available data source in this priority order: + +1. **ubc CLI**: Run `ubc --version`. If available, use `ubc build needs --format json` for the needs index. Use `ubc diff` for structural change detection. +2. **ubCode MCP**: Check for MCP tools with names containing `ubcode` or `useblocks`. Use for pre-indexed data and link graph. +3. **Raw file parsing**: Search for `ubproject.toml` or `conf.py` for configuration. Grep for need directives (`.. ::`) in RST/MD files. Parse options (`:id:`, `:status:`, `:links:`, extra_links). Build the link graph manually. + +Read `pharaoh.toml` for strictness level, workflow gates, traceability requirements, and codelinks settings. + +## Process + +### Step 1: Understand the Change + +Extract from the user's request: +- **Target need ID(s)**: One or more need IDs. If described by title, resolve after data access. +- **Nature of change**: Value change, addition, removal, or restructuring. +- **Change description**: What changes and why. + +Clarify ambiguity with at most one round of questions. + +### Step 2: Get Project Data + +Detect the project and build the needs index. Present summary: + +``` +Project: () +Types: +Links: +Data source: +Needs found: +Codelinks: +Strictness: +``` + +Resolve need IDs from descriptions if needed (title/content matching). + +### Step 3: Impact Analysis + +**Direct impact (1 hop)**: Find every need directly linked to the target through any link type and direction. + +**Transitive impact (full graph)**: BFS from directly impacted needs through all link types. Track distance from target. Use a visited set to handle cycles. + +**Code impact** (if codelinks enabled): For every affected need, search code files for codelink annotations (`# codelink: `, `// codelink: `, etc.). + +**Classify severity** for each affected item: +- **Must update**: Content references the specific value being changed. +- **Review needed**: Linked but impact unclear; content relates to the changed property without referencing the specific value. +- **No change needed**: Linked but addresses a different concern entirely. + +### Step 4: Produce Change Document + +``` +## Change Document + +### Change Request +- **Target**: () +- **Change**: <description> +- **Date**: <ISO 8601 date> + +### Direct Impact (1 hop) + +| Need ID | Type | Title | Link Type | Direction | Action | +|---------|------|-------|-----------|-----------|--------| + +### Transitive Impact + +| Need ID | Type | Title | Distance | Path | Action | +|---------|------|-------|----------|------|--------| + +### Code Impact + +| File | Location | Linked Need | Action | +|------|----------|-------------|--------| + +### Summary +- Needs requiring update: <count> +- Needs requiring review: <count> +- No change needed: <count> +- Code files affected: <count> +- Recommendation: <proceed / escalate / discuss> +``` + +**Recommendation**: Proceed if <= 5 must-update items and no safety-tagged needs affected. Escalate if safety/critical/regulatory-tagged needs are "Must update" or >10 items need update. Discuss if impact is ambiguous. + +### Step 5: Update Session State + +Write to `.pharaoh/session.json`: +- Set `changes.<target_id>.change_analysis` to current timestamp. +- Set `acknowledged` to `false` initially. + +### Step 6: Ask for Acknowledgment + +``` +Acknowledge this change analysis? Acknowledging allows proceeding to @pharaoh.author for the affected needs. +``` + +If acknowledged, set `changes.<target_id>.acknowledged = true` in session state. + +## Strictness Behavior + +This agent has **no prerequisites** and runs freely in both advisory and enforcing modes. However, its output gates `@pharaoh.author` in enforcing mode -- authoring requires an acknowledged change analysis. + +## Constraints + +1. Always trace ALL configured link types. Read the project's `extra_links` configuration. +2. Handle circular links with a visited set. Report cycles. +3. Support multi-project setups. Label cross-project needs. +4. For large impact scopes (>50 needs), recommend escalation. +5. Never modify need source files. This agent is read-only except for session state. diff --git a/.github/agents/pharaoh.class-diagram-draft.agent.md b/.github/agents/pharaoh.class-diagram-draft.agent.md new file mode 100644 index 0000000..5911999 --- /dev/null +++ b/.github/agents/pharaoh.class-diagram-draft.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when drafting one class diagram showing a bounded set of types/entities with their fields, methods, and relationships (inheritance, composition, aggregation, association). Renderer tailored via `pharaoh.toml`. Does NOT emit component, sequence, or state diagrams. Status — PLANNED (design-only scaffold; invoking returns sentinel FAIL until implemented). +handoffs: [] +--- + +# @pharaoh.class-diagram-draft + +Use when drafting one class diagram showing a bounded set of types/entities with their fields, methods, and relationships (inheritance, composition, aggregation, association). Renderer tailored via `pharaoh.toml`. Does NOT emit component, sequence, or state diagrams. Status — PLANNED (design-only scaffold; invoking returns sentinel FAIL until implemented). + +See [`skills/pharaoh-class-diagram-draft/SKILL.md`](../../skills/pharaoh-class-diagram-draft/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.component-diagram-draft.agent.md b/.github/agents/pharaoh.component-diagram-draft.agent.md new file mode 100644 index 0000000..e4bf231 --- /dev/null +++ b/.github/agents/pharaoh.component-diagram-draft.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when drafting one component-relationship diagram (nodes = sphinx-needs, edges = link relations) for a bounded scope — one feature, one module, one architectural view. Renderer tailored via `pharaoh.toml`. Does NOT emit sequence, class, or state diagrams — those are separate skills. Status — PLANNED (design-only scaffold; invoking returns sentinel FAIL until implemented). +handoffs: [] +--- + +# @pharaoh.component-diagram-draft + +Use when drafting one component-relationship diagram (nodes = sphinx-needs, edges = link relations) for a bounded scope — one feature, one module, one architectural view. Renderer tailored via `pharaoh.toml`. Does NOT emit sequence, class, or state diagrams — those are separate skills. Status — PLANNED (design-only scaffold; invoking returns sentinel FAIL until implemented). + +See [`skills/pharaoh-component-diagram-draft/SKILL.md`](../../skills/pharaoh-component-diagram-draft/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.context-gather.agent.md b/.github/agents/pharaoh.context-gather.agent.md new file mode 100644 index 0000000..8f21d82 --- /dev/null +++ b/.github/agents/pharaoh.context-gather.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when retrieving rationale memories relevant to an authoring context from a Papyrus workspace, before invoking any draft or review skill. Returns a structured list of memories (memory_id, text, relevance_score). Does NOT draft, review, or modify artefacts. +handoffs: [] +--- + +# @pharaoh.context-gather + +Use when retrieving rationale memories relevant to an authoring context from a Papyrus workspace, before invoking any draft or review skill. Returns a structured list of memories (memory_id, text, relevance_score). Does NOT draft, review, or modify artefacts. + +See [`skills/pharaoh-context-gather/SKILL.md`](../../skills/pharaoh-context-gather/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.coverage-gap.agent.md b/.github/agents/pharaoh.coverage-gap.agent.md new file mode 100644 index 0000000..b95341d --- /dev/null +++ b/.github/agents/pharaoh.coverage-gap.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when detecting one gap category (orphan / unverified / duplicate / contradictory / lifecycle / ...) in a sphinx-needs corpus. Returns ordered list of needs falling into that gap. +handoffs: [] +--- + +# @pharaoh.coverage-gap + +Use when detecting one gap category (orphan / unverified / duplicate / contradictory / lifecycle / ...) in a sphinx-needs corpus. Returns ordered list of needs falling into that gap. + +See [`skills/pharaoh-coverage-gap/SKILL.md`](../../skills/pharaoh-coverage-gap/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.decide.agent.md b/.github/agents/pharaoh.decide.agent.md new file mode 100644 index 0000000..4e942fb --- /dev/null +++ b/.github/agents/pharaoh.decide.agent.md @@ -0,0 +1,115 @@ +--- +description: Use when recording a design decision as a traceable sphinx-needs object with alternatives, rationale, and links to affected requirements +handoffs: + - label: Trace Decision + agent: pharaoh.trace + prompt: Trace the decision through all linked needs + - label: Generate Spec + agent: pharaoh.spec + prompt: Generate a spec document from the affected requirements +--- + +# @pharaoh.decide + +Record design decisions as `decision` needs with `decided_by`, `alternatives`, `rationale` fields and `:decides:` links. Delegates RST writing to @pharaoh.author to avoid duplicating directive-writing logic. + +## Data Access + +1. **ubc CLI**: `ubc build needs --format json` for index, `ubc config` for schema. +2. **ubCode MCP**: Pre-indexed needs data. +3. **Raw file parsing**: Read `ubproject.toml`/`conf.py` for types, extra_links, ID settings. Grep for directives. Parse needs. + +Read `pharaoh.toml` for strictness level and workflow settings. + +## Process + +### Step 1: Get Project Data + +Build needs index. Present detection summary. Verify that a `decision` type is configured. If missing, show the user the TOML to add: + +```toml +[[needs.types]] +directive = "decision" +title = "Decision" +prefix = "DEC_" +color = "#E8D0A9" +style = "node" +``` + +Also verify `decided_by`, `alternatives`, `rationale` extra options and the `decides` extra link type exist. Ask user to confirm before proceeding if anything is missing. + +### Step 2: Gather Decision Context + +Collect all required fields: + +- **Title**: What is being decided. +- **Affected needs**: Need IDs for the `:decides:` link. +- **decided_by**: Who made the decision. Default to `claude` when AI decides autonomously. +- **alternatives**: Rejected alternatives, semicolon-separated. +- **rationale**: Why this option was chosen. +- **status**: One of `proposed`, `accepted`, `superseded`, `rejected`. + +**Standalone**: Prompt the user for each missing piece. Do not proceed until all five fields are populated. + +**Called by @pharaoh.spec**: Accept all context programmatically. Do not prompt. + +**Status defaults**: `proposed` when standalone, `accepted` when called by @pharaoh.spec. User may override. + +### Step 3: Generate ID + +Reuse @pharaoh.author ID generation logic: + +1. Check `pharaoh.toml` for `[pharaoh.id_scheme]`. Apply pattern with `{TYPE}` resolving to `DEC`. +2. If no scheme, infer from existing `decision` needs (look for `DEC_*` numbering). +3. If no existing decisions, use prefix from type config and start at `001`, padded to `id_length`. +4. Validate uniqueness against the full needs index. + +### Step 4: Write the Need + +Delegate to @pharaoh.author with all fields: + +```rst +.. decision:: <title> + :id: <generated_id> + :status: <proposed|accepted> + :decides: <need_id1>, <need_id2> + :decided_by: <name or claude> + :alternatives: <alt1>; <alt2> + :rationale: <why this option> + + <expanded description> +``` + +**Superseding**: When replacing an old decision, set old status to `superseded` via @pharaoh.author, add `:links: <old_dec_id>` on the new decision, and explain the replacement in the description. + +### Step 5: File Placement + +Place in `decisions.rst` in the same directory as the first need in `:decides:`. Create the file with proper RST title if it does not exist. If no `:decides:` links, fall back to @pharaoh.author file placement. Delegate actual writing to @pharaoh.author. + +### Step 6: Update Session State + +Write to `.pharaoh/session.json`: set `changes.<dec_id>.authored = true` with current ISO 8601 timestamp. + +### Step 7: Follow-up + +**Standalone**: Suggest `Run @pharaoh.verify to validate the decision against its linked requirements.` + +**Called by @pharaoh.spec**: Return the decision ID silently. No follow-up. + +## Strictness Behavior + +**Advisory mode**: Execute freely. No gates. No tips needed -- decisions can be recorded at any time. + +**Enforcing mode**: Execute freely. No gates. Decisions are gate-free in both modes. + +Strictness has no effect on decision recording. Both modes follow the same process. + +## Constraints + +1. **All three fields mandatory.** Always populate `decided_by`, `alternatives`, `rationale`. Ask explicitly if any are missing. +2. **Default `decided_by` to `claude`** when the AI decides autonomously (e.g., during @pharaoh.spec). +3. **Default `status`** to `proposed` (standalone) or `accepted` (called by @pharaoh.spec). +4. **Superseding requires two writes.** Update old decision to `superseded` AND add `:links:` on the new decision. +5. **Reuse @pharaoh.author** for RST writing, file placement, and ID generation. Do not duplicate logic. +6. **Validate `:decides:` targets exist.** Warn if a target is missing from the needs index. +7. **Semicolons for alternatives.** Separate with semicolons, not commas. diff --git a/.github/agents/pharaoh.decision-record.agent.md b/.github/agents/pharaoh.decision-record.agent.md new file mode 100644 index 0000000..c7cccc6 --- /dev/null +++ b/.github/agents/pharaoh.decision-record.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when recording a canonical decision, fact, or preference in the shared Papyrus workspace with automatic dedup on (type, canonical_name). Returns {action: wrote|duplicate, papyrus_id}. Generalizes pharaoh-finding-record beyond audit findings. +handoffs: [] +--- + +# @pharaoh.decision-record + +Use when recording a canonical decision, fact, or preference in the shared Papyrus workspace with automatic dedup on (type, canonical_name). Returns {action: wrote|duplicate, papyrus_id}. Generalizes pharaoh-finding-record beyond audit findings. + +See [`skills/pharaoh-decision-record/SKILL.md`](../../skills/pharaoh-decision-record/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.decision-review.agent.md b/.github/agents/pharaoh.decision-review.agent.md new file mode 100644 index 0000000..35d3bb0 --- /dev/null +++ b/.github/agents/pharaoh.decision-review.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when auditing a single recorded decision (DR / ADR / design note) against the generic decision review axes in `shared/checklists/decision.md`. Checks context/alternatives/consequences structure, traceability to affected artefacts, rationale completeness. Emits structured findings JSON. +handoffs: [] +--- + +# @pharaoh.decision-review + +Use when auditing a single recorded decision (DR / ADR / design note) against the generic decision review axes in `shared/checklists/decision.md`. Checks context/alternatives/consequences structure, traceability to affected artefacts, rationale completeness. Emits structured findings JSON. + +See [`skills/pharaoh-decision-review/SKILL.md`](../../skills/pharaoh-decision-review/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.deployment-diagram-draft.agent.md b/.github/agents/pharaoh.deployment-diagram-draft.agent.md new file mode 100644 index 0000000..b35581c --- /dev/null +++ b/.github/agents/pharaoh.deployment-diagram-draft.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when drafting one deployment diagram showing physical nodes (ECUs, servers, boards), the software artefacts deployed on each, and communication channels (buses, networks). Typical ASPICE usage — SYS.3 System Architectural Design; essential for automotive HW/SW allocation per ISO 26262 Part 5 (HW) and Part 6 (SW). Renderer tailored via `pharaoh.toml`. Status — PLANNED (design-only scaffold; invoking returns sentinel FAIL until implemented). +handoffs: [] +--- + +# @pharaoh.deployment-diagram-draft + +Use when drafting one deployment diagram showing physical nodes (ECUs, servers, boards), the software artefacts deployed on each, and communication channels (buses, networks). Typical ASPICE usage — SYS.3 System Architectural Design; essential for automotive HW/SW allocation per ISO 26262 Part 5 (HW) and Part 6 (SW). Renderer tailored via `pharaoh.toml`. Status — PLANNED (design-only scaffold; invoking returns sentinel FAIL until implemented). + +See [`skills/pharaoh-deployment-diagram-draft/SKILL.md`](../../skills/pharaoh-deployment-diagram-draft/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.diagram-lint.agent.md b/.github/agents/pharaoh.diagram-lint.agent.md new file mode 100644 index 0000000..45014e4 --- /dev/null +++ b/.github/agents/pharaoh.diagram-lint.agent.md @@ -0,0 +1,13 @@ +--- +description: Use when running a terminal validation step over a directory of RST files to catch Mermaid / PlantUML parse failures that sphinx-build cannot detect. Extracts every `.. mermaid::` and `.. uml::` block and pipes it to the real renderer parser (mmdc / plantuml -checkonly). Returns structured findings. Does NOT modify the RST files. +handoffs: + - label: Aggregate into quality gate + agent: pharaoh.quality-gate + prompt: Consume the diagram-lint findings alongside review/mece/coverage reports for the terminal pass/fail decision +--- + +# @pharaoh.diagram-lint + +Use when running a terminal validation step over a directory of RST files to catch Mermaid / PlantUML parse failures that sphinx-build cannot detect. Extracts every `.. mermaid::` and `.. uml::` block and pipes it to the real renderer parser (mmdc / plantuml -checkonly). Returns structured findings. Does NOT modify the RST files. + +See [`skills/pharaoh-diagram-lint/SKILL.md`](../../skills/pharaoh-diagram-lint/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.diagram-review.agent.md b/.github/agents/pharaoh.diagram-review.agent.md new file mode 100644 index 0000000..deb7695 --- /dev/null +++ b/.github/agents/pharaoh.diagram-review.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when auditing a single diagram block (Mermaid or PlantUML) emitted by any diagram-emitting skill. Single review atom covering all diagram types — trace/caption/element-count/parser/required-elements checks plus LLM-judge axes for purpose clarity and granularity consistency. Per-type required-element checks dispatched based on `diagram_type` input. +handoffs: [] +--- + +# @pharaoh.diagram-review + +Use when auditing a single diagram block (Mermaid or PlantUML) emitted by any diagram-emitting skill. Single review atom covering all diagram types — trace/caption/element-count/parser/required-elements checks plus LLM-judge axes for purpose clarity and granularity consistency. Per-type required-element checks dispatched based on `diagram_type` input. + +See [`skills/pharaoh-diagram-review/SKILL.md`](../../skills/pharaoh-diagram-review/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.dispatch-signal-check.agent.md b/.github/agents/pharaoh.dispatch-signal-check.agent.md new file mode 100644 index 0000000..60f80e4 --- /dev/null +++ b/.github/agents/pharaoh.dispatch-signal-check.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when verifying that a plan's declared `execution_mode` matches observed subagent artefacts in `runs/`. Detects the "LLM-executor collapsed subagents into inline" failure class observed during dogfooding. One mechanical structural check. +handoffs: [] +--- + +# @pharaoh.dispatch-signal-check + +Use when verifying that a plan's declared `execution_mode` matches observed subagent artefacts in `runs/`. Detects the "LLM-executor collapsed subagents into inline" failure class observed during dogfooding. One mechanical structural check. + +See [`skills/pharaoh-dispatch-signal-check/SKILL.md`](../../skills/pharaoh-dispatch-signal-check/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.execute-plan.agent.md b/.github/agents/pharaoh.execute-plan.agent.md new file mode 100644 index 0000000..2324022 --- /dev/null +++ b/.github/agents/pharaoh.execute-plan.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when executing a plan.yaml produced by pharaoh-write-plan. Reads the plan, runs each task (inline or via subagent dispatch), threads outputs between tasks per the ref grammar, validates outputs via pharaoh-output-validate, persists artefacts and report.yaml. Generic — the plan is the orchestrator, this skill is the engine. +handoffs: [] +--- + +# @pharaoh.execute-plan + +Use when executing a plan.yaml produced by pharaoh-write-plan. Reads the plan, runs each task (inline or via subagent dispatch), threads outputs between tasks per the ref grammar, validates outputs via pharaoh-output-validate, persists artefacts and report.yaml. Generic — the plan is the orchestrator, this skill is the engine. + +See [`skills/pharaoh-execute-plan/SKILL.md`](../../skills/pharaoh-execute-plan/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.fault-tree-diagram-draft.agent.md b/.github/agents/pharaoh.fault-tree-diagram-draft.agent.md new file mode 100644 index 0000000..044a7ad --- /dev/null +++ b/.github/agents/pharaoh.fault-tree-diagram-draft.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when drafting one fault tree for FTA (Fault Tree Analysis) — a top hazard event decomposed through AND/OR gates into basic events (component failures, random hardware faults, human errors). Typical ISO 26262 usage — Part 3 Hazard Analysis & Risk Assessment, and Part 5 supporting hardware architectural metrics. Renderer tailored via `pharaoh.toml`. Status — PLANNED (design-only scaffold; invoking returns sentinel FAIL until implemented). +handoffs: [] +--- + +# @pharaoh.fault-tree-diagram-draft + +Use when drafting one fault tree for FTA (Fault Tree Analysis) — a top hazard event decomposed through AND/OR gates into basic events (component failures, random hardware faults, human errors). Typical ISO 26262 usage — Part 3 Hazard Analysis & Risk Assessment, and Part 5 supporting hardware architectural metrics. Renderer tailored via `pharaoh.toml`. Status — PLANNED (design-only scaffold; invoking returns sentinel FAIL until implemented). + +See [`skills/pharaoh-fault-tree-diagram-draft/SKILL.md`](../../skills/pharaoh-fault-tree-diagram-draft/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.feat-balance.agent.md b/.github/agents/pharaoh.feat-balance.agent.md new file mode 100644 index 0000000..fd28551 --- /dev/null +++ b/.github/agents/pharaoh.feat-balance.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when a plan emitted by `pharaoh-write-plan` has completed its feature + comp_req emission and you need to check for granularity skew — features with too many reqs (under-decomposed feature model), too few (over-decomposed), fused sub-features (generic names like "utilities"), or redundancy (symmetric import/export pairs). Reports health and suggestions; does not mutate. +handoffs: [] +--- + +# @pharaoh.feat-balance + +Use when a plan emitted by `pharaoh-write-plan` has completed its feature + comp_req emission and you need to check for granularity skew — features with too many reqs (under-decomposed feature model), too few (over-decomposed), fused sub-features (generic names like "utilities"), or redundancy (symmetric import/export pairs). Reports health and suggestions; does not mutate. + +See [`skills/pharaoh-feat-balance/SKILL.md`](../../skills/pharaoh-feat-balance/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.feat-component-extract.agent.md b/.github/agents/pharaoh.feat-component-extract.agent.md new file mode 100644 index 0000000..b3db215 --- /dev/null +++ b/.github/agents/pharaoh.feat-component-extract.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when reverse-engineering a feat and you need to derive a component composition diagram automatically from the feat + its source files. Walks import edges between the listed files and emits a Mermaid or PlantUML diagram whose output shape is compatible with pharaoh-component-diagram-draft. Does NOT hand-author nodes or edges; extraction is rule-based. +handoffs: [] +--- + +# @pharaoh.feat-component-extract + +Use when reverse-engineering a feat and you need to derive a component composition diagram automatically from the feat + its source files. Walks import edges between the listed files and emits a Mermaid or PlantUML diagram whose output shape is compatible with pharaoh-component-diagram-draft. Does NOT hand-author nodes or edges; extraction is rule-based. + +See [`skills/pharaoh-feat-component-extract/SKILL.md`](../../skills/pharaoh-feat-component-extract/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.feat-draft-from-docs.agent.md b/.github/agents/pharaoh.feat-draft-from-docs.agent.md new file mode 100644 index 0000000..d2c4477 --- /dev/null +++ b/.github/agents/pharaoh.feat-draft-from-docs.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when reading one or more existing documentation files (unstructured prose, README, tutorial) and emitting one or more feature-level RST directives (typed by `target_level`, default `feat`) that describe the user-facing capabilities documented in those files. Does NOT read source code. Does NOT emit component requirements. Does NOT map features to files — that is `pharaoh-feat-file-map`. +handoffs: [] +--- + +# @pharaoh.feat-draft-from-docs + +Use when reading one or more existing documentation files (unstructured prose, README, tutorial) and emitting one or more feature-level RST directives (typed by `target_level`, default `feat`) that describe the user-facing capabilities documented in those files. Does NOT read source code. Does NOT emit component requirements. Does NOT map features to files — that is `pharaoh-feat-file-map`. + +See [`skills/pharaoh-feat-draft-from-docs/SKILL.md`](../../skills/pharaoh-feat-draft-from-docs/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.feat-file-map.agent.md b/.github/agents/pharaoh.feat-file-map.agent.md new file mode 100644 index 0000000..5726391 --- /dev/null +++ b/.github/agents/pharaoh.feat-file-map.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when mapping one feature (already emitted as an RST directive) to the source files that implement it. Reads the source tree, returns a YAML entry `{feat_id: {files: [...], rationale: "..."}}`. Does NOT read docs. Does NOT emit reqs. Does NOT create or modify source files. +handoffs: [] +--- + +# @pharaoh.feat-file-map + +Use when mapping one feature (already emitted as an RST directive) to the source files that implement it. Reads the source tree, returns a YAML entry `{feat_id: {files: [...], rationale: "..."}}`. Does NOT read docs. Does NOT emit reqs. Does NOT create or modify source files. + +See [`skills/pharaoh-feat-file-map/SKILL.md`](../../skills/pharaoh-feat-file-map/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.feat-flow-extract.agent.md b/.github/agents/pharaoh.feat-flow-extract.agent.md new file mode 100644 index 0000000..5d9cc2d --- /dev/null +++ b/.github/agents/pharaoh.feat-flow-extract.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when reverse-engineering a feat and you need to derive a sequence diagram showing the control flow from its entry point through its source files. Walks the call graph up to a bounded depth and emits a Mermaid or PlantUML sequence diagram whose output shape matches pharaoh-sequence-diagram-draft. Complements pharaoh-feat-component-extract (static view); this is the dynamic view. +handoffs: [] +--- + +# @pharaoh.feat-flow-extract + +Use when reverse-engineering a feat and you need to derive a sequence diagram showing the control flow from its entry point through its source files. Walks the call graph up to a bounded depth and emits a Mermaid or PlantUML sequence diagram whose output shape matches pharaoh-sequence-diagram-draft. Complements pharaoh-feat-component-extract (static view); this is the dynamic view. + +See [`skills/pharaoh-feat-flow-extract/SKILL.md`](../../skills/pharaoh-feat-flow-extract/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.feat-review.agent.md b/.github/agents/pharaoh.feat-review.agent.md new file mode 100644 index 0000000..73e9d33 --- /dev/null +++ b/.github/agents/pharaoh.feat-review.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when auditing a single feature-level need (feat) against the generic feat review axes in `shared/checklists/feat.md` plus any per-project addenda in `.pharaoh/project/checklists/feat.md`. Emits structured findings JSON — per-axis pass/fail for mechanized axes, 0-3 score for subjective axes. Mirrors `pharaoh-req-review`'s shape for feat-level artefacts. +handoffs: [] +--- + +# @pharaoh.feat-review + +Use when auditing a single feature-level need (feat) against the generic feat review axes in `shared/checklists/feat.md` plus any per-project addenda in `.pharaoh/project/checklists/feat.md`. Emits structured findings JSON — per-axis pass/fail for mechanized axes, 0-3 score for subjective axes. Mirrors `pharaoh-req-review`'s shape for feat-level artefacts. + +See [`skills/pharaoh-feat-review/SKILL.md`](../../skills/pharaoh-feat-review/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.finding-record.agent.md b/.github/agents/pharaoh.finding-record.agent.md new file mode 100644 index 0000000..35e2162 --- /dev/null +++ b/.github/agents/pharaoh.finding-record.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when recording an audit finding in the shared Papyrus workspace with automatic dedup. Uses deterministic ID to ensure the same {category, subject_id} tuple never appears twice across concurrent subagents. Returns {action: wrote|duplicate, papyrus_id}. +handoffs: [] +--- + +# @pharaoh.finding-record + +Use when recording an audit finding in the shared Papyrus workspace with automatic dedup. Uses deterministic ID to ensure the same {category, subject_id} tuple never appears twice across concurrent subagents. Returns {action: wrote|duplicate, papyrus_id}. + +See [`skills/pharaoh-finding-record/SKILL.md`](../../skills/pharaoh-finding-record/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.flow.agent.md b/.github/agents/pharaoh.flow.agent.md new file mode 100644 index 0000000..028d3e8 --- /dev/null +++ b/.github/agents/pharaoh.flow.agent.md @@ -0,0 +1,12 @@ +--- +description: Use when orchestrating the full V-model chain for one feature context across the optional ISO 26262 safety V (hazard / safety_goal / fsr), the ASPICE SYS layer (sysreq / sys-arch), the ASPICE SW layer (swreq / swarch), and the classical component V (req / comp_req then arch then vplan then fmea), each with a review pass. Auto-detects which layers to run from the project's artefact-catalog.yaml; the caller can pass a stages argument to skip layers explicitly. Dispatches to pharaoh-req-draft, pharaoh-req-review, pharaoh-arch-draft, pharaoh-arch-review, pharaoh-vplan-draft, pharaoh-vplan-review, and pharaoh-fmea — safety-V types route through pharaoh-req-draft with the appropriate target_level, no new safety-V drafting skills are introduced. +handoffs: [] +--- + +# @pharaoh.flow + +Use when orchestrating the full V-model chain for one feature context across the optional ISO 26262 safety V (hazard / safety_goal / fsr), the ASPICE SYS layer (sysreq / sys-arch), the ASPICE SW layer (swreq / swarch), and the classical component V (req / comp_req then arch then vplan then fmea), each with a review pass. Auto-detects which layers to run from the project's artefact-catalog.yaml; the caller can pass a stages argument to skip layers explicitly. + +Dispatches to pharaoh-req-draft, pharaoh-req-review, pharaoh-arch-draft, pharaoh-arch-review, pharaoh-vplan-draft, pharaoh-vplan-review, and pharaoh-fmea. Safety-V types route through pharaoh-req-draft with the appropriate target_level (hazard, safety_goal, fsr) — no new safety-V drafting skills are introduced. + +See [`skills/pharaoh-flow/SKILL.md`](../../skills/pharaoh-flow/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.fmea-review.agent.md b/.github/agents/pharaoh.fmea-review.agent.md new file mode 100644 index 0000000..9f8c623 --- /dev/null +++ b/.github/agents/pharaoh.fmea-review.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when auditing a single FMEA entry (failure-mode row) against the generic FMEA review axes in `shared/checklists/fmea.md` plus per-project addenda. Checks severity/occurrence/detection scales, RPN computation, cause/effect well-formedness, traceability to the analyzed artefact. Emits structured findings JSON. +handoffs: [] +--- + +# @pharaoh.fmea-review + +Use when auditing a single FMEA entry (failure-mode row) against the generic FMEA review axes in `shared/checklists/fmea.md` plus per-project addenda. Checks severity/occurrence/detection scales, RPN computation, cause/effect well-formedness, traceability to the analyzed artefact. Emits structured findings JSON. + +See [`skills/pharaoh-fmea-review/SKILL.md`](../../skills/pharaoh-fmea-review/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.fmea.agent.md b/.github/agents/pharaoh.fmea.agent.md new file mode 100644 index 0000000..9b3f4b4 --- /dev/null +++ b/.github/agents/pharaoh.fmea.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when deriving a single failure-mode entry (FMEA / DFA row) from one requirement or architecture element. Emits structured JSON with cause, effect, severity (1-10), occurrence (1-10), detection (1-10), and RPN. +handoffs: [] +--- + +# @pharaoh.fmea + +Use when deriving a single failure-mode entry (FMEA / DFA row) from one requirement or architecture element. Emits structured JSON with cause, effect, severity (1-10), occurrence (1-10), detection (1-10), and RPN. + +See [`skills/pharaoh-fmea/SKILL.md`](../../skills/pharaoh-fmea/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.gate-advisor.agent.md b/.github/agents/pharaoh.gate-advisor.agent.md new file mode 100644 index 0000000..0991f5c --- /dev/null +++ b/.github/agents/pharaoh.gate-advisor.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when reading a project's `pharaoh.toml` to report which phased-enablement ladder step is the recommended next gate to switch on. Single mechanical advisory check — parses five flags (`strictness`, `require_verification`, `require_change_analysis`, `require_mece_on_release`, `codelinks.enabled`), walks the fixed ladder in order, and emits the first unmet step plus its blocker note. Read-only; never edits `pharaoh.toml`. +handoffs: [] +--- + +# @pharaoh.gate-advisor + +Use when reading a project's `pharaoh.toml` to report which phased-enablement ladder step is the recommended next gate to switch on. Single mechanical advisory check — parses five flags (`strictness`, `require_verification`, `require_change_analysis`, `require_mece_on_release`, `codelinks.enabled`), walks the fixed ladder in order, and emits the first unmet step plus its blocker note. Read-only; never edits `pharaoh.toml`. + +See [`skills/pharaoh-gate-advisor/SKILL.md`](../../skills/pharaoh-gate-advisor/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.id-allocate.agent.md b/.github/agents/pharaoh.id-allocate.agent.md new file mode 100644 index 0000000..bb80390 --- /dev/null +++ b/.github/agents/pharaoh.id-allocate.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when about to dispatch a fan-out of emission subagents (pharaoh-req-from-code, pharaoh-feat-draft-from-docs) and you need to pre-allocate globally-unique sphinx-needs IDs. Each subagent receives its pre-allocated pool and emits only from that pool, so parallel agents cannot collide on stem choice. Does NOT invoke emitters, does NOT write RST. +handoffs: [] +--- + +# @pharaoh.id-allocate + +Use when about to dispatch a fan-out of emission subagents (pharaoh-req-from-code, pharaoh-feat-draft-from-docs) and you need to pre-allocate globally-unique sphinx-needs IDs. Each subagent receives its pre-allocated pool and emits only from that pool, so parallel agents cannot collide on stem choice. Does NOT invoke emitters, does NOT write RST. + +See [`skills/pharaoh-id-allocate/SKILL.md`](../../skills/pharaoh-id-allocate/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.id-convention-check.agent.md b/.github/agents/pharaoh.id-convention-check.agent.md new file mode 100644 index 0000000..f8e2ff2 --- /dev/null +++ b/.github/agents/pharaoh.id-convention-check.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when verifying that every need id in a sphinx-needs corpus matches the regex declared for its type in `.pharaoh/project/id-conventions.yaml`. Single mechanical structural check — applies the tailored per-type regex, emits a list of violations. Does NOT auto-detect how many schemes coexist — scheme policy is the tailoring author's responsibility (declare an alternation to allow multiple forms). +handoffs: [] +--- + +# @pharaoh.id-convention-check + +Use when verifying that every need id in a sphinx-needs corpus matches the regex declared for its type in `.pharaoh/project/id-conventions.yaml`. Single mechanical structural check — applies the tailored per-type regex, emits a list of violations. Does NOT auto-detect how many schemes coexist — scheme policy is the tailoring author's responsibility (declare an alternation to allow multiple forms). + +See [`skills/pharaoh-id-convention-check/SKILL.md`](../../skills/pharaoh-id-convention-check/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.lifecycle-check.agent.md b/.github/agents/pharaoh.lifecycle-check.agent.md new file mode 100644 index 0000000..b7474ef --- /dev/null +++ b/.github/agents/pharaoh.lifecycle-check.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when verifying a sphinx-needs artefact's current lifecycle state and the legality of a requested state transition per the project's workflows.yaml state machine. +handoffs: [] +--- + +# @pharaoh.lifecycle-check + +Use when verifying a sphinx-needs artefact's current lifecycle state and the legality of a requested state transition per the project's workflows.yaml state machine. + +See [`skills/pharaoh-lifecycle-check/SKILL.md`](../../skills/pharaoh-lifecycle-check/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.link-completeness-check.agent.md b/.github/agents/pharaoh.link-completeness-check.agent.md new file mode 100644 index 0000000..6d91804 --- /dev/null +++ b/.github/agents/pharaoh.link-completeness-check.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when verifying outgoing-link coverage across a full needs.json graph. For each declared link type in `artefact-catalog.yaml`, confirms every need of the governed type carries a non-empty value AND every target id resolves to an existing need. Closes the "catalogue declares `verifies` required but half the reqs ship without it" failure class. +handoffs: [] +--- + +# @pharaoh.link-completeness-check + +Use when verifying outgoing-link coverage across a full needs.json graph. For each declared link type in `artefact-catalog.yaml`, confirms every need of the governed type carries a non-empty value AND every target id resolves to an existing need. Closes the "catalogue declares `verifies` required but half the reqs ship without it" failure class. + +See [`skills/pharaoh-link-completeness-check/SKILL.md`](../../skills/pharaoh-link-completeness-check/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.mece.agent.md b/.github/agents/pharaoh.mece.agent.md new file mode 100644 index 0000000..ec4d116 --- /dev/null +++ b/.github/agents/pharaoh.mece.agent.md @@ -0,0 +1,145 @@ +--- +description: Use when checking for gaps, redundancies, and inconsistencies in sphinx-needs requirements, or validating traceability completeness +handoffs: + - label: Trace a Need + agent: pharaoh.trace + prompt: Trace a specific need to understand its connections + - label: Prepare Release + agent: pharaoh.release + prompt: Generate release notes and changelog +--- + +# @pharaoh.mece -- MECE Analysis + +Analyze a sphinx-needs project for structural completeness and consistency. MECE = Mutually Exclusive, Collectively Exhaustive. + +**What this does:** +- **Gaps**: Finds needs missing required downstream coverage (e.g., a requirement with no specification). +- **Orphans**: Finds needs disconnected from the traceability graph. +- **Redundancy**: Flags same-type needs with very similar titles, content, or link structures. +- **Status inconsistencies**: Detects contradictions (e.g., parent closed but child still open). +- **ID violations**: Checks ID format compliance and duplicates. +- **Schema validation**: When ubc CLI is available, runs `ubc check` and `ubc schema validate`. + +**Differs from @pharaoh.verify**: MECE checks structure (links, gaps, orphans). Verify checks content (does the spec actually satisfy the requirement?). + +## Data Access + +1. **ubc CLI**: `ubc build needs --format json`, `ubc check`, `ubc schema validate`. +2. **ubCode MCP**: Pre-indexed data with link graph. +3. **Raw file parsing**: Read config from `ubproject.toml`/`conf.py`. Grep for directives. Build needs index and link graph. + +Read `pharaoh.toml` for `[pharaoh.traceability]` `required_links`. If not configured, use defaults based on detected types (e.g., `req -> spec`, `spec -> impl`, `impl -> test` if those types exist). + +## Process + +### Step 1: Get Project Data + +Build complete needs index and link graph. Present summary: + +``` +Project: <name> (<config source>) +Types: <directive names> +Links: <link type names> +Data source: <tier> +Needs found: <count> +Strictness: <advisory|enforcing> +Required chains: <rules> +``` + +### Step 2: Gap Analysis + +For each `required_links` rule (e.g., `"req -> spec"`): +1. Find all needs of the source type. +2. Check each has at least one link to a need of the target type. +3. Record gaps (source needs with no link to target type). + +### Step 3: Orphan Detection + +Classify each need: +- **Orphan**: No incoming AND no outgoing links. Severity: error for intermediate/leaf types, warning for root types. +- **Dead end**: Has incoming but no outgoing links. Expected for leaf types (e.g., test), error for intermediate types (e.g., spec with no impl). + +Determine root/intermediate/leaf types from the `required_links` rules. + +### Step 4: Redundancy Analysis + +Within each need type, compare pairs for: +- **Title similarity**: Identical or near-identical titles (after normalizing case/whitespace). +- **Content similarity**: Identical content or one is a subset of the other. +- **Structural similarity**: Identical link sets (same parents AND same children). + +Flag as informational. Redundancy may be intentional. + +### Step 5: Status Inconsistencies + +- Parent has closed-family status (`closed`, `done`, `verified`, `approved`) but child has open-family status (`open`, `draft`, `in_progress`). +- All children closed but parent still open. +- Status implies work done (e.g., `implemented`) but no link to the expected type exists. + +### Step 6: ID Violations + +- Check IDs match the pattern from `pharaoh.toml` `[pharaoh.id_scheme]` or the type prefix from config. +- Check for duplicate IDs across all files. +- Check format consistency within each type. + +### Step 7: Schema Validation + +If ubc CLI is available, run `ubc check` and `ubc schema validate`. Merge with file-based findings. + +### Step 8: Present Report + +``` +## MECE Analysis Report + +### Gaps (Missing Coverage) +| Source | Type | Missing | Required By | +|--------|------|---------|-------------| + +### Orphans +| Need ID | Type | Title | Issue | Severity | +|---------|------|-------|-------|----------| + +### Potential Redundancies +| Need A | Need B | Similarity | Reason | +|--------|--------|------------|--------| + +### Status Inconsistencies +| Need ID | Status | Issue | Severity | +|---------|--------|-------|----------| + +### ID Violations +| Need ID | Expected Pattern | Issue | Severity | +|---------|-----------------|-------|----------| + +### Summary +- Gaps: <N> Orphans: <N> Redundancies: <N> +- Status issues: <N> ID violations: <N> +- Overall health: <good | needs-attention | critical> +``` + +**Health**: good = 0 errors; needs-attention = 1-5 errors; critical = >5 errors. + +### Step 9: Update Session State + +Write to `.pharaoh/session.json`: +- Set `global.mece_checked = true` +- Set `global.mece_timestamp` to current timestamp. + +## Scope Options + +- **Full project** (default): Analyze all needs. +- **Single file/directory**: Restrict to needs in specified path. Still load full link graph for resolution. +- **Specific type**: Only analyze needs of specified type. +- **Specific chain**: Only check a specific `required_links` rule. + +## Strictness Behavior + +This agent has **no prerequisites**. Runs freely in any mode. Its result gates `@pharaoh.release` when `require_mece_on_release = true` in enforcing mode. + +## Constraints + +1. Do not skip steps. If a step produces no data, note it and continue. +2. Follow ALL configured link types. Do not hardcode type or link names. +3. Redundancy uses string comparison only, not semantic analysis. +4. Always update session state after completing the report. diff --git a/.github/agents/pharaoh.output-validate.agent.md b/.github/agents/pharaoh.output-validate.agent.md new file mode 100644 index 0000000..bfa1914 --- /dev/null +++ b/.github/agents/pharaoh.output-validate.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when `pharaoh-execute-plan` (or any caller) has dispatched a subagent whose output must match one of the documented schemas (RST directive, sphinx-codelinks one-line comment, YAML mapping, JSON object). Returns {valid, errors, parsed, recovery}. Callers gate subagent output through this before writing anything to disk. +handoffs: [] +--- + +# @pharaoh.output-validate + +Use when `pharaoh-execute-plan` (or any caller) has dispatched a subagent whose output must match one of the documented schemas (RST directive, sphinx-codelinks one-line comment, YAML mapping, JSON object). Returns {valid, errors, parsed, recovery}. Callers gate subagent output through this before writing anything to disk. + +See [`skills/pharaoh-output-validate/SKILL.md`](../../skills/pharaoh-output-validate/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.papyrus-non-empty-check.agent.md b/.github/agents/pharaoh.papyrus-non-empty-check.agent.md new file mode 100644 index 0000000..712d563 --- /dev/null +++ b/.github/agents/pharaoh.papyrus-non-empty-check.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when verifying that a Papyrus workspace actually received writes during a plan run. Single mechanical check — counts directives across `.papyrus/memory/*.rst` and returns pass/fail against a configured minimum. Wired into `pharaoh-quality-gate` to detect the "LLM-executor skipped the atomic Papyrus writes" failure class observed in prior dogfooding. +handoffs: [] +--- + +# @pharaoh.papyrus-non-empty-check + +Use when verifying that a Papyrus workspace actually received writes during a plan run. Single mechanical check — counts directives across `.papyrus/memory/*.rst` and returns pass/fail against a configured minimum. Wired into `pharaoh-quality-gate` to detect the "LLM-executor skipped the atomic Papyrus writes" failure class observed in prior dogfooding. + +See [`skills/pharaoh-papyrus-non-empty-check/SKILL.md`](../../skills/pharaoh-papyrus-non-empty-check/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.plan.agent.md b/.github/agents/pharaoh.plan.agent.md new file mode 100644 index 0000000..027ff2b --- /dev/null +++ b/.github/agents/pharaoh.plan.agent.md @@ -0,0 +1,133 @@ +--- +description: Use when breaking requirement changes into structured implementation tasks with workflow enforcement and dependency ordering +handoffs: + - label: Start Change Analysis + agent: pharaoh.change + prompt: Analyze the impact of the planned changes + - label: Author the planned needs + agent: pharaoh.author + prompt: Author the needs identified in this plan, one per task + - label: Verify the authored needs + agent: pharaoh.verify + prompt: Verify the authored needs against their parents and review axes +--- + +# @pharaoh.plan + +Break a set of requirement changes into ordered, actionable tasks. Each task maps to a Pharaoh agent invocation. The plan respects `pharaoh.toml` workflow gates, establishes task dependencies, and provides a roadmap for implementing changes across the requirements hierarchy. + +## Data Access + +1. **ubc CLI**: `ubc build needs --format json` for index. +2. **ubCode MCP**: Pre-indexed data. +3. **Raw file parsing**: Config from `ubproject.toml`/`conf.py`. Grep for directives. + +Read `pharaoh.toml` for strictness, workflow gates, and traceability requirements. + +## Process + +### Step 1: Get Project Data + +Build needs index and link graph. Present detection summary. + +### Step 2: Understand the Scope + +**From a Change Document** (output from @pharaoh.change): Parse target needs, affected needs, affected files. + +**From natural language**: Identify target needs by searching index. If ambiguous, present candidates. + +**For new features**: Determine which hierarchy levels need new needs based on `required_links`. + +Record: change type (modify/create), target needs, affected needs, files, hierarchy levels. + +### Step 3: Read Workflow Gates + +From `pharaoh.toml` (or defaults): +- `require_change_analysis`: Must run @pharaoh.change before @pharaoh.author. +- `require_verification`: Must run @pharaoh.verify before @pharaoh.release. +- `require_mece_on_release`: Must run @pharaoh.mece before @pharaoh.release. + +### Step 4: Build Task Sequence + +**For modifications**: +1. Change analysis (@pharaoh.change) -- skip if user provided Change Document +2. Author target needs (@pharaoh.author) -- one task per target +3. Author affected specs (@pharaoh.author) -- top-down through hierarchy +4. Author affected impls (@pharaoh.author) +5. Author affected tests (@pharaoh.author) +6. Verify all changes (@pharaoh.verify) +7. MECE check (@pharaoh.mece) -- optional +8. Release (@pharaoh.release) -- only if user is preparing a release + +**For new features**: +1. Author new requirements +2. Author new specifications +3. Author new implementations +4. Author new test cases +5. MECE check +6. Verify all + +**Ordering**: Top-down through hierarchy. Requirements before specs, specs before impls, impls before tests. + +### Step 5: Present the Plan + +``` +## Implementation Plan + +### Scope +- Change: <description> +- Type: modify|create +- Target needs: <count> +- Affected needs: <count> +- Strictness: <advisory|enforcing> + +### Tasks + +| # | Task | Agent | Target | Detail | File | Required | +|---|------|-------|--------|--------|------|----------| + +### Dependencies +- Task 1 before Tasks 2-5 +- Tasks 2-5 in order (top-down) +- Task 6 after Tasks 2-5 +``` + +Mark tasks as "Required" (enforcing) or "Recommended" (advisory). + +### Step 6: Offer Execution + +``` +Execute this plan? +1. Execute all tasks in sequence +2. Execute up to task N +3. Modify the plan first +4. Save the plan and execute later +``` + +During execution: +- Report progress after each task. +- Pause on failures or unexpected impacts. +- Allow user to pause/resume at any point. +- Update session state as each agent completes. + +### Step 7: Handle Edge Cases + +- **New impacts discovered**: Pause, report, offer to extend the plan. +- **Gate failures**: Report which prerequisite is missing, offer to insert it. +- **Conflicting changes**: Merge tasks that modify the same need. + +## Strictness Behavior + +**Advisory**: All tasks are "recommended". User can skip any task. Show tips for skipped tasks. + +**Enforcing**: Tasks mandated by workflow gates are "required". Block if user tries to skip a required task. + +## Constraints + +1. Keep plans concrete. Name every need ID, every file, every change. +2. Never auto-execute without user consent. Always present plan first. +3. Allow plan modification before and during execution. +4. Respect the hierarchy: author top-down. +5. One agent invocation per task. +6. Building the plan does not modify session state. Only execution does. +7. This agent has no workflow gates and runs freely in any mode. diff --git a/.github/agents/pharaoh.process-audit.agent.md b/.github/agents/pharaoh.process-audit.agent.md new file mode 100644 index 0000000..3911cc0 --- /dev/null +++ b/.github/agents/pharaoh.process-audit.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when running a full-corpus audit against a sphinx-needs project. Orchestrates pharaoh-coverage-gap across all gap categories plus cross-artefact consistency checks. Emits a prioritised gap report. +handoffs: [] +--- + +# @pharaoh.process-audit + +Use when running a full-corpus audit against a sphinx-needs project. Orchestrates pharaoh-coverage-gap across all gap categories plus cross-artefact consistency checks. Emits a prioritised gap report. + +See [`skills/pharaoh-process-audit/SKILL.md`](../../skills/pharaoh-process-audit/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.prose-migrate.agent.md b/.github/agents/pharaoh.prose-migrate.agent.md new file mode 100644 index 0000000..de450d1 --- /dev/null +++ b/.github/agents/pharaoh.prose-migrate.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when a reverse-engineering run (a plan emitted by pharaoh-write-plan) finds pre-existing prose documentation files in the target output directory that would collide with generated feat RST files. Produces a sentence-by-sentence migration proposal — keep-as-user-guide, merge-into-feat-body, discard. Does NOT overwrite anything; the caller applies the proposal manually. +handoffs: [] +--- + +# @pharaoh.prose-migrate + +Use when a reverse-engineering run (a plan emitted by pharaoh-write-plan) finds pre-existing prose documentation files in the target output directory that would collide with generated feat RST files. Produces a sentence-by-sentence migration proposal — keep-as-user-guide, merge-into-feat-body, discard. Does NOT overwrite anything; the caller applies the proposal manually. + +See [`skills/pharaoh-prose-migrate/SKILL.md`](../../skills/pharaoh-prose-migrate/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.quality-gate.agent.md b/.github/agents/pharaoh.quality-gate.agent.md new file mode 100644 index 0000000..d500423 --- /dev/null +++ b/.github/agents/pharaoh.quality-gate.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when running the final validation step of any Pharaoh composition that emits artefacts (reqs, features, architecture elements). Consumes an aggregated review+mece+coverage summary plus a gate spec; returns pass/fail with named breaches. Never produces summaries itself — thin gate layer over upstream atomic checkers. +handoffs: [] +--- + +# @pharaoh.quality-gate + +Use when running the final validation step of any Pharaoh composition that emits artefacts (reqs, features, architecture elements). Consumes an aggregated review+mece+coverage summary plus a gate spec; returns pass/fail with named breaches. Never produces summaries itself — thin gate layer over upstream atomic checkers. + +See [`skills/pharaoh-quality-gate/SKILL.md`](../../skills/pharaoh-quality-gate/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.release.agent.md b/.github/agents/pharaoh.release.agent.md new file mode 100644 index 0000000..089e004 --- /dev/null +++ b/.github/agents/pharaoh.release.agent.md @@ -0,0 +1,110 @@ +--- +description: Use when preparing a release, generating changelogs from requirements, or summarizing requirement changes for version management +handoffs: + - label: MECE Check + agent: pharaoh.mece + prompt: Run MECE analysis before release +--- + +# @pharaoh.release + +Generate release artifacts from sphinx-needs changes. Identifies which needs changed between releases, produces structured changelogs and release notes, and computes traceability coverage metrics for audit trails. + +## Strictness Check + +**Enforcing mode**: +1. If `require_verification = true`: Check `.pharaoh/session.json` for `verified = true` on all authored/modified needs. Block if any are unverified. +2. If `require_mece_on_release = true`: Check `global.mece_checked = true`. Block if MECE not run. + +**Advisory mode**: Proceed freely. Show tips for missing prerequisites after output. + +**User bypass**: If user says "proceed anyway", allow with a warning. + +## Data Access + +1. **ubc CLI**: `ubc build needs --format json`, `ubc diff` for structural change detection. +2. **ubCode MCP**: Pre-indexed data. +3. **Raw file parsing**: Config from `ubproject.toml`/`conf.py`. Git diff for change detection. + +## Process + +### Step 1: Get Project Data + +Build needs index and link graph. Present detection summary. + +### Step 2: Determine Release Scope + +- Get version identifier from user (or "since last release"). +- Find comparison baseline: latest git tag (`git tag --sort=-v:refname`), or user-specified ref. +- Find changed files: `git diff --name-only <baseline>..HEAD -- <source_dirs>` (or `ubc diff` if available). + +### Step 3: Identify Changed Needs + +Categorize into: **new**, **modified**, **removed**. + +- **With ubc diff** (Tier 1): Parse JSON output for need-level changes. +- **With git diff** (fallback): Look for added/removed directive lines, compare attributes between baseline and current. + +Build change summary with type breakdowns and impact chains. + +### Step 4: Generate Changelog + +```markdown +## Release <version> - <date> + +### Summary +- **<count>** new needs added +- **<count>** needs modified +- **<count>** needs removed + +### New <Type>s +- **<ID>**: <title> [<status>] + +### Modified <Type>s +- **<ID>**: <title> + - <attribute>: <old> -> <new> + - Impact: <linked IDs that may need review> + +### Removed <Type>s +- **<ID>**: <title> + +### Traceability Changes +- New links: <list> +- Removed links: <list> + +### Verification Status +- All modified needs verified: <yes/no> +- MECE analysis: <passed / not run> +``` + +Adapt sections dynamically to the project's configured types. Omit empty sections. + +### Step 5: Generate Release Summary + +**Needs inventory**: Count all needs by type and status. + +**Traceability coverage**: For each `required_links` rule, calculate `coverage = linked / total * 100%`. Include full-chain coverage. + +**MECE issues**: Summarize open issues from session state if available. + +**Codelinks summary**: If enabled, count needs with code references. + +### Step 6: Output and Next Steps + +1. Present changelog and release summary. +2. Offer to write files: + - Append to `CHANGELOG.md` + - Write to `docs/releases/<version>.md` + - Both, or neither +3. Suggest git tag: `git tag -a <version> -m "Release <version>"`. Only create if user confirms. Never push. +4. Update session state: set `global.last_release` to current timestamp. + +## Constraints + +1. Never auto-tag or auto-push without user confirmation. +2. Never overwrite files without asking. +3. Always include traceability metrics for audit trails. +4. Adapt to project-specific types and statuses. Don't hardcode. +5. Handle missing git history gracefully. +6. Never modify need directive source files. This agent is read-only for documentation. +7. Prefer `ubc diff` when available for accurate structural change detection. diff --git a/.github/agents/pharaoh.reproducibility-check.agent.md b/.github/agents/pharaoh.reproducibility-check.agent.md new file mode 100644 index 0000000..a42da34 --- /dev/null +++ b/.github/agents/pharaoh.reproducibility-check.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when diffing two output directories produced by running the same plan twice to confirm the build is reproducible. Consumes a baseline directory, a rerun directory, and an optional list of mask rules for known-non-deterministic fields (timestamps, randomly-generated ids); emits a list of drifted files with per-file changed-field summaries. Does NOT run the plan — running is the caller's responsibility (`pharaoh-execute-plan`). +handoffs: [] +--- + +# @pharaoh.reproducibility-check + +Use when diffing two output directories produced by running the same plan twice to confirm the build is reproducible. Consumes a baseline directory, a rerun directory, and an optional list of mask rules for known-non-deterministic fields (timestamps, randomly-generated ids); emits a list of drifted files with per-file changed-field summaries. Does NOT run the plan — running is the caller's responsibility (`pharaoh-execute-plan`). + +See [`skills/pharaoh-reproducibility-check/SKILL.md`](../../skills/pharaoh-reproducibility-check/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.req-code-grounding-check.agent.md b/.github/agents/pharaoh.req-code-grounding-check.agent.md new file mode 100644 index 0000000..c8d4075 --- /dev/null +++ b/.github/agents/pharaoh.req-code-grounding-check.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when verifying a single drafted requirement against the source file it cites via `:source_doc:`. Single mechanical fidelity check — compares the CREQ's claims about exceptions, triggers, types, structural symbols, backtick-quoted identifiers, grounding density, adjectives, quantifiers, and branch count against the cited source, returning per-axis findings JSON. Complements `pharaoh-req-review` (which grades prose quality) with code-grounded axes. +handoffs: [] +--- + +# @pharaoh.req-code-grounding-check + +Use when verifying a single drafted requirement against the source file it cites via `:source_doc:`. Single mechanical fidelity check — compares the CREQ's claims about exceptions, triggers, types, structural symbols, backtick-quoted identifiers, grounding density, adjectives, quantifiers, and branch count against the cited source, returning per-axis findings JSON. Complements `pharaoh-req-review` (which grades prose quality) with code-grounded axes. + +See [`skills/pharaoh-req-code-grounding-check/SKILL.md`](../../skills/pharaoh-req-code-grounding-check/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.req-codelink-annotate.agent.md b/.github/agents/pharaoh.req-codelink-annotate.agent.md new file mode 100644 index 0000000..c4f79b1 --- /dev/null +++ b/.github/agents/pharaoh.req-codelink-annotate.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when a requirement has been drafted (either as an RST block by `pharaoh-req-from-code` or implicitly) and you need to insert a one-line comment into the source file that carries the trace. Two modes — `codelinks` (sphinx-codelinks-compatible multi-field `@ title, id, type, [links]` form; the comment IS the need) and `backref` (minimal `@req ID: title` pointer back to an RST-hosted need). Mode is tailored via `ubproject.toml` / `pharaoh.toml`, not hardcoded. +handoffs: [] +--- + +# @pharaoh.req-codelink-annotate + +Use when a requirement has been drafted (either as an RST block by `pharaoh-req-from-code` or implicitly) and you need to insert a one-line comment into the source file that carries the trace. Two modes — `codelinks` (sphinx-codelinks-compatible multi-field `@ title, id, type, [links]` form; the comment IS the need) and `backref` (minimal `@req ID: title` pointer back to an RST-hosted need). Mode is tailored via `ubproject.toml` / `pharaoh.toml`, not hardcoded. + +See [`skills/pharaoh-req-codelink-annotate/SKILL.md`](../../skills/pharaoh-req-codelink-annotate/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.req-draft.agent.md b/.github/agents/pharaoh.req-draft.agent.md new file mode 100644 index 0000000..6021bfd --- /dev/null +++ b/.github/agents/pharaoh.req-draft.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when drafting a single sphinx-needs requirement-shaped artefact (req, comp_req, sysreq, swreq, hazard, safety_goal, fsr, etc.) from a feature description. The artefact type is parameterised via target_level (any catalog-declared requirement-shaped type, including ISO 26262 safety-V types). Produces a new RST directive block with ID, status=draft, and either a shall-clause body or a hazard/goal-shaped body, linking to a parent per the project's artefact-catalog. +handoffs: [] +--- + +# @pharaoh.req-draft + +Use when drafting a single sphinx-needs requirement-shaped artefact (req, comp_req, sysreq, swreq, hazard, safety_goal, fsr, etc.) from a feature description. The artefact type is parameterised via target_level (any catalog-declared requirement-shaped type, including ISO 26262 safety-V types). Produces a new RST directive block with ID, status=draft, and either a shall-clause body or a hazard/goal-shaped body, linking to a parent per the project's artefact-catalog. + +See [`skills/pharaoh-req-draft/SKILL.md`](../../skills/pharaoh-req-draft/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.req-from-code.agent.md b/.github/agents/pharaoh.req-from-code.agent.md new file mode 100644 index 0000000..9e7bb93 --- /dev/null +++ b/.github/agents/pharaoh.req-from-code.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when reading one source file and emitting one or more requirement RST directives (typed by `target_level`) describing the observable behavior in that file. Queries shared Papyrus for canonical terms before naming concepts; writes newly surfaced concepts back. Does not draft architecture, plans, or FMEA. +handoffs: [] +--- + +# @pharaoh.req-from-code + +Use when reading one source file and emitting one or more requirement RST directives (typed by `target_level`) describing the observable behavior in that file. Queries shared Papyrus for canonical terms before naming concepts; writes newly surfaced concepts back. Does not draft architecture, plans, or FMEA. + +See [`skills/pharaoh-req-from-code/SKILL.md`](../../skills/pharaoh-req-from-code/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.req-regenerate.agent.md b/.github/agents/pharaoh.req-regenerate.agent.md new file mode 100644 index 0000000..28f7407 --- /dev/null +++ b/.github/agents/pharaoh.req-regenerate.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when regenerating a single sphinx-needs requirement to address findings from pharaoh-req-review. Consumes the original RST + findings JSON, emits a revised RST directive that passes the flagged axes. +handoffs: [] +--- + +# @pharaoh.req-regenerate + +Use when regenerating a single sphinx-needs requirement to address findings from pharaoh-req-review. Consumes the original RST + findings JSON, emits a revised RST directive that passes the flagged axes. + +See [`skills/pharaoh-req-regenerate/SKILL.md`](../../skills/pharaoh-req-regenerate/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.req-review.agent.md b/.github/agents/pharaoh.req-review.agent.md new file mode 100644 index 0000000..dabf554 --- /dev/null +++ b/.github/agents/pharaoh.req-review.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when auditing a single sphinx-needs requirement against the 11 ISO 26262 Part 8 §6 axes. Emits structured findings JSON — per-axis pass/fail for mechanized axes, 0-3 score for subjective axes, with action items for any failure. +handoffs: [] +--- + +# @pharaoh.req-review + +Use when auditing a single sphinx-needs requirement against the 11 ISO 26262 Part 8 §6 axes. Emits structured findings JSON — per-axis pass/fail for mechanized axes, 0-3 score for subjective axes, with action items for any failure. + +See [`skills/pharaoh-req-review/SKILL.md`](../../skills/pharaoh-req-review/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.review-completeness.agent.md b/.github/agents/pharaoh.review-completeness.agent.md new file mode 100644 index 0000000..df8be0e --- /dev/null +++ b/.github/agents/pharaoh.review-completeness.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when inspecting one or more needs for review / approval-chain completeness. Flags needs missing required :reviewer: or :approved_by: fields per the project's artefact catalog. Emits one finding per incomplete need via pharaoh-finding-record. +handoffs: [] +--- + +# @pharaoh.review-completeness + +Use when inspecting one or more needs for review / approval-chain completeness. Flags needs missing required :reviewer: or :approved_by: fields per the project's artefact catalog. Emits one finding per incomplete need via pharaoh-finding-record. + +See [`skills/pharaoh-review-completeness/SKILL.md`](../../skills/pharaoh-review-completeness/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.self-review-coverage-check.agent.md b/.github/agents/pharaoh.self-review-coverage-check.agent.md new file mode 100644 index 0000000..e93ca48 --- /dev/null +++ b/.github/agents/pharaoh.self-review-coverage-check.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when verifying that every artefact emitted during a plan run received a matching review. For every drafted artefact in `runs/`, confirms a matching `<id>_review.json` exists and is non-empty. Closes the "draft emitted but review was skipped" failure class. +handoffs: [] +--- + +# @pharaoh.self-review-coverage-check + +Use when verifying that every artefact emitted during a plan run received a matching review. For every drafted artefact in `runs/`, confirms a matching `<id>_review.json` exists and is non-empty. Closes the "draft emitted but review was skipped" failure class. + +See [`skills/pharaoh-self-review-coverage-check/SKILL.md`](../../skills/pharaoh-self-review-coverage-check/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.sequence-diagram-draft.agent.md b/.github/agents/pharaoh.sequence-diagram-draft.agent.md new file mode 100644 index 0000000..666e39c --- /dev/null +++ b/.github/agents/pharaoh.sequence-diagram-draft.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when drafting one sequence diagram showing ordered interactions between participants (components, actors, external systems) over time. Renderer tailored via `pharaoh.toml`. Does NOT emit component, class, or state diagrams. Status — PLANNED (design-only scaffold; invoking returns sentinel FAIL until implemented). +handoffs: [] +--- + +# @pharaoh.sequence-diagram-draft + +Use when drafting one sequence diagram showing ordered interactions between participants (components, actors, external systems) over time. Renderer tailored via `pharaoh.toml`. Does NOT emit component, class, or state diagrams. Status — PLANNED (design-only scaffold; invoking returns sentinel FAIL until implemented). + +See [`skills/pharaoh-sequence-diagram-draft/SKILL.md`](../../skills/pharaoh-sequence-diagram-draft/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.setup.agent.md b/.github/agents/pharaoh.setup.agent.md new file mode 100644 index 0000000..c0b857c --- /dev/null +++ b/.github/agents/pharaoh.setup.agent.md @@ -0,0 +1,133 @@ +--- +description: Use when setting up Pharaoh in a sphinx-needs project for the first time, scaffolding Copilot agents, or reconfiguring project detection. Reads project state (declared types, fields, links, observed RST IDs and statuses) before imposing Pharaoh-internal defaults. +handoffs: + - label: Run MECE Check + agent: pharaoh.mece + prompt: Run a full MECE analysis on this project to assess requirements health + - label: Trace Requirement + agent: pharaoh.trace + prompt: Trace a requirement through all levels +--- + +# @pharaoh.setup + +Scaffold Pharaoh into a sphinx-needs project. Detect the project structure, read its declared conventions and existing artefacts, generate a `pharaoh.toml` configuration file, seed `.pharaoh/project/` tailoring descriptively from observation, and recommend tooling for the best experience. + +## Data Access + +Use the best available data source in this priority order: + +1. **ubc CLI** (best): Run `ubc --version` to check. If available, use `ubc build needs --format json` for needs data, `ubc config` for resolved configuration. +2. **ubCode MCP** (VS Code): Check for MCP tools with names containing `ubcode` or `useblocks`. Use them for pre-indexed data. +3. **Raw file parsing** (fallback): Read `ubproject.toml` or `conf.py` directly for configuration. Use file search to find `.rst` and `.md` files containing need directives. + +## Process + +### Step 1: Detect Project Structure + +1. Search for `ubproject.toml` files in the workspace root and up to two levels of subdirectories. Each location is a project root. +2. If no `ubproject.toml` is found, search for `conf.py` files containing `sphinx_needs`, `needs_types`, or `needs_from_toml`. +3. For each project root, read the configuration: + - **From `ubproject.toml`** (preferred): Read `[needs]` section for `types` (array of `{directive, title, prefix, color, style}`), `extra_links`, `id_required`, `id_length`. Note: settings do NOT use the `needs_` prefix. + - **From `conf.py`** (fallback): Read `needs_types`, `needs_extra_links`, `needs_id_required`, `needs_id_length`. Settings use the `needs_` prefix. +4. Locate the documentation source directory: check `docs/`, `source/`, or `conf.py` for source configuration. +5. Check for sphinx-codelinks: look for `sphinx_codelinks` in extensions. +6. Check ubc CLI availability: `ubc --version`. +7. Check ubCode MCP availability: look for ubcode/useblocks MCP tools. + +Present detection summary: + +``` +Pharaoh Project Detection +========================= +Project roots found: <count> + +Project: <name> + Root: <path> + Source dir: <path> + Config: ubproject.toml | conf.py + Types: <directive names> + Extra links: <link option names> + ID required: yes/no + ID length: <number> + Codelinks: detected/not detected + +Data access: + ubc CLI: <available (version) | not available> + ubCode MCP: <available | not available> +``` + +### Step 2: Generate pharaoh.toml and seed .pharaoh/project/ descriptively + +1. Ask the user for strictness preference: `advisory` (default, suggests but never blocks) or `enforcing` (checks prerequisites, blocks if not met). +2. **Classify project mode from declared types and RST content** — not from `needs.json` existence (a gitignored build artefact). `[[needs.types]]` declared + RST has needs with ≥10% in matured statuses → `steady-state`; types declared + RST has needs → `reverse-eng`; types declared + no needs → `greenfield`. +3. **Detect the ID pattern from up to 20 sampled IDs** in `<source-dir>/**/*.rst`. Recognise `{TYPE}_{NUMBER}`, `{TYPE}-{MODULE}-{NUMBER}`, and `{DOMAIN}_{NUMBER}` (leading token is not a declared type prefix — e.g. `BRAKE_CTRL_01`). Reject the heuristic `{TYPE}_{NUMBER}` default when observed IDs do not conform. +4. **Read `[needs.fields.X]` and `[needs.links.X]` from `ubproject.toml`** to populate `optional_fields`, `required_metadata_fields`, `required_links`, `optional_links`, `required_roles` per declared type for `.pharaoh/project/artefact-catalog.yaml`. Fall back to Pharaoh-internal defaults (`reviewer`, `approved_by`, `source_doc`) only when the project declares no fields. +5. **Compute `lifecycle_states` from a status histogram** of `:status:` values in existing RST files. Fall back to `[draft, reviewed, approved]` only when no `:status:` is observed anywhere. +6. **Detect ID-prefix collisions in `[[needs.types]]`.** In `advisory` strictness, WARN with a remediation hint and proceed; in `enforcing`, FAIL and refuse to write `id-conventions.yaml`. +7. **Direction-infer `required_links` from edges**, not link names. Apply the rule uniformly to every link option declared in `[needs.links.<name>]` (no per-name allow-list). Source 1 (built `needs.json` ≥3 instances + ≥90% coverage) → Source 2 (declared `from`/`to` hint) → Source 3 (refuse to guess; emit TODO comment). +8. Check if `pharaoh.toml` already exists. If so, show a diff and ask what to do. +9. Present the generated content and get confirmation before writing. + +After writing `pharaoh.toml`, invoke `pharaoh-tailor-bootstrap` with the descriptive overrides from steps 4-7 above so `.pharaoh/project/{workflows,id-conventions,artefact-catalog}.yaml` capture what the project declares and uses, not Pharaoh-internal placeholders. + +### Step 3: Configure .gitignore + +`.pharaoh/` contains a mix of committed tailoring and ephemeral run state. Ignoring the whole tree is wrong — it hides `.pharaoh/project/` tailoring which IS shared across the team. Ignore only the ephemeral subpaths: + +| Path | Purpose | Commit? | +| ----------------------- | -------------------------------------------------------- | ------- | +| `.pharaoh/project/` | Tailoring: workflows, id-conventions, artefact-catalog, checklists | **yes** | +| `.pharaoh/runs/` | `pharaoh-execute-plan` run artefacts (report.yaml, staged RST) | no | +| `.pharaoh/plans/` | plan.yaml files emitted by `pharaoh-write-plan` | no | +| `.pharaoh/session.json` | Session / gate state | no | +| `.pharaoh/cache/` | Derived caches | no | + +Entries to add (create `.gitignore` if missing): + +``` +.pharaoh/runs/ +.pharaoh/plans/ +.pharaoh/session.json +.pharaoh/cache/ +``` + +If `.gitignore` already contains a bare `.pharaoh/` (or `.pharaoh`) line, leave it alone and warn the user that the wide form hides `.pharaoh/project/` tailoring which should be committed; recommend narrowing to the four ephemeral entries above. Do not auto-migrate — respect user control. + +### Step 4: Recommend Tooling + +Present the three experience tiers: + +| Tier | What's installed | Experience | +|------|-----------------|------------| +| Basic | Pharaoh only | AI reads files directly. Works everywhere, slower on large projects. | +| Good | + ubc CLI | Fast deterministic indexing, JSON output, CI/CD compatible. | +| Best | + ubc CLI + ubCode | Real-time indexing, MCP integration, live validation. | + +Report the current tier based on what was detected. + +### Step 5: Summary + +Present everything configured and list available agents: + +``` +Available agents (GitHub Copilot): + <enumerate from the `.github/agents/pharaoh.*.agent.md` files installed + in this project, one entry per file in alphabetical order, formatted as + @pharaoh.<name>. Do not hardcode this list — the skill set has grown + beyond the original happy-path agents to include atomic skills like + pharaoh.req-draft, pharaoh.req-review, pharaoh.arch-draft, + pharaoh.tailor-detect, pharaoh.tailor-fill, pharaoh.audit-fanout, and + others.> + +Workflow: @pharaoh.change -> @pharaoh.author -> @pharaoh.verify -> @pharaoh.release +``` + +Recommend running `@pharaoh.mece` next. + +## Constraints + +1. Never overwrite files without asking. Always show what will be created and get confirmation. +2. `pharaoh.toml` controls only Pharaoh's behavior. Never re-define need types or link types from `ubproject.toml`. +3. Degrade gracefully when tools are missing. +4. This agent has no workflow gates and runs freely in any mode. diff --git a/.github/agents/pharaoh.spec.agent.md b/.github/agents/pharaoh.spec.agent.md new file mode 100644 index 0000000..a3b87b5 --- /dev/null +++ b/.github/agents/pharaoh.spec.agent.md @@ -0,0 +1,124 @@ +--- +description: Use when generating a Superpowers-compatible spec and plan document from sphinx-needs requirements, bridging requirements to implementation +handoffs: + - label: Execute Plan + agent: pharaoh.plan + prompt: Execute the plan table from the generated spec + - label: Record Decision + agent: pharaoh.decide + prompt: Record a design decision for a gap in the requirements + - label: MECE Check + agent: pharaoh.mece + prompt: Check for traceability gaps in the spec scope +--- + +# @pharaoh.spec + +Generate a Superpowers-compatible spec document from sphinx-needs requirements. Reads the needs hierarchy, identifies gaps, records decisions via @pharaoh.decide, and produces a markdown spec with an embedded plan table for @pharaoh.plan. + +Output location: `docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md` (overridable by user). + +## Data Access + +1. **ubc CLI**: `ubc build needs --format json` for index, `ubc config` for schema. +2. **ubCode MCP**: Pre-indexed needs data. +3. **Raw file parsing**: Read `ubproject.toml`/`conf.py` for types, extra_links, ID settings. Grep for directives. Parse needs. + +Read `pharaoh.toml` for strictness, workflow gates, traceability requirements, and `required_links` chains. + +## Process + +### Step 1: Get Project Data + +Build needs index and full link graph (both directions for all link types). Present detection summary. If detection fails, report and ask for guidance. + +### Step 2: Parse Input + +Accept one or more requirement IDs. Validate against the needs index. + +- **IDs not found**: Report, suggest similar IDs, ask for confirmation. +- **Natural language**: Resolve by title match, substring, content, or tags. Present candidates if multiple match. +- **Multiple IDs**: Produce a single combined spec document. + +### Step 3: Resolve Requirements Scope + +For each input requirement: + +1. **Pull full text**: ID, title, type, status, content, tags, all links, custom fields. Requirements appear verbatim in the spec. +2. **Trace downstream**: Follow all link types recursively. Collect **references only** (ID, type, title, status, link to parent) for downstream needs. +3. **Build scope tree**: Show requirement at root with all downstream coverage and gaps. +4. **Identify gaps**: Use `required_links` chains from `pharaoh.toml` (or infer from types). Gaps: missing specs, impls, tests, or partial coverage. + +### Step 4: Present Scope Summary + +Show counts of requirements (full text), specs, impls, tests (references), gaps, and decisions needed. Warn if scope exceeds 30 downstream needs. Wait for user confirmation. + +### Step 5: Make Design Decisions + +For each gap needing a design choice (decomposition, technology, test strategy, conflicting constraints), invoke @pharaoh.decide programmatically with: + +- **decided_by**: `claude` +- **status**: `accepted` +- All other fields (title, decides, alternatives, rationale) populated from context. + +Write all decisions BEFORE generating the spec. The spec must reference stable decision IDs, not placeholders. + +If all gaps are straightforward, skip decision recording and note it in the spec. + +### Step 6: Generate the Spec Document + +Write to `docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md`. Create the directory if needed. + +**Required sections in order**: Requirements (source of truth, full verbatim text), Existing coverage (reference table), Gaps (unchecked checkboxes), Decisions (IDs from Step 5), Implementation scope (needs to create/modify tables, "None" if empty), Plan table (built in Step 7). + +Full text for requirements, ref-only for downstream. Decisions must reference stable IDs written in Step 5. + +### Step 7: Build the Plan Table + +Follow @pharaoh.plan task sequencing: + +1. **Change analysis first** (if modifying existing needs). +2. **Author top-down**: Requirements > specs > impls > tests. New before modifications. +3. **Verify after all authoring**. +4. **MECE check** if `require_mece_on_release = true` or multi-level scope. + +Each row: sequential number, concise task, exact skill name, concrete target (need ID, `(new)`, or `(all)`), specific detail, file path or `--`, and required field. + +### Step 8: Handoff + +Present the file path and options: + +``` +Spec document written to: docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md + +Options: + 1. Execute the plan via @pharaoh.plan + 2. Review or modify the spec first + 3. Execute later (plan is saved in the spec document) +``` + +Never auto-execute. Always wait for explicit user approval. + +## Strictness Behavior + +**Advisory mode**: Execute freely. No gates. All plan table tasks marked `recommended`. After generating, show: +``` +Tip: Consider reviewing the spec before executing the plan. +The spec captures design decisions that affect downstream authoring. +``` + +**Enforcing mode**: Execute freely. No gates. Plan table tasks mandated by workflow gates marked `yes`: +- `@pharaoh.change` if `require_change_analysis = true` +- `@pharaoh.verify` if `require_verification = true` +- `@pharaoh.mece` if `require_mece_on_release = true` + +Both modes perform identical analysis depth. Strictness only affects the `Required` column. + +## Constraints + +1. **Full text for requirements, references only for downstream.** Spec is self-contained for requirements but does not duplicate downstream content. +2. **Decisions written before the spec references them.** Always invoke @pharaoh.decide first, collect the ID, then use it. +3. **Plan table format matches @pharaoh.plan exactly.** Same columns, granularity, and semantics. +4. **Never auto-execute.** Present the complete spec and wait for approval before invoking downstream skills. +5. **Single combined spec for multiple requirements.** Do not produce separate documents. +6. **No session state changes from spec generation.** Only @pharaoh.decide and @pharaoh.plan update session state. diff --git a/.github/agents/pharaoh.sphinx-extension-add.agent.md b/.github/agents/pharaoh.sphinx-extension-add.agent.md new file mode 100644 index 0000000..9f9e20c --- /dev/null +++ b/.github/agents/pharaoh.sphinx-extension-add.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when you need to idempotently add one or more sphinx extension modules to a project's `conf.py` extensions list, optionally installing the corresponding pypi packages via the detected package manager. Invoked by plans produced by pharaoh-write-plan when a diagram-emitting task requires a renderer extension that `conf.py` does not yet load. Does NOT emit RST. Does NOT build. +handoffs: [] +--- + +# @pharaoh.sphinx-extension-add + +Use when you need to idempotently add one or more sphinx extension modules to a project's `conf.py` extensions list, optionally installing the corresponding pypi packages via the detected package manager. Invoked by plans produced by pharaoh-write-plan when a diagram-emitting task requires a renderer extension that `conf.py` does not yet load. Does NOT emit RST. Does NOT build. + +See [`skills/pharaoh-sphinx-extension-add/SKILL.md`](../../skills/pharaoh-sphinx-extension-add/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.standard-conformance.agent.md b/.github/agents/pharaoh.standard-conformance.agent.md new file mode 100644 index 0000000..ae0163c --- /dev/null +++ b/.github/agents/pharaoh.standard-conformance.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when evaluating a single sphinx-needs artefact against one regulatory standard (ISO 26262-8 §6, ASPICE 4.0, ISO/SAE 21434). Emits per-indicator findings JSON with pass/fail on mechanizable indicators and 0-3 scores on subjective ones. +handoffs: [] +--- + +# @pharaoh.standard-conformance + +Use when evaluating a single sphinx-needs artefact against one regulatory standard (ISO 26262-8 §6, ASPICE 4.0, ISO/SAE 21434). Emits per-indicator findings JSON with pass/fail on mechanizable indicators and 0-3 scores on subjective ones. + +See [`skills/pharaoh-standard-conformance/SKILL.md`](../../skills/pharaoh-standard-conformance/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.state-diagram-draft.agent.md b/.github/agents/pharaoh.state-diagram-draft.agent.md new file mode 100644 index 0000000..ec261c4 --- /dev/null +++ b/.github/agents/pharaoh.state-diagram-draft.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when drafting one state-machine diagram showing lifecycle or behavioral states of a component/entity, with labeled transitions. Renderer tailored via `pharaoh.toml`. Does NOT emit component, sequence, or class diagrams. Status — PLANNED (design-only scaffold; invoking returns sentinel FAIL until implemented). +handoffs: [] +--- + +# @pharaoh.state-diagram-draft + +Use when drafting one state-machine diagram showing lifecycle or behavioral states of a component/entity, with labeled transitions. Renderer tailored via `pharaoh.toml`. Does NOT emit component, sequence, or class diagrams. Status — PLANNED (design-only scaffold; invoking returns sentinel FAIL until implemented). + +See [`skills/pharaoh-state-diagram-draft/SKILL.md`](../../skills/pharaoh-state-diagram-draft/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.status-lifecycle-check.agent.md b/.github/agents/pharaoh.status-lifecycle-check.agent.md new file mode 100644 index 0000000..f04f403 --- /dev/null +++ b/.github/agents/pharaoh.status-lifecycle-check.agent.md @@ -0,0 +1,13 @@ +--- +description: Use when running a release-gate check over a full sphinx-needs corpus to confirm that zero needs remain in the initial `draft` status. Single mechanical binary gate — aggregates `status` across every need in `needs.json`, compares against the initial-state declaration in `workflows.yaml`, and returns pass/fail plus per-status counts. Advisory by default (pre-release development passes); release pipelines override `enforce=true` so any draft blocks the gate. +handoffs: + - label: Aggregate into quality gate + agent: pharaoh.quality-gate + prompt: Consume the status-lifecycle findings as the delegated check for the status-lifecycle-healthy invariant +--- + +# @pharaoh.status-lifecycle-check + +Use when running a release-gate check over a full sphinx-needs corpus to confirm that zero needs remain in the initial `draft` status. Single mechanical binary gate — aggregates `status` across every need in `needs.json`, compares against the initial-state declaration in `workflows.yaml`, and returns pass/fail plus per-status counts. Advisory by default (pre-release development passes); release pipelines override `enforce=true` so any draft blocks the gate. + +See [`skills/pharaoh-status-lifecycle-check/SKILL.md`](../../skills/pharaoh-status-lifecycle-check/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.tailor-bootstrap.agent.md b/.github/agents/pharaoh.tailor-bootstrap.agent.md new file mode 100644 index 0000000..2e37a48 --- /dev/null +++ b/.github/agents/pharaoh.tailor-bootstrap.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when a sphinx-needs project has just been bootstrapped (post pharaoh-bootstrap, pre any needs authoring) and you need to generate minimal tailoring files from declared types — workflows.yaml, id-conventions.yaml, artefact-catalog.yaml, and per-type checklists — without requiring any needs to exist. Complements pharaoh-tailor-detect which requires ≥10 needs. +handoffs: [] +--- + +# @pharaoh.tailor-bootstrap + +Use when a sphinx-needs project has just been bootstrapped (post pharaoh-bootstrap, pre any needs authoring) and you need to generate minimal tailoring files from declared types — workflows.yaml, id-conventions.yaml, artefact-catalog.yaml, and per-type checklists — without requiring any needs to exist. Complements pharaoh-tailor-detect which requires ≥10 needs. + +See [`skills/pharaoh-tailor-bootstrap/SKILL.md`](../../skills/pharaoh-tailor-bootstrap/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.tailor-code-grounding-filters.agent.md b/.github/agents/pharaoh.tailor-code-grounding-filters.agent.md new file mode 100644 index 0000000..f2f5bb6 --- /dev/null +++ b/.github/agents/pharaoh.tailor-code-grounding-filters.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when authoring a project's `code-grounding-filters.yaml` from observed stack conventions. Detects language + CLI framework + config-object style in the project source tree and emits a tailoring YAML populated with the four parameterised filter strategies. Does not invoke `pharaoh-req-code-grounding-check`; purely produces tailoring. +handoffs: [] +--- + +# @pharaoh.tailor-code-grounding-filters + +Use when authoring a project's `code-grounding-filters.yaml` from observed stack conventions. Detects language + CLI framework + config-object style in the project source tree and emits a tailoring YAML populated with the four parameterised filter strategies. Does not invoke `pharaoh-req-code-grounding-check`; purely produces tailoring. + +See [`skills/pharaoh-tailor-code-grounding-filters/SKILL.md`](../../skills/pharaoh-tailor-code-grounding-filters/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.tailor-detect.agent.md b/.github/agents/pharaoh.tailor-detect.agent.md new file mode 100644 index 0000000..680f063 --- /dev/null +++ b/.github/agents/pharaoh.tailor-detect.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when inspecting a sphinx-needs project to emit a structured report of detected conventions — prefixes, ID regex candidates, separator, lifecycle states, artefact types with observed required/optional fields. Does NOT author tailoring files (see pharaoh-tailor-fill). +handoffs: [] +--- + +# @pharaoh.tailor-detect + +Use when inspecting a sphinx-needs project to emit a structured report of detected conventions — prefixes, ID regex candidates, separator, lifecycle states, artefact types with observed required/optional fields. Does NOT author tailoring files (see pharaoh-tailor-fill). + +See [`skills/pharaoh-tailor-detect/SKILL.md`](../../skills/pharaoh-tailor-detect/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.tailor-fill.agent.md b/.github/agents/pharaoh.tailor-fill.agent.md new file mode 100644 index 0000000..11afcd1 --- /dev/null +++ b/.github/agents/pharaoh.tailor-fill.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when authoring the .pharaoh/project/ tailoring files (id-conventions.yaml, workflows.yaml, artefact-catalog.yaml, checklists/requirement.md) from detected-conventions JSON produced by pharaoh-tailor-detect. +handoffs: [] +--- + +# @pharaoh.tailor-fill + +Use when authoring the .pharaoh/project/ tailoring files (id-conventions.yaml, workflows.yaml, artefact-catalog.yaml, checklists/requirement.md) from detected-conventions JSON produced by pharaoh-tailor-detect. + +See [`skills/pharaoh-tailor-fill/SKILL.md`](../../skills/pharaoh-tailor-fill/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.tailor-review.agent.md b/.github/agents/pharaoh.tailor-review.agent.md new file mode 100644 index 0000000..30a5d4e --- /dev/null +++ b/.github/agents/pharaoh.tailor-review.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when auditing .pharaoh/project/ tailoring files against JSON schemas (id-conventions, workflows, artefact-catalog, checklists frontmatter) plus cross-file consistency checks (every lifecycle state referenced in artefact-catalog exists in workflows.yaml, every prefix in artefact-catalog is declared in id-conventions, etc.). +handoffs: [] +--- + +# @pharaoh.tailor-review + +Use when auditing .pharaoh/project/ tailoring files against JSON schemas (id-conventions, workflows, artefact-catalog, checklists frontmatter) plus cross-file consistency checks (every lifecycle state referenced in artefact-catalog exists in workflows.yaml, every prefix in artefact-catalog is declared in id-conventions, etc.). + +See [`skills/pharaoh-tailor-review/SKILL.md`](../../skills/pharaoh-tailor-review/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.toctree-emit.agent.md b/.github/agents/pharaoh.toctree-emit.agent.md new file mode 100644 index 0000000..1532bdb --- /dev/null +++ b/.github/agents/pharaoh.toctree-emit.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when a composition skill has just emitted a set of RST files into a directory and needs to add (or regenerate) an `index.rst` with a Sphinx toctree over them. Prevents orphan-file warnings under `sphinx-build -W`. Does NOT modify the emitted RST files. Does NOT wire the emitted directory into any parent toctree — that is a caller concern. +handoffs: [] +--- + +# @pharaoh.toctree-emit + +Use when a composition skill has just emitted a set of RST files into a directory and needs to add (or regenerate) an `index.rst` with a Sphinx toctree over them. Prevents orphan-file warnings under `sphinx-build -W`. Does NOT modify the emitted RST files. Does NOT wire the emitted directory into any parent toctree — that is a caller concern. + +See [`skills/pharaoh-toctree-emit/SKILL.md`](../../skills/pharaoh-toctree-emit/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.trace.agent.md b/.github/agents/pharaoh.trace.agent.md new file mode 100644 index 0000000..095c023 --- /dev/null +++ b/.github/agents/pharaoh.trace.agent.md @@ -0,0 +1,107 @@ +--- +description: Use when navigating traceability links between requirements, specifications, implementations, tests, and code in a sphinx-needs project +handoffs: + - label: Analyze Impact + agent: pharaoh.change + prompt: Analyze the impact of changing this requirement + - label: Check Gaps + agent: pharaoh.mece + prompt: Check for traceability gaps in this area +--- + +# @pharaoh.trace + +Navigate traceability in any direction from any need in a sphinx-needs project. Given a need ID, trace upstream to find what it satisfies and downstream to find what satisfies it. Follow all link types: standard `links`, extra_links (`implements`, `tests`, and any project-specific types), and sphinx-codelinks. + +## Data Access + +Use the best available data source in priority order: + +1. **ubc CLI**: `ubc build needs --format json` for complete needs index with links. +2. **ubCode MCP**: Pre-indexed data with link graph already built. +3. **Raw file parsing**: Read `ubproject.toml`/`conf.py` for types and links. Grep for need directives in RST/MD files. Parse `:id:`, `:links:`, and all extra_link options. Build bidirectional link graph. + +Read `pharaoh.toml` for `required_links` (used for gap highlighting). + +## Process + +### Step 1: Get Project Data + +Detect the project and build the needs index and link graph. Present summary: + +``` +Project: <name> (<config source>) +Types: <directive names> +Links: <link type names> +Data source: <tier used> +Needs found: <count> +Codelinks: <enabled|not configured> +``` + +### Step 2: Identify Target Need + +- If the user provides a need ID, look it up. If not found, suggest similar IDs. +- If the user provides a description, search by title/content. Present matches for confirmation. +- Parse optional flags: `--upstream`, `--downstream`, `--depth N`, `--type <type>`. + +### Step 3: Trace Upstream + +Follow incoming links to find parent needs. "Upstream" = toward higher-level, more abstract needs. + +- Check standard `links` (incoming), all extra_link types (incoming direction). +- Use a visited set to detect cycles. Mark cycle nodes as `[CYCLE - already visited]`. +- Stop at top-level needs (no incoming links). + +### Step 4: Trace Downstream + +Follow outgoing links to find child needs. "Downstream" = toward concrete implementations and tests. + +- Check standard `links` (outgoing), all extra_link types (outgoing direction). +- If codelinks enabled, search code files for codelink annotations referencing the need ID. +- Use a visited set. Codelinks are always leaf nodes. + +### Step 5: Present Traceability Tree + +``` +=== Traceability: REQ_001 (Requirement: Brake response time) [open] === + +--- Upstream (satisfies) --- +(no upstream links - top-level requirement) + +--- Downstream (satisfied by) --- +REQ_001 (Requirement: Brake response time) [open] ++-- SPEC_001 (Specification: Brake pedal sensor interface) [open] --links--> +| +-- IMPL_001 (Implementation: Brake pedal driver) [open] --implements--> +| | +-- TEST_001 (Test Case: Brake response time test) [open] --tests--> +| | +-- src/brake_controller.c:brake_check() [codelink] +| +-- [GAP] No test case directly linked (expected: spec -> impl -> test) ++-- REQ_002 (Requirement: Brake force distribution) [open] --links--> + +-- SPEC_002 (Specification: Force distribution algorithm) [open] --links--> +``` + +Tree formatting: +- Each node: `ID (Type: Title) [status]` +- Each edge: `--<link_type>-->` +- Box-drawing characters: `+--`, `|` +- Codelinks: `file:symbol [codelink]` +- Cycles: `ID [CYCLE - already visited]` +- Broken links: `ID [BROKEN LINK - need not found]` + +### Step 6: Highlight Gaps + +Using `required_links` from `pharaoh.toml`, check for missing children of the expected type. Insert `[GAP]` markers in the tree. Provide a gap summary after the tree. + +### Step 7: Multi-project Support + +If multiple project roots exist, search all indexes when a link target is not found locally. Annotate cross-project needs with `[project: <name>]`. + +## Constraints + +1. Handle circular links gracefully with a visited set. Never infinite recurse. +2. Follow ALL configured link types. Do not hardcode names. +3. Always show link type labels on every edge. +4. Show status on every node. +5. Show broken references rather than silently dropping them. +6. This agent is read-only. No session state changes, no file modifications. +7. No workflow gates. Runs freely in any mode. +8. For large projects (>500 needs), suggest `--depth` or `--type` filters. diff --git a/.github/agents/pharaoh.use-case-diagram-draft.agent.md b/.github/agents/pharaoh.use-case-diagram-draft.agent.md new file mode 100644 index 0000000..a32fd11 --- /dev/null +++ b/.github/agents/pharaoh.use-case-diagram-draft.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when drafting one use-case diagram for a single feat — actors (primary, secondary, external systems), use cases (one per user-facing capability), and system boundary. Renderer-aware (mermaid or plantuml per `.pharaoh/project/diagram-conventions.yaml`). First concrete `*-diagram-draft` skill — others follow the same shape. +handoffs: [pharaoh.diagram-review] +--- + +# @pharaoh.use-case-diagram-draft + +Use when drafting one use-case diagram for a single feat — actors (primary, secondary, external systems), use cases (one per user-facing capability), and system boundary. Renderer-aware (mermaid or plantuml per `.pharaoh/project/diagram-conventions.yaml`). First concrete `*-diagram-draft` skill — others follow the same shape. + +See [`skills/pharaoh-use-case-diagram-draft/SKILL.md`](../../skills/pharaoh-use-case-diagram-draft/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.verify.agent.md b/.github/agents/pharaoh.verify.agent.md new file mode 100644 index 0000000..f02b890 --- /dev/null +++ b/.github/agents/pharaoh.verify.agent.md @@ -0,0 +1,19 @@ +--- +description: Use when checking whether one sphinx-needs artefact actually addresses the substance of every parent it links to via :satisfies: or :verifies:. Cross-need content check — distinct from structural MECE, schema-level tailor-review, and per-axis req-review/arch-review. +handoffs: + - label: MECE Check + agent: pharaoh.mece + prompt: Run a structural gap-and-orphan analysis around the verified need + - label: Trace the Need + agent: pharaoh.trace + prompt: Trace the verified need through every link type + - label: Re-author the Need + agent: pharaoh.author + prompt: Revise the body to address the missing parent claims +--- + +# @pharaoh.verify + +Use when checking whether one sphinx-needs artefact actually addresses the substance of every parent it links to via :satisfies: or :verifies:. Cross-need content check — distinct from structural MECE, schema-level tailor-review, and per-axis req-review/arch-review. + +See [`skills/pharaoh-verify/SKILL.md`](../../skills/pharaoh-verify/SKILL.md) for the full atomic specification — inputs, scoring scale, and composition patterns. diff --git a/.github/agents/pharaoh.vplan-draft.agent.md b/.github/agents/pharaoh.vplan-draft.agent.md new file mode 100644 index 0000000..24c2d1f --- /dev/null +++ b/.github/agents/pharaoh.vplan-draft.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when drafting a single sphinx-needs test-case (verification plan item) for one requirement. The artefact type is parameterised via `target_level` (any catalog-declared verification-plan / test-case type — e.g. `tc`, `test`, `vplan`). Emits an RST directive with inputs, steps, and expected outcome, linking to the parent req via `:verifies:`. +handoffs: [] +--- + +# @pharaoh.vplan-draft + +Use when drafting a single sphinx-needs test-case (verification plan item) for one requirement. The artefact type is parameterised via `target_level` (any catalog-declared verification-plan / test-case type — e.g. `tc`, `test`, `vplan`). Emits an RST directive with inputs, steps, and expected outcome, linking to the parent req via `:verifies:`. + +See [`skills/pharaoh-vplan-draft/SKILL.md`](../../skills/pharaoh-vplan-draft/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.vplan-review.agent.md b/.github/agents/pharaoh.vplan-review.agent.md new file mode 100644 index 0000000..e01ddbf --- /dev/null +++ b/.github/agents/pharaoh.vplan-review.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when auditing a single test case against ISO 26262-8 §6 axes plus vplan-specific axes (coverage of parent req, completeness of steps, clarity of expected outcome). Emits structured findings JSON. +handoffs: [] +--- + +# @pharaoh.vplan-review + +Use when auditing a single test case against ISO 26262-8 §6 axes plus vplan-specific axes (coverage of parent req, completeness of steps, clarity of expected outcome). Emits structured findings JSON. + +See [`skills/pharaoh-vplan-review/SKILL.md`](../../skills/pharaoh-vplan-review/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/agents/pharaoh.write-plan.agent.md b/.github/agents/pharaoh.write-plan.agent.md new file mode 100644 index 0000000..a9584a4 --- /dev/null +++ b/.github/agents/pharaoh.write-plan.agent.md @@ -0,0 +1,10 @@ +--- +description: Use when you have an intent (e.g. "reverse-engineer features and reqs from this module") and need a concrete plan.yaml that pharaoh-execute-plan can run. Picks a plan template by intent, fills project-specific values, emits a plan that validates against schema.md. Does NOT execute anything. +handoffs: [] +--- + +# @pharaoh.write-plan + +Use when you have an intent (e.g. "reverse-engineer features and reqs from this module") and need a concrete plan.yaml that pharaoh-execute-plan can run. Picks a plan template by intent, fills project-specific values, emits a plan that validates against schema.md. Does NOT execute anything. + +See [`skills/pharaoh-write-plan/SKILL.md`](../../skills/pharaoh-write-plan/SKILL.md) for the full atomic specification — inputs, outputs, atomicity contract, and composition patterns. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..ff9f389 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,84 @@ +# Pharaoh - AI Assistant for sphinx-needs Projects + +Pharaoh is a skill-based AI assistant framework for sphinx-needs projects. It helps teams author, analyze, trace, and validate requirements using AI. Pharaoh is designed for safety-critical workflows (A-SPICE, ISO 26262) but works with any sphinx-needs project. + +## Core Principles + +- **Static-first data access**: Parse RST/MD source files directly; use ubc CLI or ubCode MCP when available for speed and accuracy. +- **Advisory by default, strict when configured**: `pharaoh.toml` controls enforcement level; no config = advisory mode with guardrails. +- **Safety-critical ready**: Designed for A-SPICE/ISO 26262 workflows but usable by any sphinx-needs team. + +## Available Agents + +| Agent | Purpose | +|-------|---------| +| `@pharaoh.setup` | Scaffold Pharaoh into a project -- detect structure, generate `pharaoh.toml` | +| `@pharaoh.change` | Analyze impact of a change -- trace through needs links and codelinks, produce a Change Document | +| `@pharaoh.trace` | Navigate traceability in any direction -- show everything linked to a need across all levels | +| `@pharaoh.mece` | Gap and redundancy analysis -- find orphans, missing links, MECE violations | +| `@pharaoh.author` | AI-assisted requirement authoring -- create/modify needs with proper IDs, types, and links | +| `@pharaoh.verify` | Validate implementations against requirements -- content-level satisfaction checks | +| `@pharaoh.release` | Release management -- changelog from requirements, traceability coverage metrics | +| `@pharaoh.plan` | Structured implementation planning -- break changes into tasks with workflow enforcement | +| `@pharaoh.spec` | Generate spec from requirements -- read needs hierarchy, record decisions, produce spec with plan table | +| `@pharaoh.decide` | Record design decisions -- create `decision` needs with alternatives, rationale, and traceability links | + +## Recommended Workflow + +``` +@pharaoh.spec -> @pharaoh.decide (for gaps) + -> produces spec doc with plan table + | +@pharaoh.plan -> @pharaoh.change -> @pharaoh.author -> @pharaoh.verify -> @pharaoh.release + -> @pharaoh.mece (optional, for gap analysis) + -> @pharaoh.trace (optional, for exploration) +``` + +## Data Access Tiers + +Agents automatically use the best available data source: + +1. **ubc CLI** (best): Fast, deterministic JSON output. Install from https://ubcode.useblocks.com/ubc/installation.html +2. **ubCode MCP** (VS Code): Real-time indexed data via the ubCode extension. Automatic when the extension is running. +3. **Raw file parsing** (fallback): AI reads RST/MD files directly. Always works, slower on large projects. + +## Configuration + +### Project Configuration + +Agents read need types, link types, and ID settings from: +- `ubproject.toml` (preferred) -- the `[needs]` section +- `conf.py` (fallback) -- `needs_types`, `needs_extra_links`, etc. + +### Pharaoh Configuration (`pharaoh.toml`) + +Optional. Controls Pharaoh's workflow behavior, not sphinx-needs configuration. + +```toml +[pharaoh] +strictness = "advisory" # or "enforcing" + +[pharaoh.workflow] +require_change_analysis = true +require_verification = true +require_mece_on_release = false + +[pharaoh.traceability] +required_links = ["req -> spec", "spec -> impl", "impl -> test"] + +[pharaoh.codelinks] +enabled = true +``` + +### Advisory vs Enforcing Mode + +- **Advisory** (default): Agents suggest the recommended workflow but never block. Tips are shown for skipped steps. +- **Enforcing**: Agents check prerequisites and block if not met (e.g., `@pharaoh.author` requires `@pharaoh.change` first). + +## sphinx-codelinks Integration + +When a project uses sphinx-codelinks, Pharaoh follows codelink references in change analysis and traceability. A change to a requirement surfaces affected code files, not just other requirements. + +## Session State + +Workflow progress is tracked in `.pharaoh/session.json` (ephemeral, gitignored). This enables enforcing mode gates and tracks which needs have been analyzed, authored, and verified. diff --git a/.github/prompts/pharaoh.author.prompt.md b/.github/prompts/pharaoh.author.prompt.md new file mode 100644 index 0000000..1234b39 --- /dev/null +++ b/.github/prompts/pharaoh.author.prompt.md @@ -0,0 +1,23 @@ +--- +agent: pharaoh.author +--- + +# /pharaoh.author + +Author or modify one sphinx-needs artefact — a requirement, an architecture element, a test +case, or a decision — by routing to the right atomic drafting skill based on the project's +artefact catalog. One invocation produces one drafted RST directive with an ID, a parent link, +and a suggested file placement. + +Hand the agent: + +- the **target type** (e.g. `req`, `arch`, `tc`, `decision`), +- a short **draft seed** describing what to author, +- the **parent link** the new artefact will trace to (need-id), and +- any type-specific extras the dispatched drafter needs (e.g. `arch_type`, + `verification_level`). + +The agent picks the matching atomic drafter (`pharaoh-req-draft`, `pharaoh-arch-draft`, +`pharaoh-vplan-draft`, or `pharaoh-decide`), forwards the inputs, and returns the drafted RST +directive plus an authoring summary. Run `@pharaoh.verify` next to check the new artefact +against the substance of its parent. diff --git a/.github/prompts/pharaoh.change.prompt.md b/.github/prompts/pharaoh.change.prompt.md new file mode 100644 index 0000000..7bcd3b1 --- /dev/null +++ b/.github/prompts/pharaoh.change.prompt.md @@ -0,0 +1,3 @@ +--- +agent: pharaoh.change +--- diff --git a/.github/prompts/pharaoh.mece.prompt.md b/.github/prompts/pharaoh.mece.prompt.md new file mode 100644 index 0000000..ebb2eed --- /dev/null +++ b/.github/prompts/pharaoh.mece.prompt.md @@ -0,0 +1,3 @@ +--- +agent: pharaoh.mece +--- diff --git a/.github/prompts/pharaoh.plan.prompt.md b/.github/prompts/pharaoh.plan.prompt.md new file mode 100644 index 0000000..9babb20 --- /dev/null +++ b/.github/prompts/pharaoh.plan.prompt.md @@ -0,0 +1,3 @@ +--- +agent: pharaoh.plan +--- diff --git a/.github/prompts/pharaoh.release.prompt.md b/.github/prompts/pharaoh.release.prompt.md new file mode 100644 index 0000000..ea5d165 --- /dev/null +++ b/.github/prompts/pharaoh.release.prompt.md @@ -0,0 +1,3 @@ +--- +agent: pharaoh.release +--- diff --git a/.github/prompts/pharaoh.trace.prompt.md b/.github/prompts/pharaoh.trace.prompt.md new file mode 100644 index 0000000..250bcd8 --- /dev/null +++ b/.github/prompts/pharaoh.trace.prompt.md @@ -0,0 +1,3 @@ +--- +agent: pharaoh.trace +--- diff --git a/.github/prompts/pharaoh.verify.prompt.md b/.github/prompts/pharaoh.verify.prompt.md new file mode 100644 index 0000000..57d5ee9 --- /dev/null +++ b/.github/prompts/pharaoh.verify.prompt.md @@ -0,0 +1,22 @@ +--- +agent: pharaoh.verify +--- + +# /pharaoh.verify + +Check whether one sphinx-needs artefact actually addresses the substance of every parent it +links to via `:satisfies:` or `:verifies:`. This is a cross-need content check — distinct from +structural MECE (`@pharaoh.mece`), schema-level tailoring review (`@pharaoh.tailor-review`), +and per-axis prose review (`@pharaoh.req-review`, `@pharaoh.arch-review`, +`@pharaoh.vplan-review`). + +Hand the agent: + +- the **need-id** to verify, and +- optionally `transitive: true` to walk the full parent chain rather than just direct parents. + +The agent reads `needs.json`, walks the parent links, scores each (child, parent) pair on a +0-3 ordinal for substantive coverage, and returns a JSON document with per-pair verdicts and +concrete missing aspects. Use the result to decide whether to re-author the body via +`@pharaoh.author`, regenerate it per-axis via `@pharaoh.req-regenerate`, or move on to a +corpus-wide check with `@pharaoh.mece`. diff --git a/.gitignore b/.gitignore index 694344e..06c5758 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,10 @@ bazel-* # Local-only AI agent workflow artifacts (specs, plans, etc.) — never commit. docs/superpowers/ /prompts.md + +# Pharaoh ephemeral state (do not commit). Project tailoring at +# .pharaoh/project/ IS committed and shared with the team. +.pharaoh/runs/ +.pharaoh/plans/ +.pharaoh/session.json +.pharaoh/cache/ diff --git a/.pharaoh/project/artefact-catalog.yaml b/.pharaoh/project/artefact-catalog.yaml new file mode 100644 index 0000000..3071897 --- /dev/null +++ b/.pharaoh/project/artefact-catalog.yaml @@ -0,0 +1,85 @@ +# artefact-catalog.yaml — per-type contract for every declared need type. +# +# Validates against schemas/artefact-catalog.schema.json. +# +# Agile spine + ISO 26262-8 §11 tool-error layer (see +# docs/source/specs/approach.rst): +# story agile user story +# feat feature, :realizes: a story +# err potential tool error, :affects: a feat +# test :prevents: an err (structural prevention; one-line need in tests/) +# check :detects: an err (runtime/CI mitigation; RST) +# restriction :avoids: an err (usage-constraint mitigation; RST) +# impl :links: a feat (code trace; one-line need in src/) +# +# required_links names the outgoing relation every need of that type must +# carry. The *incoming* OR-rule "every err has at least one treatment +# (test/check/restriction)" is not representable here (AND semantics) — it is +# a checklist item and an upstream gap (sphinx-needs#1590). + +story: + required_fields: [id, status, title] + optional_fields: [issue] + lifecycle: [open, in_progress, implemented] + required_links: [] + optional_links: [] + required_metadata_fields: [status] + required_roles: [] + +feat: + required_fields: [id, status, title, source_doc] + optional_fields: [issue] + lifecycle: [open, in_progress, implemented] + required_links: [realizes] + optional_links: [] + required_metadata_fields: [status, source_doc] + required_roles: [] + +err: + required_fields: [id, status, title, source_doc] + optional_fields: [issue, severity] + lifecycle: [open, in_progress, implemented] + required_links: [affects] + optional_links: [] + required_metadata_fields: [status, source_doc, severity] + required_roles: [] + +test: + required_fields: [id, status, title] + optional_fields: [] + lifecycle: [open, in_progress, implemented] + required_links: [verifies] + optional_links: [prevents] + required_metadata_fields: [status] + required_roles: [] + +check: + required_fields: [id, status, title] + optional_fields: [] + lifecycle: [open, in_progress, implemented] + required_links: [detects] + optional_links: [] + required_metadata_fields: [status] + required_roles: [] + +restriction: + required_fields: [id, status, title] + optional_fields: [] + lifecycle: [open, in_progress, implemented] + required_links: [avoids] + optional_links: [] + required_metadata_fields: [status] + required_roles: [] + +# impl needs are authored in src/ as one-line needs and carry a non-empty +# built-in `links` to their feat — guaranteed by the mandatory one-line field +# and checked by the "impl -> feat" chain in pharaoh.toml (kept out of +# required_links here, which lists named extra-link relations). +impl: + required_fields: [id, status, title] + optional_fields: [] + lifecycle: [open, in_progress, implemented] + required_links: [] + optional_links: [] + required_metadata_fields: [status] + required_roles: [] diff --git a/.pharaoh/project/checklists/check.md b/.pharaoh/project/checklists/check.md new file mode 100644 index 0000000..abe9a5b --- /dev/null +++ b/.pharaoh/project/checklists/check.md @@ -0,0 +1,19 @@ +--- +name: Check review checklist +applies_to: check +axes: + - clarity + - correctness + - traceability +--- + +# Check review checklist + +- [ ] ID matches `id_regex` (`^(STORY|FEAT|ERR|TEST|CHECK|REST|IMPL)_[A-Z0-9_]*[0-9]{3}$`). +- [ ] Title states what the check verifies at runtime. +- [ ] Status reflects reality (`in_progress` until the CI job is wired up). +- [ ] `:detects:` names the error(s) this check would catch. +- [ ] Body says where the check runs (CI job / tool) and what signal it + inspects (log lines, output, needs.json …). +- [ ] The check fails the build/pipeline when the error appears — not + advisory-only. diff --git a/.pharaoh/project/checklists/err.md b/.pharaoh/project/checklists/err.md new file mode 100644 index 0000000..ffc9ef5 --- /dev/null +++ b/.pharaoh/project/checklists/err.md @@ -0,0 +1,24 @@ +--- +name: Error review checklist +applies_to: err +axes: + - clarity + - correctness + - traceability +--- + +# Error review checklist + +- [ ] ID matches `id_regex` (`^(STORY|FEAT|ERR|TEST|CHECK|REST|IMPL)_[A-Z0-9_]*[0-9]{3}$`). +- [ ] Title names the observable failure condition (not its cause). +- [ ] Status is one of `open`, `in_progress`, `implemented`. +- [ ] Body explains the condition, when it arises, and the consequence if unguarded. +- [ ] `:affects:` names the feature(s) in which this error can occur. +- [ ] `:severity:` is set (`low` / `medium` / `high`). +- [ ] `:source_doc:` names the source file(s) where the condition arises. +- [ ] **Treated** by at least one of: a `test` that `:prevents:` it + (structural), a `check` that `:detects:` it (runtime/CI), or a + `restriction` that `:avoids:` it (usage constraint). Incoming rule — + reviewed here, not schema-enforced (SN #1590). +- [ ] Treatment choice matches reality: prevention only when the error is + structurally impossible once the test passes; otherwise mitigation. diff --git a/.pharaoh/project/checklists/feat.md b/.pharaoh/project/checklists/feat.md new file mode 100644 index 0000000..896baab --- /dev/null +++ b/.pharaoh/project/checklists/feat.md @@ -0,0 +1,22 @@ +--- +name: Feature review checklist +applies_to: feat +axes: + - clarity + - traceability +--- + +# Feature review checklist + +- [ ] ID matches `id_regex` (`^(STORY|FEAT|ERR|TEST|CHECK|REST|IMPL)_[A-Z0-9_]*[0-9]{3}$`). +- [ ] Title names a single user-observable capability (not an implementation detail). +- [ ] Status is one of `open`, `in_progress`, `implemented`. +- [ ] Body describes the capability in user terms (what, when, for whom). +- [ ] `:realizes:` points at the user story this feature delivers. +- [ ] `:source_doc:` names the implementing source file(s) (forward trace). +- [ ] At least one code-authored `impl` need `:links:` this feature + (reverse trace — advisory; see traceability.rst and SN #1590). +- [ ] At least one `test` `:verifies:` this feature. +- [ ] The errors this feature can exhibit are captured as `err` needs that + `:affects:` it, each with a treatment. +- [ ] No semantic overlap with another feature. diff --git a/.pharaoh/project/checklists/requirement.md b/.pharaoh/project/checklists/requirement.md new file mode 100644 index 0000000..bf42beb --- /dev/null +++ b/.pharaoh/project/checklists/requirement.md @@ -0,0 +1,15 @@ +--- +name: Requirement review checklist (alias) +applies_to: feat +axes: + - clarity + - traceability +--- + +# Requirement review checklist + +This project models the requirement-shaped artefact as the **feature** +(`feat`) on the agile spine (story → feature). Pharaoh skills that reference +`checklists/requirement.md` by its well-known filename are routed here. + +For the actual review criteria see [feat.md](feat.md). diff --git a/.pharaoh/project/checklists/restriction.md b/.pharaoh/project/checklists/restriction.md new file mode 100644 index 0000000..7dafaf5 --- /dev/null +++ b/.pharaoh/project/checklists/restriction.md @@ -0,0 +1,18 @@ +--- +name: Restriction review checklist +applies_to: restriction +axes: + - clarity + - traceability +--- + +# Restriction review checklist + +- [ ] ID matches `id_regex` (`^(STORY|FEAT|ERR|TEST|CHECK|REST|IMPL)_[A-Z0-9_]*[0-9]{3}$`). +- [ ] Title states the constraint as a single rule. +- [ ] Status is one of `open`, `in_progress`, `implemented`. +- [ ] `:avoids:` names the error(s) whose precondition the restriction removes. +- [ ] Body documents the constraint and how it is communicated or enforced + (install docs, CI guard, environment requirement). +- [ ] The restriction removes the error's precondition rather than detecting + it after the fact. diff --git a/.pharaoh/project/checklists/story.md b/.pharaoh/project/checklists/story.md new file mode 100644 index 0000000..c89d03f --- /dev/null +++ b/.pharaoh/project/checklists/story.md @@ -0,0 +1,16 @@ +--- +name: Story review checklist +applies_to: story +axes: + - clarity + - traceability +--- + +# Story review checklist + +- [ ] ID matches `id_regex` (`^(STORY|FEAT|ERR|TEST|CHECK|REST|IMPL)_[A-Z0-9_]*[0-9]{3}$`). +- [ ] Title is a short, capability-level name. +- [ ] Body follows "As a <role>, I want <goal>, so that <benefit>". +- [ ] Status is one of `open`, `in_progress`, `implemented`. +- [ ] At least one `feat` `:realizes:` this story. +- [ ] The story expresses user-facing demand, not an implementation detail. diff --git a/.pharaoh/project/checklists/test.md b/.pharaoh/project/checklists/test.md new file mode 100644 index 0000000..53dd050 --- /dev/null +++ b/.pharaoh/project/checklists/test.md @@ -0,0 +1,23 @@ +--- +name: Test review checklist +applies_to: test +axes: + - clarity + - correctness + - traceability +--- + +# Test review checklist + +- [ ] ID matches `id_regex` (`^(STORY|FEAT|ERR|TEST|CHECK|REST|IMPL)_[A-Z0-9_]*[0-9]{3}$`). +- [ ] Title names what the test exercises. +- [ ] Status is one of `open`, `in_progress`, `implemented`. +- [ ] `:verifies:` names the feature the test exercises (every test verifies a + feature). +- [ ] `:prevents:` (optional) names the error(s) the test structurally rules + out. +- [ ] The one-line need sits in the test file next to the test it represents + (sphinx-codelinks local-url). +- [ ] If it claims `:prevents:`, the test actually fails when the error + condition is introduced — it proves prevention, not just happy-path + behaviour. diff --git a/.pharaoh/project/id-conventions.yaml b/.pharaoh/project/id-conventions.yaml new file mode 100644 index 0000000..ee44130 --- /dev/null +++ b/.pharaoh/project/id-conventions.yaml @@ -0,0 +1,20 @@ +# id-conventions.yaml — declared prefixes and the canonical ID regex. +# +# Validates against schemas/id-conventions.schema.json. +# +# `prefixes` mirrors [[needs.types]] in docs/ubproject.toml. `id_regex` +# matches the same shape declared there: one of the type prefixes, a +# SCREAMING_SNAKE domain token, and a 3-digit ordinal — e.g. +# STORY_MOUNT_001, FEAT_DIRMOUNT_001, ERR_LONGPATH_001, REST_WINPATH_001. + +prefixes: + story: STORY_ + feat: FEAT_ + err: ERR_ + test: TEST_ + check: CHECK_ + restriction: REST_ + impl: IMPL_ + +id_regex: "^(STORY|FEAT|ERR|TEST|CHECK|REST|IMPL)_[A-Z0-9_]*[0-9]{3}$" +separator: "_" diff --git a/.pharaoh/project/workflows.yaml b/.pharaoh/project/workflows.yaml new file mode 100644 index 0000000..093416c --- /dev/null +++ b/.pharaoh/project/workflows.yaml @@ -0,0 +1,30 @@ +# workflows.yaml — Pharaoh project-wide lifecycle state machine. +# +# Validates against the Pharaoh schemas/workflows.schema.json. +# +# Agile lifecycle: a need is identified (open), worked on (in_progress), and +# done (implemented). There is deliberately no terminal "closed"/"deprecated" +# state — an implemented need is finished, and a need that no longer applies +# is deleted from the repository rather than retired in place. The same three +# states are enforced at build time via the status field enum in +# docs/ubproject.toml ([needs.fields.status]). + +lifecycle_states: + - open + - in_progress + - implemented + +transitions: + - from: open + to: in_progress + requires: [] + - from: in_progress + to: implemented + requires: [] + # Rework paths (e.g. an implemented need reopened by a change). + - from: in_progress + to: open + requires: [] + - from: implemented + to: in_progress + requires: [] diff --git a/docs/conf.py b/docs/conf.py index 71bad71..8cdd09a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,8 +17,26 @@ "sphinx_design", "myst_parser", "sphinx.ext.intersphinx", + "sphinxcontrib.mermaid", + # sphinx_needs MUST be loaded before sphinx_codelinks: the codelinks + # setup hook fails fast if ``sphinx_needs`` is not already registered. + "sphinx_needs", + "sphinx_codelinks", ] +# Sphinx-Needs configuration is declared in ubproject.toml. The TOML form +# is consumable by ubCode and other static readers without evaluating +# this file. See https://sphinx-needs.readthedocs.io/en/latest/configuration.html#needs-from-toml +needs_from_toml = "ubproject.toml" + +# -- sphinx-codelinks: code -> need traceability ---------------------------- +# All sphinx-codelinks configuration lives under the [codelinks] table in +# ubproject.toml, so that ubCode and other static readers consume exactly the +# same config without evaluating this file. ubCode currently supports the +# one-line-need form (``@ title, id, [links]``) configured this way. The only +# thing conf.py does is point the extension at that table: +src_trace_config_from_toml = "ubproject.toml" + exclude_patterns: list[str] = [] templates_path = ["source/_static/_templates/furo"] diff --git a/docs/source/index.rst b/docs/source/index.rst index 3751505..f24e4ca 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -55,6 +55,7 @@ Use cases: usage integration bazel + specs/index changelog .. _related-projects: diff --git a/docs/source/specs/approach.rst b/docs/source/specs/approach.rst new file mode 100644 index 0000000..c4dbcf4 --- /dev/null +++ b/docs/source/specs/approach.rst @@ -0,0 +1,116 @@ +Approach +======== + +sphinx-mounts pairs **agile terminology** with the tool-error reasoning of +**ISO 26262-8 §11** ("Confidence in the use of software tools"). The agile +spine captures intent; the tool-error layer captures how the tool is kept +trustworthy. + +Model at a glance +----------------- + +.. mermaid:: + + flowchart LR + subgraph codelinks["Codelinks"] + test["test (TEST_)"] + impl["impl (IMPL_)"] + end + subgraph mitigation["Mitigation"] + check["check (CHECK_)"] + rest["restriction (REST_)"] + end + err["err (ERR_)"] + feat["feat (FEAT_)"] + story["story (STORY_)"] + + test -->|verifies| feat + test -.->|prevents| err + check -->|detects| err + rest -->|avoids| err + err -->|affects| feat + feat -->|realizes| story + impl -->|links| feat + + %% node fills match each need type's declared color in ubproject.toml + style story fill:#E8DAEF,stroke:#333,color:#000 + style feat fill:#BFD8D2,stroke:#333,color:#000 + style err fill:#F5B7B1,stroke:#333,color:#000 + style check fill:#A9DFBF,stroke:#333,color:#000 + style rest fill:#FAD7A0,stroke:#333,color:#000 + style impl fill:#DF744A,stroke:#333,color:#000 + style test fill:#00CED1,stroke:#333,color:#000 + style mitigation fill:#FCFCFC,stroke:#888,stroke-dasharray:5 4,color:#000 + style codelinks fill:#F4F8FB,stroke:#5B9BD5,stroke-dasharray:5 4,color:#000 + +Solid edges are mandatory for that need type; the dotted ``prevents`` edge is +optional — a test always ``:verifies:`` a feature and *may* also +``:prevents:`` an error. The **Mitigation** frame groups the two mitigation +treatments (``check`` and ``restriction``); the **Codelinks** frame groups the +needs authored in code as sphinx-codelinks one-line comments (``test`` in +``tests/``, ``impl`` in ``src/``). + +Agile spine +----------- + +- A **story** (``story``) is a user-facing demand — *"As a … I want … so + that …"*. +- A **feature** (``feat``) ``:realizes:`` a story: a capability the extension + provides. + +Tool-error layer +---------------- + +Every feature can fail in specific ways. Each such failure is a **potential +tool error** (``err``) that ``:affects:`` the feature. In ISO 26262-8 §11 +terms an error contributes to the tool's *impact*; the open question is +whether there is enough confidence that it will not silently corrupt the +output. + +That confidence comes from **treating** every error in one of two ways: + +**Prevention — the error cannot occur.** + A **test** (``test``) — which ``:verifies:`` the feature it exercises — + also ``:prevents:`` the error: it proves, structurally, that the condition + is ruled out (e.g. a guard raises and the test asserts it). A prevented + error cannot reach the output. + +**Mitigation — the error can occur, but is contained.** + For errors that cannot be ruled out structurally, one of: + + - a **check** (``check``) ``:detects:`` the error at runtime — typically a + CI step that inspects the build log or output with another tool and + fails if the error appears; or + - a **restriction** (``restriction``) ``:avoids:`` the error by + constraining how the tool may be used — e.g. *if long mount paths are + required, enable long-path support or do not run on Windows*. + +Mapping to ISO 26262-8 §11 +-------------------------- + +.. list-table:: + :header-rows: 1 + :widths: 20 80 + + * - Concept + - ISO 26262-8 §11 role + * - ``err`` + - Potential tool error (input to Tool Impact, TI) + * - ``test`` + - Prevention measure — the error is eliminated structurally + * - ``check`` + - Tool-error **detection** measure (TD), at runtime / in CI + * - ``restriction`` + - Constraint of use that removes the error's preconditions + +A feature whose every error is prevented or mitigated carries a defensible +tool-confidence argument. :doc:`traceability` renders the full node/link +graph and the per-error treatment tables. + +Code traceability +----------------- + +Orthogonal to the error layer, an **impl** (``impl``) ``:links:`` the feature +it implements. Both ``impl`` and ``test`` are authored as one-line +`sphinx-codelinks`_ needs in the source and test trees, so each need lives +next to the code it represents. diff --git a/docs/source/specs/errors.rst b/docs/source/specs/errors.rst new file mode 100644 index 0000000..cb82c96 --- /dev/null +++ b/docs/source/specs/errors.rst @@ -0,0 +1,75 @@ +Errors +====== + +Each **error** (``err``) is a potential tool error that ``:affects:`` one or +more features. Every error is *treated* by **prevention** (a ``test`` that +proves it cannot occur) or **mitigation** (a ``check`` that detects it, or a +``restriction`` that avoids it) — see :doc:`approach`. ``:severity:`` records +the impact if the error were hit unguarded. + +Prevented by a test +------------------- + +.. err:: Mounted docname collides with an existing document + :id: ERR_COLLISION_001 + :status: implemented + :affects: FEAT_DIRMOUNT_001, FEAT_FILEMOUNT_001 + :severity: high + :source_doc: src/sphinx_mounts/mounter.py + + Two mounts — or a mount and a host document — can produce the same + docname, which would silently shadow content. Registration raises + ``ValueError`` naming both contributors. + +.. err:: File-list entry has an unregistered suffix + :id: ERR_BADSUFFIX_001 + :status: implemented + :affects: FEAT_FILEMOUNT_001 + :severity: medium + :source_doc: src/sphinx_mounts/mounter.py + + In file-list mode a file whose suffix is not one of the project's + ``source_suffix`` values cannot become a docname. The extension raises + ``ValueError`` rather than mounting it silently. + +.. err:: toctree_index out of range for the host document + :id: ERR_TOCIDX_001 + :status: implemented + :affects: FEAT_TOCWIRE_001 + :severity: medium + :source_doc: src/sphinx_mounts/extension.py + + A mount can request a ``toctree_index`` larger than the number of toctrees + present in the host document. The extension raises ``ExtensionError`` + rather than leaving the mount unreferenced. + +Mitigated by a check +-------------------- + +.. err:: A configured mount contributes zero documents + :id: ERR_EMPTYMOUNT_001 + :status: implemented + :affects: FEAT_DIRMOUNT_001 + :severity: medium + :source_doc: src/sphinx_mounts/mounter.py + + A mount can resolve to nothing — a mistyped ``dir``, or a parent + ``.gitignore`` that hides every file — and the build still succeeds, just + without the expected pages. This cannot be ruled out structurally (an + empty directory is legal), so it is detected at runtime rather than + prevented. + +Avoided by a restriction +------------------------ + +.. err:: Mounted path exceeds the Windows MAX_PATH limit + :id: ERR_LONGPATH_001 + :status: implemented + :affects: FEAT_DIRMOUNT_001 + :severity: medium + :source_doc: src/sphinx_mounts/mounter.py + + A deep mounted tree can produce absolute paths longer than the Windows + ``MAX_PATH`` (260-character) limit, which fails the read. The condition is + environmental, not structural, so it is avoided by a usage restriction + rather than prevented in code. diff --git a/docs/source/specs/features.rst b/docs/source/specs/features.rst new file mode 100644 index 0000000..77f24df --- /dev/null +++ b/docs/source/specs/features.rst @@ -0,0 +1,53 @@ +Features +======== + +Each **feature** (``feat``) is a user-observable capability that +``:realizes:`` a user story. ``:source_doc:`` is the forward trace to the +implementing source; an ``impl`` one-line need in that source carries the +reverse trace (see :doc:`traceability`). The errors each feature can exhibit +are in :doc:`errors`. + +.. feat:: Directory mounting + :id: FEAT_DIRMOUNT_001 + :status: implemented + :realizes: STORY_MOUNT_001 + :source_doc: src/sphinx_mounts/mounter.py + + sphinx-mounts mounts an external directory into the host Sphinx project. + Every file under the directory whose extension matches the project's + configured ``source_suffix`` is registered as a document at the + ``mount_at`` prefix, with its path relative to the directory (minus the + matched suffix) as the docname tail. Sources are read in place — never + copied or symlinked. + +.. feat:: File-list mounting + :id: FEAT_FILEMOUNT_001 + :status: implemented + :realizes: STORY_MOUNT_001 + :source_doc: src/sphinx_mounts/mounter.py + + sphinx-mounts mounts an explicit list of individual files. Each file's + basename (minus the matched suffix) becomes a docname under ``mount_at``, + forming a flat namespace; subdirectories in the file paths are ignored. + +.. feat:: Declarative TOML configuration + :id: FEAT_TOMLCONF_001 + :status: implemented + :realizes: STORY_DECLARE_001 + :source_doc: src/sphinx_mounts/config.py + + Mount entries can be declared in ``ubproject.toml`` as a top-level + ``[[mounts]]`` array, so non-Python tooling (ubCode, IDEs, CI gates) can + read the configuration without evaluating ``conf.py``. The TOML list + replaces any ``mounts`` value set in ``conf.py``. + +.. feat:: Automatic toctree wiring + :id: FEAT_TOCWIRE_001 + :status: implemented + :realizes: STORY_MOUNT_001 + :source_doc: src/sphinx_mounts/extension.py + + A mount can request that its entry document be wired into a host + document's toctree via ``attach_to``. sphinx-mounts appends the entry to + the selected toctree (``toctree_index``), or creates a new toctree + beneath the first section when the host document has none. diff --git a/docs/source/specs/index.rst b/docs/source/specs/index.rst new file mode 100644 index 0000000..e249093 --- /dev/null +++ b/docs/source/specs/index.rst @@ -0,0 +1,19 @@ +.. _specs: + +Specifications & traceability +============================= + +A lightweight, agile traceability model for sphinx-mounts authored with +`Sphinx-Needs`_, combined with the tool-error reasoning of ISO 26262-8 §11. +Start with :doc:`approach` for the method; :doc:`traceability` renders the +full node/link graph. + +.. toctree:: + :maxdepth: 2 + + approach + stories + features + errors + mitigations + traceability diff --git a/docs/source/specs/mitigations.rst b/docs/source/specs/mitigations.rst new file mode 100644 index 0000000..de33928 --- /dev/null +++ b/docs/source/specs/mitigations.rst @@ -0,0 +1,39 @@ +Mitigations +=========== + +For errors that cannot be ruled out structurally by a test, a **check** +detects them at runtime or a **restriction** avoids them by constraining how +the tool is used. (Prevention via tests lives next to the tests — see +:doc:`traceability`.) + +Checks +------ + +A **check** (``check``) ``:detects:`` an error: an external, runtime +verification — usually a CI step — that fails if the error appears. + +.. check:: CI asserts every mount contributed documents + :id: CHECK_MOUNTCOUNT_001 + :status: in_progress + :detects: ERR_EMPTYMOUNT_001 + + A CI step parses the build's ``needs.json`` / output and fails if any + configured mount contributed zero documents, catching a mount that + silently resolved to nothing (wrong path, parent ``.gitignore``). + +Restrictions +------------ + +A **restriction** (``restriction``) ``:avoids:`` an error by removing its +precondition — a documented constraint on how the tool may be used. + +.. restriction:: Enable long-path support, or avoid Windows, for deep mounts + :id: REST_WINPATH_001 + :status: implemented + :avoids: ERR_LONGPATH_001 + + When mounted document paths can exceed the Windows ``MAX_PATH`` + (260-character) limit, the project must either enable long-path support + (``LongPathsEnabled``) or run the build on a platform without the limit. + This removes the precondition for the error instead of detecting it after + the fact. diff --git a/docs/source/specs/stories.rst b/docs/source/specs/stories.rst new file mode 100644 index 0000000..6fa1ff2 --- /dev/null +++ b/docs/source/specs/stories.rst @@ -0,0 +1,22 @@ +User stories +============ + +The agile demand side: what users want from sphinx-mounts. Each feature +``:realizes:`` one of these stories. + +.. story:: Mount external sources in place + :id: STORY_MOUNT_001 + :status: implemented + + As a documentation author, I want to include external source trees in my + Sphinx build without copying or symlinking them, so that generated or + sibling documentation renders in the same site and stays in sync with its + source. + +.. story:: Declare mounts once for the whole toolchain + :id: STORY_DECLARE_001 + :status: implemented + + As a user of the useblocks documentation stack, I want mounts declared in + ``ubproject.toml``, so that ubCode, IDEs, and CI read the same mount + configuration without executing ``conf.py``. diff --git a/docs/source/specs/traceability.rst b/docs/source/specs/traceability.rst new file mode 100644 index 0000000..23f9afa --- /dev/null +++ b/docs/source/specs/traceability.rst @@ -0,0 +1,72 @@ +Traceability +============ + +The needs form a closed loop of *outgoing* links (each schema-validatable), +authored in three places: RST (``story``, ``feat``, ``err``, ``check``, +``restriction``), the package source (``impl``), and the test suite +(``test``). The :doc:`approach` page carries the node/link diagram and +explains what each link means; this page renders the live tables and source +traces. + +Agile spine +----------- + +.. needtable:: Features and the story each realizes + :types: feat + :columns: id, title, realizes, source_doc + :style: table + +Errors and their treatment +-------------------------- + +.. needtable:: Errors and the feature they affect + :types: err + :columns: id, title, severity, affects + :style: table + +.. needtable:: Treatments — prevention (test) and mitigation (check / restriction) + :types: test, check, restriction + :columns: id, type, title, prevents, detects, avoids, status + :style: table + +Code & test traceability (sphinx-codelinks) +------------------------------------------- + +``impl`` and ``test`` needs are authored as one-line `sphinx-codelinks`_ needs +in comments next to the code, discovered at build time (configured under the +``[codelinks]`` table in ``ubproject.toml``, which ubCode reads too). One +``src-trace`` directive per project renders them. + +Implementation needs (``src/``) — each ``:links:`` the feature it implements: + +.. src-trace:: + :project: sphinx_mounts + +.. needtable:: Implementation needs and the feature each links to + :types: impl + :columns: id, title, links, status + :style: table + +Test needs (``tests/``) — each ``:verifies:`` a feature and may also +``:prevents:`` an error: + +.. src-trace:: + :project: sphinx_mounts_tests + +.. needtable:: Test needs — feature verified and any error prevented + :types: test + :columns: id, title, verifies, prevents, status + :style: table + +.. note:: Backlink coverage is not yet schema-validatable (sphinx-needs #1590) + + Every edge above is an *outgoing* link, which IS schema-validatable. What + we cannot yet express is the *incoming* rule — e.g. "every ``feat`` must + have at least one ``impl`` linking to it" or "every ``err`` must have at + least one treatment (``test`` / ``check`` / ``restriction``)." sphinx-needs + schema validation currently constrains a need's own fields and its + **outgoing** links only, not its **incoming** links / backlinks. + + Enforcing that via schema validation would be the canonical, declarative + approach. It is tracked upstream at + `sphinx-needs#1590 <https://github.com/useblocks/sphinx-needs/issues/1590>`__. diff --git a/docs/ubproject.toml b/docs/ubproject.toml index 835107b..f3944a2 100644 --- a/docs/ubproject.toml +++ b/docs/ubproject.toml @@ -2,11 +2,8 @@ # importantly the ubCode language server (https://ubcode.useblocks.com/), # but also any other static reader (IDE plugins, indexers, CI gates) # that wants to understand this project's documentation setup without -# evaluating ``conf.py``. -# -# This project does *not* use sphinx-needs, but the file is still useful: -# it tells ubCode which schema to validate against and how to behave -# while editing. +# evaluating ``conf.py``. The TOML form also drives sphinx-needs itself +# via ``needs_from_toml = "ubproject.toml"`` set in ``conf.py``. "$schema" = "https://ubcode.useblocks.com/ubproject.schema.json" # Make the language server reindex the project automatically whenever a @@ -14,3 +11,231 @@ # the latest edits without having to trigger a manual rebuild. [server] index_on_save = true + +# --------------------------------------------------------------------------- +# Sphinx-Needs configuration — agile terminology, ISO 26262-8 §11 reasoning. +# +# An agile spine (story -> feature) carries the tool's intent; an ISO 26262 +# tool-error layer hangs off each feature and records how every potential +# tool error is prevented or mitigated. See docs/source/specs/approach.rst. +# +# story — agile user story (the demand). +# feat — a feature; ``:realizes:`` a story. +# err — a potential *tool error* of a feature; ``:affects:`` a feat. +# test — a test case that ``:verifies:`` a feat (it exercises the +# feature) and may also ``:prevents:`` an err (proving the +# error cannot occur structurally). Authored as a one-line +# sphinx-codelinks need in tests/. +# check — a runtime / CI check that ``:detects:`` an err (e.g. greps +# the build log via another tool to confirm it did not occur). +# restriction — a usage restriction that ``:avoids:`` an err (e.g. "enable +# long-path support, or do not run on Windows"). +# impl — implementation marker; ``:links:`` the feat it implements. +# Authored as a one-line sphinx-codelinks need in src/. +# +# An error is *treated* by EITHER a test (prevention — error cannot occur) OR +# a mitigation for errors that can occur: a check (runtime detection) or a +# restriction (usage constraint). test / check / restriction are the ISO +# 26262-8 §11 error prevention/detection measures behind the tool-confidence +# argument. +# +# Lifecycle is ``open -> in_progress -> implemented`` (see +# ``.pharaoh/project/workflows.yaml``). There is no terminal "closed": an +# implemented need is done; a need that no longer applies is deleted. +# +# Every link is *outgoing* (schema-validatable). The *incoming* rules +# ("every feat has an impl", "every err has a treatment") are not yet +# expressible in sphinx-needs schema validation — +# https://github.com/useblocks/sphinx-needs/issues/1590. +# --------------------------------------------------------------------------- +[needs] +build_json = true + +# Force the author to set an ID by hand. A title-derived auto-ID would +# break every inbound link the moment someone reworded the title. +id_required = true + +# Allowed ID shape: one of the declared type prefixes, a SCREAMING_SNAKE +# domain token, and a 3-digit ordinal — e.g. ``FEAT_DIRMOUNT_001``, +# ``ERR_LONGPATH_001``, ``REST_WINPATH_001``. Kept in sync with +# ``.pharaoh/project/id-conventions.yaml``. +id_regex = "(STORY|FEAT|ERR|TEST|CHECK|REST|IMPL)_[A-Z0-9_]*[0-9]{3}" + +# Allowed lifecycle states, enforced via the built-in ``status`` field's +# schema enum. (The older ``[[needs.statuses]]`` table is deprecated in +# sphinx-needs 8.x.) A ``:status:`` outside this set becomes a +# schema-validation finding — and an error under ``sphinx-build -W``. +[needs.fields.status] +description = "Lifecycle state: open -> in_progress -> implemented." +schema = { type = "string", enum = ["open", "in_progress", "implemented"] } + +# --- Custom fields (sphinx-needs 8.x [needs.fields.<name>] shape) --------- +[needs.fields.source_doc] +description = "Path(s) under the repo root of the source file(s) relevant to this need (comma-separated). Forward need -> code trace." +schema = { type = "string" } +nullable = true +default = "" + +[needs.fields.issue] +description = "GitHub issue number tracking the underlying work, if any." +schema = { type = "string" } +nullable = true +default = "" + +[needs.fields.severity] +description = "For errors: impact if the condition is hit unguarded — one of low / medium / high." +schema = { type = "string" } +nullable = true +default = "" + +# --- Link types (sphinx-needs 8.x [needs.links.<name>] shape) ------------- +[needs.links.realizes] +description = "feat -> story: the feature realizes (delivers) the user story." +incoming = "realized by" +outgoing = "realizes" + +[needs.links.affects] +description = "err -> feat: the error is a potential failure of the feature." +incoming = "can fail with" +outgoing = "affects" + +[needs.links.verifies] +description = "test -> feat: the test exercises and verifies the feature." +incoming = "verified by" +outgoing = "verifies" + +[needs.links.prevents] +description = "test -> err: the test also proves an error cannot occur structurally (prevention)." +incoming = "prevented by" +outgoing = "prevents" + +[needs.links.detects] +description = "check -> err: a runtime / CI check detects whether the error occurred (mitigation)." +incoming = "detected by" +outgoing = "detects" + +[needs.links.avoids] +description = "restriction -> err: the restriction constrains usage so the error cannot arise (mitigation)." +incoming = "avoided by" +outgoing = "avoids" + +# --- Need types ------------------------------------------------------------ +[[needs.types]] +directive = "story" +title = "Story" +prefix = "STORY_" +color = "#E8DAEF" +style = "node" + +[[needs.types]] +directive = "feat" +title = "Feature" +prefix = "FEAT_" +color = "#BFD8D2" +style = "node" + +[[needs.types]] +directive = "err" +title = "Error" +prefix = "ERR_" +color = "#F5B7B1" +style = "node" + +[[needs.types]] +directive = "check" +title = "Check" +prefix = "CHECK_" +color = "#A9DFBF" +style = "node" + +[[needs.types]] +directive = "restriction" +title = "Restriction" +prefix = "REST_" +color = "#FAD7A0" +style = "node" + +# Authored in source code as sphinx-codelinks one-line needs (see [codelinks] +# below), not via an RST directive — but still first-class need types. +[[needs.types]] +directive = "impl" +title = "Implementation" +prefix = "IMPL_" +color = "#DF744A" +style = "node" + +[[needs.types]] +directive = "test" +title = "Test" +prefix = "TEST_" +color = "#00CED1" +style = "node" + +# --------------------------------------------------------------------------- +# sphinx-codelinks configuration. +# +# Read by sphinx-codelinks AND ubCode because ``conf.py`` sets +# ``src_trace_config_from_toml = "ubproject.toml"``. ubCode supports the +# one-line-need form configured this way. Two projects are scanned: +# +# sphinx_mounts — src/, one-line ``impl`` needs: +# # @ <title>, <IMPL_id>, [<feat-id>, ...] +# each ``:links:`` the feature(s) it implements. +# sphinx_mounts_tests — tests/, one-line ``test`` needs: +# # @ <title>, <TEST_id>, [<feat-id>], [<err-id>] +# each ``:verifies:`` the feature it exercises and +# may also ``:prevents:`` an error. +# +# In both, ``type`` and ``status`` come from field defaults, so they are never +# written in the comment — the author writes only the title, id, and links. +# --------------------------------------------------------------------------- +[codelinks] +# Copy each marked source file into the build and cross-link the marked line +# to the need it creates (and back), so the HTML carries source <-> need both +# ways. +set_local_url = true + +[codelinks.projects.sphinx_mounts.source_discover] +src_dir = "../src/sphinx_mounts" # relative to confdir (the docs/ directory) +comment_type = "python" +gitignore = true + +[codelinks.projects.sphinx_mounts.analyse] +get_oneline_needs = true +get_need_id_refs = false + +# One-line field order. Parsing is POSITIONAL and defaults fill only TRAILING +# omitted fields, so the two defaulted fields (type, status) must come last — +# which lets authors write just ``@ title, IMPL_id, [feat-ids]``. The first +# three (title, id, links) have no default and are therefore mandatory. +[codelinks.projects.sphinx_mounts.analyse.oneline_comment_style] +needs_fields = [ + { name = "title" }, + { name = "id" }, + { name = "links", type = "list[str]" }, + { name = "type", default = "impl" }, + { name = "status", default = "implemented" }, +] + +# Second project: the test suite. A test ``verifies`` the feature it exercises +# (mandatory) and may also ``prevents`` the error(s) it structurally rules out +# (optional — write just the feature when there is no error to prevent). +[codelinks.projects.sphinx_mounts_tests.source_discover] +src_dir = "../tests" # relative to confdir (the docs/ directory) +comment_type = "python" +gitignore = true + +[codelinks.projects.sphinx_mounts_tests.analyse] +get_oneline_needs = true +get_need_id_refs = false + +[codelinks.projects.sphinx_mounts_tests.analyse.oneline_comment_style] +needs_fields = [ + { name = "title" }, + { name = "id" }, + { name = "verifies", type = "list[str]" }, + { name = "prevents", type = "list[str]", default = [ + ] }, + { name = "type", default = "test" }, + { name = "status", default = "implemented" }, +] diff --git a/pharaoh.toml b/pharaoh.toml new file mode 100644 index 0000000..668aaef --- /dev/null +++ b/pharaoh.toml @@ -0,0 +1,48 @@ +# Pharaoh project configuration. +# +# Pharaoh wraps a sphinx-needs project with authoring, review, and audit +# skills. This file controls *Pharaoh's* behaviour only — need types, link +# options, custom fields, statuses, and id_regex live in +# ``docs/ubproject.toml`` and are not duplicated here. + +[pharaoh] +strictness = "advisory" + +[pharaoh.id_scheme] +# IDs are <TYPE-PREFIX>_<DOMAIN>_<NNN>, e.g. STORY_MOUNT_001, FEAT_DIRMOUNT_001, +# ERR_LONGPATH_001, REST_WINPATH_001. The authoritative, machine-checkable +# regex lives in .pharaoh/project/id-conventions.yaml; this pattern is the +# descriptive hint used by pharaoh-id-allocate. +pattern = "{PREFIX}{DOMAIN}_{NUMBER}" +auto_increment = true + +[pharaoh.workflow] +mode = "reverse-eng" +# Reverse-engineering an existing codebase: needs are authored from code that +# already exists. Verification matters from day one; change-analysis and the +# MECE release gate stay off until the catalogue stabilises. +require_change_analysis = false +require_verification = true +require_mece_on_release = false + +[pharaoh.traceability] +# "source-type -> target-type": every need of source-type must carry at least +# one outgoing link that reaches a target-type need. +required_links = [ + "feat -> story", # every feature realizes a user story + "err -> feat", # every error affects a feature + "impl -> feat", # every impl links the feature it implements + "test -> feat", # every test verifies the feature it exercises + "check -> err", # every check detects an error + "restriction -> err", # every restriction avoids an error +] +# NOT a hard chain: "every err has at least one treatment (test / check / +# restriction)". That is an *incoming* OR-rule on err, which required_links +# (outgoing, AND semantics) cannot express — enforced via the err review +# checklist and tracked upstream (sphinx-needs#1590). + +[pharaoh.codelinks] +# sphinx-codelinks is loaded by docs/conf.py and configured under the +# [codelinks] table in docs/ubproject.toml. Two one-line-need projects: +# impl needs in src/ (:links: a feat) and test needs in tests/ (:prevents: an err). +enabled = true diff --git a/pyproject.toml b/pyproject.toml index 8e493a8..0023838 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,14 @@ testing = [ # project loads `myst_parser`. "myst-parser>=4.0.0", ] -docs = ["furo>=2024.5.6", "myst-parser>=4.0.0", "sphinx-design>=0.6.1"] +docs = [ + "furo>=2024.5.6", + "myst-parser>=4.0.0", + "sphinx-codelinks>=1.2.0", + "sphinx-design>=0.6.1", + "sphinx-needs>=8.1", + "sphinxcontrib-mermaid>=2.0.2", +] ty = ["ty>=0.0.37"] ruff = ["ruff>=0.8.0"] diff --git a/src/sphinx_mounts/config.py b/src/sphinx_mounts/config.py index 23f20e5..a4854bd 100644 --- a/src/sphinx_mounts/config.py +++ b/src/sphinx_mounts/config.py @@ -460,6 +460,7 @@ def load_mounts_from_toml(toml_path: Path) -> list[dict[str, Any]] | None: :raises TomlConfigError: If the file exists but is not valid TOML, or if the top-level ``mounts`` key has the wrong shape. """ + # @ TOML mount loading and path anchoring, IMPL_TOMLCONF_001, [FEAT_TOMLCONF_001] if not toml_path.is_file(): return None try: diff --git a/src/sphinx_mounts/extension.py b/src/sphinx_mounts/extension.py index 98ce8eb..112c1ae 100644 --- a/src/sphinx_mounts/extension.py +++ b/src/sphinx_mounts/extension.py @@ -105,6 +105,7 @@ def _on_doctree_read(app: Sphinx, doctree: nodes.document) -> None: :class:`ExtensionError` — silent misconfiguration would leave the mount unreferenced. """ + # @ Toctree injection on doctree-read, IMPL_TOCWIRE_001, [FEAT_TOCWIRE_001] parsed: tuple[MountConfig, ...] = getattr(app, _CACHED_KEY, ()) if not parsed: return diff --git a/src/sphinx_mounts/mounter.py b/src/sphinx_mounts/mounter.py index 8738fa5..08ebb88 100644 --- a/src/sphinx_mounts/mounter.py +++ b/src/sphinx_mounts/mounter.py @@ -144,6 +144,7 @@ def _attach_mount_dir( directory whose parent gitignores it (the canonical ``bazel-bin/...`` case) would silently produce zero files. """ + # @ Directory walk and docname registration, IMPL_DIRMOUNT_001, [FEAT_DIRMOUNT_001] added: set[str] = set() suffixes = tuple(project.source_suffix) @@ -229,6 +230,7 @@ def _build_walker( def _attach_mount_files( project: Project, mount: MountConfig, files: Iterable[Path] ) -> set[str]: + # @ File-list docname registration, IMPL_FILEMOUNT_001, [FEAT_FILEMOUNT_001] added: set[str] = set() suffixes = tuple(project.source_suffix) diff --git a/tests/test_mounting.py b/tests/test_mounting.py index 9e8b4b1..07b0d3c 100644 --- a/tests/test_mounting.py +++ b/tests/test_mounting.py @@ -47,6 +47,7 @@ def _build(make_app, host_dir: Path) -> Path: # ---------- TOML-driven tests (primary path) ---------- +# @ Directory mount discovery, TEST_DIRMOUNT_001, [FEAT_DIRMOUNT_001] def test_basic_mount_makes_external_files_readable( make_app, make_host_project, bundle_simple ): @@ -649,6 +650,7 @@ def test_mount_single_file_via_files(make_app, make_host_project, bundle_simple) assert not (outdir / "_generated/api/index.html").exists() +# @ File-list mounting, TEST_FILEMOUNT_001, [FEAT_FILEMOUNT_001] def test_mount_multiple_files_via_files(make_app, make_host_project, bundle_simple): """A `files = [...]` mount with multiple entries registers all of them flat under ``mount_at`` (basename → docname tail).""" @@ -706,6 +708,7 @@ def test_mount_files_with_attach_to_wires_entry_doc( assert "_generated/api/intro.html" in index_html +# @ Suffix guard, TEST_BADSUFFIX_001, [FEAT_FILEMOUNT_001], [ERR_BADSUFFIX_001] def test_mount_files_unknown_suffix_raises(make_app, make_host_project, tmp_path): """A listed file whose extension is not in source_suffix is an error — the user explicitly asked for it to be mounted, so silently skipping @@ -773,6 +776,7 @@ def test_exclude_filter_bundle_files(make_app, make_host_project, bundle_simple) assert not (outdir / "_generated/api-foo/details.html").exists() +# @ Collision guard, TEST_COLLISION_001, [FEAT_DIRMOUNT_001], [ERR_COLLISION_001] def test_docname_conflict_raises(make_app, make_host_project, bundle_simple, tmp_path): """Two mounts producing the same docname must be rejected.""" host = make_host_project() @@ -910,6 +914,7 @@ def test_toml_overrides_conf_py_mounts( assert not (outdir / "_generated/from-py/index.html").exists() +# @ TOML path anchoring, TEST_TOMLCONF_001, [FEAT_TOMLCONF_001] def test_toml_in_subdir_anchors_paths_to_toml_directory( make_app, make_host_project, bundle_simple ): @@ -1015,6 +1020,7 @@ def test_mounts_from_toml_disabled_with_none( # ---------- attach_to: toctree integration ---------- +# @ Toctree wiring, TEST_TOCWIRE_001, [FEAT_TOCWIRE_001] def test_attach_to_extends_existing_toctree(make_app, make_host_project, bundle_simple): """A mount with attach_to extends the host doc's existing toctree.""" host = make_host_project() @@ -1083,6 +1089,7 @@ def test_attach_to_targets_specific_toctree_by_index( ) +# @ Toctree index guard, TEST_TOCIDX_001, [FEAT_TOCWIRE_001], [ERR_TOCIDX_001] def test_attach_to_index_out_of_range_raises( make_app, make_host_project, bundle_simple ): diff --git a/uv.lock b/uv.lock index 73c1585..a0a860f 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,10 @@ version = 1 revision = 3 requires-python = ">=3.12, <4" +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version < '3.13'", +] [[package]] name = "accessible-pygments" @@ -23,6 +27,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, ] +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + [[package]] name = "babel" version = "2.18.0" @@ -127,6 +149,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, ] +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, +] + [[package]] name = "colorama" version = "0.4.6" @@ -136,6 +170,33 @@ 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 = "comment-parser" +version = "1.2.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.13'", +] +dependencies = [ + { name = "python-magic", version = "0.4.24", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/cd/6e4c79d4a25e3b6542d1a3728b3e2b2c4650e3f8c3cde950aaecc6f64764/comment_parser-1.2.4.tar.gz", hash = "sha256:4e03c421c07d146f5a4d248423820347f3589f60d17acea55f7fb5683faffa22", size = 8350, upload-time = "2022-01-26T04:50:40.828Z" } + +[[package]] +name = "comment-parser" +version = "1.2.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", +] +dependencies = [ + { name = "python-magic", version = "0.4.27", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/37/6a/354d8e640b5f90996ac07c002189a552226a0ddaad85efd14863166aaa14/comment_parser-1.2.5.tar.gz", hash = "sha256:5606b769228cafce03d538e361472896581b386f3bc44bd62f4b61ff45ff05ec", size = 8852, upload-time = "2024-12-25T05:08:14.618Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/53/12ceba92ce55851a3d34123b2774a32689f745ce4c0c9bebca1a2a07e266/comment_parser-1.2.5-py3-none-any.whl", hash = "sha256:fb3dee8f351ebd1cfc22d3f19dabf93a4f7d4c9ea13b2092242de086a4449d9e", size = 13222, upload-time = "2026-03-16T03:30:00.77Z" }, +] + [[package]] name = "coverage" version = "7.14.0" @@ -222,11 +283,11 @@ wheels = [ [[package]] name = "docutils" -version = "0.22.4" +version = "0.21.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, + { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, ] [[package]] @@ -245,6 +306,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/b2/50e9b292b5cac13e9e81272c7171301abc753a60460d21505b606e15cf21/furo-2025.12.19-py3-none-any.whl", hash = "sha256:bb0ead5309f9500130665a26bee87693c41ce4dbdff864dbfb6b0dae4673d24f", size = 339262, upload-time = "2025-12-19T17:34:38.905Z" }, ] +[[package]] +name = "gitignore-parser" +version = "0.1.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/51/e391a1a4238f18d0abb47be479b07af265ad4519022cf51b7da47ef82487/gitignore_parser-0.1.13.tar.gz", hash = "sha256:c7e10c8190accb8ae57fb3711889e73a9c0dbc04d4222b91ace8a4bf64d2f746", size = 5603, upload-time = "2025-08-25T06:33:22.704Z" } + +[[package]] +name = "giturlparse" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/35/7f25a604a406be7d7d0f849bfcbc1603df084e9e58fe6170980c231138e4/giturlparse-0.14.0.tar.gz", hash = "sha256:0a13208cb3f60e067ee3d09d28e01f9c936065986004fa2d5cd6db7758e9f6e6", size = 15637, upload-time = "2025-10-22T09:21:11.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/f9/9ff5a301459f804a885f237453ba81564bc6ee54740e9f2676c2642043f6/giturlparse-0.14.0-py2.py3-none-any.whl", hash = "sha256:04fd9c262ca9a4db86043d2ef32b2b90bfcbcdefc4f6a260fd9402127880931d", size = 16299, upload-time = "2025-10-22T09:21:10.818Z" }, +] + [[package]] name = "idna" version = "3.15" @@ -351,6 +427,48 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, +] + +[[package]] +name = "jsonschema-rs" +version = "0.37.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e2/52/06a843d1c21d11ae22fc15abc5cb7e3b83c8d5aa7e6056c009e7189f4a58/jsonschema_rs-0.37.4.tar.gz", hash = "sha256:67f36f1c445c70f9975d17a84ce37f79593f6234d7eb292830d7749e5fa58ff4", size = 1790550, upload-time = "2025-11-30T21:00:23.52Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/01/fc8a5338167c45cdaf8b2ebc79e1c404c881b9cb1a0f69312acc6d38ec59/jsonschema_rs-0.37.4-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:5975e448092e99d6cc60793a71f0fee516dbf0fd1e6d2f6f1e4689627268f344", size = 4448311, upload-time = "2025-11-30T21:00:05.694Z" }, + { url = "https://files.pythonhosted.org/packages/48/17/073c55453ec65644957acfe9b435ab45e4a8208faba43fec7ceb0f92607c/jsonschema_rs-0.37.4-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:1d3f8c8b376966c19fd4183fa979dbadc9fdd6070f2bfa4d127bdf70946963cc", size = 2277865, upload-time = "2025-11-30T21:00:07.915Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3e/584247d45ad6fe020d040f892316d7100affb51d7bb2b8b31dde9633b353/jsonschema_rs-0.37.4-cp310-abi3-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:393ece7037a0d19fd528f7a67a32749453876468871a0bd2267909a57d8d4e32", size = 2453442, upload-time = "2025-11-30T21:00:09.402Z" }, + { url = "https://files.pythonhosted.org/packages/ff/85/b3f795037abf89087f62a7e49baca82e34b4f0d576ac30a740ca61cd33ea/jsonschema_rs-0.37.4-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dedf72e5e673e3af5b9925979fd71484debada61fb7a3dfabf9bbc74b8012664", size = 2334092, upload-time = "2025-11-30T21:00:10.705Z" }, + { url = "https://files.pythonhosted.org/packages/17/e9/644c293e0169425e0a321be197266162d5df2718af18014444edfb185eb9/jsonschema_rs-0.37.4-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e159075b1846718466998d5a9294c661113b347b8b4749767680a97c8ed2bf4d", size = 2418813, upload-time = "2025-11-30T21:00:12.056Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4b/e65a64839d8c8f55352ff12ccf37035cfbc8f38a383ef57ba0621007fc1b/jsonschema_rs-0.37.4-cp310-abi3-win32.whl", hash = "sha256:0f17a61deb557faa57dffb9596e4f022873404f935114367788b1eebdec2bb00", size = 2007260, upload-time = "2025-11-30T21:00:13.919Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d8/d2f94cca643bd4bd70f43762c981e566b9afe16c367a2f05fe97d7c8200c/jsonschema_rs-0.37.4-cp310-abi3-win_amd64.whl", hash = "sha256:03b34f911e99343fc388651688683010daee538a3cf8cf86a7997bca28fdf16b", size = 2226699, upload-time = "2025-11-30T21:00:15.763Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + [[package]] name = "markdown-it-py" version = "4.2.0" @@ -447,6 +565,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] +[[package]] +name = "minijinja" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/10/43/ebdf02703cafe163402ce09f2c4fd2eaf7f6f667a0f056b9bb1ac893f7f7/minijinja-2.20.0.tar.gz", hash = "sha256:93364945c36a2b44d8b62af068ff0c11c8b3f3fa2b11599190249bd4cd2c31ad", size = 292230, upload-time = "2026-05-20T00:01:47.084Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/93/a2dff85e5cc4a2c62fb00c814c6f380830e733cb5b42d5cbfeeb92a3acda/minijinja-2.20.0-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:9693c7bc574d881d24e3e64c63f7ba90d9e3dbc64a13cb9d730585f63b7fb058", size = 2118981, upload-time = "2026-05-20T00:01:26.185Z" }, + { url = "https://files.pythonhosted.org/packages/1d/9a/4a5f57d1fc0d2c58c2e327f2c02cdeeaa550631c901eebed3b6c477f8de2/minijinja-2.20.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d1de5065a0cd0158623a35f30d138ef9fc98156dd5f550527f06ba22f4ab70f", size = 1105692, upload-time = "2026-05-20T00:01:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e3/b7d03968426748db76823703f5460cc22a8286d58ce834dd967ae2b75cb3/minijinja-2.20.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:698bbc1ece5dad03aa5d322d7af52e54b907febe9971731b59384addf2783c8a", size = 1079016, upload-time = "2026-05-20T00:01:30.014Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b3/0b14377d8a9171f823aa51640dd23e8256641577b9e01d3a4496aecb5c2c/minijinja-2.20.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:203ab639b0f3b71d6af6910da6311348b3a4f1a84e6702ab7c1c1f392293d16c", size = 1141684, upload-time = "2026-05-20T00:01:31.61Z" }, + { url = "https://files.pythonhosted.org/packages/3a/34/4b786ec1f99ac470e53098e7888d5efe673230975e76d1e87fce9a91ff90/minijinja-2.20.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a60b4396cd68ef02a6a5470d086f0ae8ae0ecdfd16a24457897f6abd5e5cede4", size = 1268541, upload-time = "2026-05-20T00:01:33.197Z" }, + { url = "https://files.pythonhosted.org/packages/3b/69/fb95c87b560487d87ec38f871ae5b184b1a20bc11bf0b0f6054895b0b508/minijinja-2.20.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a28d60be22a2f76accf00d84274450aa1264911fa8f45c79e4120bfddf8471cc", size = 1282035, upload-time = "2026-05-20T00:01:35.002Z" }, + { url = "https://files.pythonhosted.org/packages/b0/d4/7b934a43a144345cd898ae4c70c4cc35783ca685cc81bbf0f6f1d26b4b85/minijinja-2.20.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:cb8d3a1e30666b883eb2ba2b3619620c62504be474e85ecc490cb28334210fd6", size = 1353836, upload-time = "2026-05-20T00:01:36.964Z" }, + { url = "https://files.pythonhosted.org/packages/16/3f/498458cec8b299a11b44b7812d564ae377b8d51e3d4308925fd203c2ac92/minijinja-2.20.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:daa32983bc995b53a16616a4cb65f9d3d5e4996fe68b61cda37909161cac4674", size = 1434449, upload-time = "2026-05-20T00:01:38.86Z" }, + { url = "https://files.pythonhosted.org/packages/b2/1d/339ee68f10f69be825395aeb3e543153300dc80bc3a7181b7334821ed4f1/minijinja-2.20.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:494b8ec3307ab4d69ea74fc9fed927f2bbd38c5636feab16bdb4ef2fc3891aa0", size = 1362165, upload-time = "2026-05-20T00:01:40.705Z" }, + { url = "https://files.pythonhosted.org/packages/cd/79/4e4c4559e1a77a1509fe2b03c4346b6b2571e2786fe8b566e407f6a4bf24/minijinja-2.20.0-cp38-abi3-win32.whl", hash = "sha256:1a2348e52708d58be056c7c6ddfab0bf008d80221e2264d0e1c1d007ac6903ba", size = 1034517, upload-time = "2026-05-20T00:01:42.57Z" }, + { url = "https://files.pythonhosted.org/packages/86/9e/fc1d88c3a373c9d49e31c8220bc2e8fa919405538b848bdf681f378100cd/minijinja-2.20.0-cp38-abi3-win_amd64.whl", hash = "sha256:18be801f16cfa0026cb932df32077a169e3737d5808f7d9a2ca19afcea217140", size = 1095692, upload-time = "2026-05-20T00:01:44.908Z" }, +] + [[package]] name = "myst-parser" version = "5.1.0" @@ -545,6 +682,30 @@ 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-magic" +version = "0.4.24" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.13'", +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/70/76b185393fecf78f81c12f9dc7b1df814df785f6acb545fc92b016e75a7e/python-magic-0.4.24.tar.gz", hash = "sha256:de800df9fb50f8ec5974761054a708af6e4246b03b4bdaee993f948947b0ebcf", size = 17295, upload-time = "2021-06-03T13:49:24.116Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/99/c89223c6547df268596899334ee77b3051f606077317023617b1c43162fb/python_magic-0.4.24-py2.py3-none-any.whl", hash = "sha256:4fec8ee805fea30c07afccd1592c0f17977089895bdfaae5fec870a84e997626", size = 12785, upload-time = "2021-06-03T13:49:22.828Z" }, +] + +[[package]] +name = "python-magic" +version = "0.4.27" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", +] +sdist = { url = "https://files.pythonhosted.org/packages/da/db/0b3e28ac047452d079d375ec6798bf76a036a08182dbb39ed38116a49130/python-magic-0.4.27.tar.gz", hash = "sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b", size = 14677, upload-time = "2022-06-07T20:16:59.508Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/73/9f872cb81fc5c3bb48f7227872c28975f998f3e7c2b1c16e95e6432bbb90/python_magic-0.4.27-py2.py3-none-any.whl", hash = "sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3", size = 13840, upload-time = "2022-06-07T20:16:57.763Z" }, +] + [[package]] name = "pyyaml" version = "6.0.3" @@ -591,6 +752,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, +] + [[package]] name = "requests" version = "2.34.2" @@ -606,6 +781,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, ] +[[package]] +name = "requests-file" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/97/bf44e6c6bd8ddbb99943baf7ba8b1a8485bcd2fe0e55e5708d7fee4ff1ae/requests_file-2.1.0.tar.gz", hash = "sha256:0f549a3f3b0699415ac04d167e9cb39bccfb730cb832b4d20be3d9867356e658", size = 6891, upload-time = "2024-05-21T16:28:00.24Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/25/dd878a121fcfdf38f52850f11c512e13ec87c2ea72385933818e5b6c15ce/requests_file-2.1.0-py2.py3-none-any.whl", hash = "sha256:cf270de5a4c5874e84599fc5778303d496c10ae5e870bfa378818f35d21bda5c", size = 4244, upload-time = "2024-05-21T16:27:57.733Z" }, +] + +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + [[package]] name = "roman-numerals" version = "4.1.0" @@ -615,6 +815,128 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/54/6f679c435d28e0a568d8e8a7c0a93a09010818634c3c3907fc98d8983770/roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7", size = 7676, upload-time = "2025-12-17T18:25:33.098Z" }, ] +[[package]] +name = "roman-numerals-py" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "roman-numerals" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/b5/de96fca640f4f656eb79bbee0e79aeec52e3e0e359f8a3e6a0d366378b64/roman_numerals_py-4.1.0.tar.gz", hash = "sha256:f5d7b2b4ca52dd855ef7ab8eb3590f428c0b1ea480736ce32b01fef2a5f8daf9", size = 4274, upload-time = "2025-12-17T18:25:41.153Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/2c/daca29684cbe9fd4bc711f8246da3c10adca1ccc4d24436b17572eb2590e/roman_numerals_py-4.1.0-py3-none-any.whl", hash = "sha256:553114c1167141c1283a51743759723ecd05604a1b6b507225e91dc1a6df0780", size = 4547, upload-time = "2025-12-17T18:25:40.136Z" }, +] + +[[package]] +name = "rpds-py" +version = "2026.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/43/25a8dcd3feedd735039a8f0b5b7e3b118232b5eae288c4fd9ab200d41094/rpds_py-2026.5.1.tar.gz", hash = "sha256:07b24fea40541e28570e5b795a4a38fbdcd12550c06bd0748005ecc8116ca256", size = 64459, upload-time = "2026-05-28T12:02:13.232Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/e7/a78582dc57caa592dcc7d4fb69b61390561e908eb3d2f5df5928a8e354c0/rpds_py-2026.5.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3abe24a66e57adcfa645d718063a5fa5103ecc71ddbf26d78af8f9368018ff1d", size = 353040, upload-time = "2026-05-28T11:59:12.531Z" }, + { url = "https://files.pythonhosted.org/packages/a3/43/35e3f136343aef451e545ce8c38d36c2f93c0ed88703db8b64ba2b205c68/rpds_py-2026.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b1d94308ddf0b1982f61f2eb54bf92997c9ece8a8093ef014250f4a517906c", size = 345775, upload-time = "2026-05-28T11:59:13.827Z" }, + { url = "https://files.pythonhosted.org/packages/20/e1/0f2160c5982d3157734d5cb3ed63d8b2d583a73c9864f77b666449f32cf8/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fa92420128dadce7f54bd73ba1825a273e9268fe9e35dbf7e6362890efa4e08", size = 376329, upload-time = "2026-05-28T11:59:15.271Z" }, + { url = "https://files.pythonhosted.org/packages/d0/11/ee0ba42aff83bf4effdbc576673c6be64c5e173978c3f6d537e94482f77d/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ca653c6546386227cd9800d1bef6a348099acf8db4250341da6d90f663d6dfcb", size = 383539, upload-time = "2026-05-28T11:59:16.665Z" }, + { url = "https://files.pythonhosted.org/packages/11/df/d94aa6a499d4ac40afe2d7620f2c597fd3c0f182e854ad7cf3f596a81cb6/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66c93681c4729e4e3ecba31b8179fae083ff3118841672835140338b4b9867c1", size = 494674, upload-time = "2026-05-28T11:59:17.991Z" }, + { url = "https://files.pythonhosted.org/packages/1f/75/33d30f43bb2f458de11979486a591b1bf6e5651765ed1704c6197c2dc773/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40ff257542e04796880e011e15cd4dc21c2599975df2aaa8f2c8495ca574e1a5", size = 389268, upload-time = "2026-05-28T11:59:19.434Z" }, + { url = "https://files.pythonhosted.org/packages/f4/1e/2c9096fc19d5fd084b0184ca2b651e659aa0a37e6fdbecf6ece47f147fe1/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6825cc329b290e93c5f6a9be2393118a763f6ccf6abd83704e0c102ca583644", size = 376280, upload-time = "2026-05-28T11:59:21Z" }, + { url = "https://files.pythonhosted.org/packages/b9/e5/61ec9f8be8211ea7f48448195549e4aaf02004083475493b0e137702ecb2/rpds_py-2026.5.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:de42116e69cb53b911cc34aee5ab98f36c597b822545045d49e938818b99e5e4", size = 387233, upload-time = "2026-05-28T11:59:22.454Z" }, + { url = "https://files.pythonhosted.org/packages/0d/ca/bcec1005c4f4a234f92a29078631fee49206c7265ccae966f18fd332e80e/rpds_py-2026.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0f920015df2a504bebaba6d4c31ccf3fcf942f92655c086da30b671aad19aa6", size = 405009, upload-time = "2026-05-28T11:59:23.845Z" }, + { url = "https://files.pythonhosted.org/packages/72/e6/4d5718c5cf26c522dc7c9999e238da1e77380b81d0c5d1df11e271ddfeb1/rpds_py-2026.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0408a24e44feb919423dc6d9da677cb5cddb894d2ca9e763967d156d9c60fab4", size = 553113, upload-time = "2026-05-28T11:59:25.184Z" }, + { url = "https://files.pythonhosted.org/packages/d4/25/2ee807bdb3e1f0b7eddf7782acd5665a8b5205a331a7d7244a52c4812fd9/rpds_py-2026.5.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cea68bcd53467561ae2f96a6bdad1544299ba97b5b0ddcd5ac3d376e5c781c24", size = 618838, upload-time = "2026-05-28T11:59:26.749Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c1/7d4c26f167f8c41501cc073d30ee22082b16ce358cf5b00ec97cbc7804ea/rpds_py-2026.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4be8b1d2a705cc37d08256004e1d07de143fa0075c8e85a3df020b776f62b732", size = 582436, upload-time = "2026-05-28T11:59:28.11Z" }, + { url = "https://files.pythonhosted.org/packages/04/1d/9d12b0a337bab46f4769f8857f4007e3b2d639e14f9a44a0efe157696e64/rpds_py-2026.5.1-cp312-cp312-win32.whl", hash = "sha256:6736718bd4fc49cbcb538ba30516fdbef161522acefb739657d48b97bd864fed", size = 212734, upload-time = "2026-05-28T11:59:29.689Z" }, + { url = "https://files.pythonhosted.org/packages/c5/93/e4116f2de7f56bc7406a76033dc501811ddeb22b7f056b92d632871ebb0c/rpds_py-2026.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:0a7d1eec967df0e9b22614a5e177622e0c89611d03727fa0cb48e45028907870", size = 229045, upload-time = "2026-05-28T11:59:31.033Z" }, + { url = "https://files.pythonhosted.org/packages/cb/53/6c3419d85eb2ec5938a37627c585b42d76a63bb731d6e42ed4b079ebf486/rpds_py-2026.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:1841d067089e117142d79b98aa0df2f08b52f2ecc1819dd2700636c0db74a473", size = 223967, upload-time = "2026-05-28T11:59:32.318Z" }, + { url = "https://files.pythonhosted.org/packages/6c/32/14c961ad295f490eb0849ada8b79683e93a59b9de3afdd983eaf55fa6867/rpds_py-2026.5.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:efef4ac29c6ff495531eb17ee705b62841ecaa291b7c7077e848ea03e237164d", size = 352787, upload-time = "2026-05-28T11:59:33.655Z" }, + { url = "https://files.pythonhosted.org/packages/ca/bb/d1b85117967c11191441a7274ae616c65d93901d082c588f89a50a8da5ae/rpds_py-2026.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c39f5b67a8a2e67179ada2a954227d670fe65fa9098457f698f56ddf248709b3", size = 345179, upload-time = "2026-05-28T11:59:35Z" }, + { url = "https://files.pythonhosted.org/packages/7c/46/d84105f062e626a1b233f863907288a4708c2d833b8b4c6fb2764bc080c0/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5c30f3f04eef4fbd362226a6f31d7c8895ca4fbb6e0b790f6890a98d8da8559", size = 376173, upload-time = "2026-05-28T11:59:36.43Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ae/469d7959ce5b1201e1de135dc735b86db3b35dd0d1734f6a44246d5f061c/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:277f6c82f0580848796c7ecc8a7173aa3bfb928e4ff831261c2f60a81dc270db", size = 383162, upload-time = "2026-05-28T11:59:37.995Z" }, + { url = "https://files.pythonhosted.org/packages/dc/a2/57853d31a1116a561aa072794602ad3f6341e18d70a8523f1bd5b9fc1e5a/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63c2c4c213f1a4e3f3de28ecab029dbdee976324e729c0d7a55211be72576b02", size = 495093, upload-time = "2026-05-28T11:59:39.453Z" }, + { url = "https://files.pythonhosted.org/packages/99/63/3a8eabcad9314b7daf5c65f451d2c33d989235cd8a5762186cf2c3f5a4f8/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3350ec808fb538fe71a1f94dfaa0e29c598dfad805ce49f0caec5ae3183c652b", size = 389829, upload-time = "2026-05-28T11:59:40.896Z" }, + { url = "https://files.pythonhosted.org/packages/4b/25/05678d97fc25e2622df14dc530fb82023174ecfff6733991ed0d78f167bd/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1b964e3ab599e718dc46c018d104b1ebc007cbc6567d827c94a687fca56d77e", size = 374786, upload-time = "2026-05-28T11:59:42.626Z" }, + { url = "https://files.pythonhosted.org/packages/88/d1/8c90b6431e80a3b91b284a5c7c8c0c4f9c006444d90477a740d6e0f9c694/rpds_py-2026.5.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:19cb09fab7b7fc96b2a6e28f2e34b72a3705ff27b37edb77455316e5d3f3dc9b", size = 386920, upload-time = "2026-05-28T11:59:44.124Z" }, + { url = "https://files.pythonhosted.org/packages/ff/99/4638f672ab356682d633ee0da9255f5b67ce6efd0b85eb94ad3e255e65a5/rpds_py-2026.5.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abe76bcdba31e576cb83eeb8797aa0d882b738fef6dc65d0601fc753806a5b46", size = 405059, upload-time = "2026-05-28T11:59:47.177Z" }, + { url = "https://files.pythonhosted.org/packages/66/3f/3546524b6eb4cc2e1f363a3d638fa52f6c24faae3500c25fb488b02f1740/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8bff7073db3899158fff55ebf57b113a67030af26f80a18978f9f0aa60250ddf", size = 553030, upload-time = "2026-05-28T11:59:48.603Z" }, + { url = "https://files.pythonhosted.org/packages/c6/c3/7b3388c796fcf471bd17194242d4dc1a7608567c0fa422bcc1c5e79f9c1e/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8ba264fa49be666cd9cc56bf34ec7002fb3d27a4aee5bcb4d43d0d18feb1bb6f", size = 618975, upload-time = "2026-05-28T11:59:50.314Z" }, + { url = "https://files.pythonhosted.org/packages/61/1e/a3cb07f2795075d1d88efddae2f541359fde5f08c81ee114c29c2949c90a/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4860b603ddda0475a8885499b3729e90229d480105b42651962a5397d995fa89", size = 581178, upload-time = "2026-05-28T11:59:51.673Z" }, + { url = "https://files.pythonhosted.org/packages/a1/74/e758c03a5ef46f04c37f2651a2893db846d569ba8a7bca469d4b58939bcd/rpds_py-2026.5.1-cp313-cp313-win32.whl", hash = "sha256:7944270ae71383f6e2657dd7d5ce4eeb4ac2d0059a6738f0510583d462ab4842", size = 212481, upload-time = "2026-05-28T11:59:53.148Z" }, + { url = "https://files.pythonhosted.org/packages/70/ec/a2aca432db9c7359b40fa393eeeaa0d166c2f70175be956e75fa24197c44/rpds_py-2026.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:88647f43a73c4e01be19b04ceef0c8d3a1958153604d13c773becd8016f2a0cf", size = 228519, upload-time = "2026-05-28T11:59:54.505Z" }, + { url = "https://files.pythonhosted.org/packages/29/60/a73bfdd45b096574556acf303bbd9fa9eed36ca8a818b514e2a5d5fe2b9d/rpds_py-2026.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:453895624ecf7db7063b1004e44037522bbaef9ff6a945e59bc71662d7a03abd", size = 223446, upload-time = "2026-05-28T11:59:56.081Z" }, + { url = "https://files.pythonhosted.org/packages/18/e2/408105fd611823f00882aea810f3989a30d26b1bab8b6beb20f98c724e0e/rpds_py-2026.5.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:b4e4bc98639ec915f512fde3aa7a95e0041d95d9c3cc86eea841fa63cb1e8600", size = 355287, upload-time = "2026-05-28T11:59:57.448Z" }, + { url = "https://files.pythonhosted.org/packages/8d/58/5c4a43436843c90d0f6d19f82c200c80e3843ca9fa07b237623327f6d384/rpds_py-2026.5.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cacedb7a6e167680acba45ad5716e89067d225dc80da0d7040cae8c81d4572fa", size = 347033, upload-time = "2026-05-28T11:59:58.881Z" }, + { url = "https://files.pythonhosted.org/packages/fb/c2/1a71acdacaf4e259b10278fb87b039ded3cf80041bcd89dd8a3ea702ded6/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68700371c5d7ae1412862ddfa719090925c93ecf351c566d66f09d04b136ea00", size = 376891, upload-time = "2026-05-28T12:00:00.516Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c8/535f3d9b65addd8e28aa87b83c6e526799c3717a88273db8ea795beeef7a/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:296c799becfa849c779c8725494fe9ed94959ed886787df4364b058465bad7f0", size = 385646, upload-time = "2026-05-28T12:00:02.394Z" }, + { url = "https://files.pythonhosted.org/packages/1c/91/dc033f313345c354ade914dbe73cdb90b615a4409ea02430d5356794f3d8/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3858b908218ee108d0bbfb2095ccc237648053c9bf98affad7cb079acaf1d97", size = 498830, upload-time = "2026-05-28T12:00:04.189Z" }, + { url = "https://files.pythonhosted.org/packages/27/fc/90fcbea459dbb8ddc18a2e0fd1de9412b48bc84ffff2db771cf714bacfd6/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4fb8d2e7cb2f850b169806d61d1b991738acec96500a75c30f49caf064ce7cef", size = 392830, upload-time = "2026-05-28T12:00:05.797Z" }, + { url = "https://files.pythonhosted.org/packages/b2/1d/46cd11a228c9750684a798d98f878be6f614aa762438da7378f035e79e35/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27b74c10ed6a8f190f4287f53bcfea348b92a84a9c9f70d30183d1e6172d580d", size = 379613, upload-time = "2026-05-28T12:00:07.433Z" }, + { url = "https://files.pythonhosted.org/packages/24/4a/d9b0c6af3a1de03eb93741bbe8be2bdce84d8fda8224f3005451d86df389/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:b9a6528956191c48c52294a592dbd4a8386d7048bdb25c0efcb6b966466c6d83", size = 388183, upload-time = "2026-05-28T12:00:09.227Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b4/db7aaabdda6d020afc87d981bcc2f57a434c7dec60ecfc2ab3dd50b20351/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:af03e34e860047bc7a352b842856fcf78798fbb81132cc98bd2f907ab4eb9cd2", size = 408578, upload-time = "2026-05-28T12:00:10.779Z" }, + { url = "https://files.pythonhosted.org/packages/08/d6/070f6a41cbb343e2ac4171859bf3f3623e0ab002f72619d6d505313ec2de/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fea6e836d10abbe191d557d33bd58bd5987725fe63aa1eefe557d230209855bd", size = 553573, upload-time = "2026-05-28T12:00:12.443Z" }, + { url = "https://files.pythonhosted.org/packages/75/ab/1a71ea3589c4345dac0a0518f0e6a031cb42689277851b683c46d27463a5/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:fc0c0f878ea770a0a8a462456c5ad36fc9fe6358e6b76fdadc7f17575e0b8bf1", size = 620861, upload-time = "2026-05-28T12:00:14.09Z" }, + { url = "https://files.pythonhosted.org/packages/8a/22/9bf80a56069c0c443fcfefac639a86a744550a2898817a6dfd3e26654924/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e0b360f316d966b048b085857630b3cc51f3db2f07b06f440eac8f695374d1e3", size = 585633, upload-time = "2026-05-28T12:00:15.66Z" }, + { url = "https://files.pythonhosted.org/packages/da/68/3b2c0a75c9e04125696f84ebdbbf304acf5a40b58ba4481cdb98a922c3ba/rpds_py-2026.5.1-cp313-cp313t-win32.whl", hash = "sha256:a2999883eedf72fdfb7520b92c7d4ec2572a71ff40239377aa604cc529eecafc", size = 210074, upload-time = "2026-05-28T12:00:17.291Z" }, + { url = "https://files.pythonhosted.org/packages/e7/8b/609157d5a25d37d4f29f92840ba531f416907c34ae5c5739dd21fc2bef98/rpds_py-2026.5.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e07be2a9d7122bd6e82dea89814ef8dc893feb1aae97fec1630f3263bbb30e55", size = 228635, upload-time = "2026-05-28T12:00:18.73Z" }, + { url = "https://files.pythonhosted.org/packages/d4/6f/19c1918a4b590d8de87e712e4abe4b3875771eff60216fb6153cf6665c68/rpds_py-2026.5.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:1f2c391c3059798093b65df23aca2cac150460ae9c630d99dec83d703d9485b9", size = 349756, upload-time = "2026-05-28T12:00:20.217Z" }, + { url = "https://files.pythonhosted.org/packages/e5/60/a06fe7da34eca79dacbf958a2ba0c6eea85bc2b29de20080bf40f72f66fa/rpds_py-2026.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:413b424f7c4ee65ab5e5be91f5731be0f8b41a1ee2b12dfe810d716312e95a78", size = 343831, upload-time = "2026-05-28T12:00:21.711Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ec/b2333b97b90e2a6ef6ca8ad386ee284968e74bcfe113b3f1a8d9036429a9/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c595a1d9255dce0599e13130d1440ab2506654f2b50294226ee06402f8fef63", size = 375127, upload-time = "2026-05-28T12:00:23.326Z" }, + { url = "https://files.pythonhosted.org/packages/14/7f/e00aae54067f2b488c4637961d5f58204d470795fc791085fa3f15060d2e/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c27c5f6102eac8c03e7595a00827a53b271ba40a53b59ff8709170e0855ea4a", size = 379034, upload-time = "2026-05-28T12:00:24.89Z" }, + { url = "https://files.pythonhosted.org/packages/be/cc/423999bbb8ae8dc93c77fc1d5e984ade5eb89d237d3bb884ccfa72ae2890/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c7fcf61d44cacecaf3aea542b0e053db77972a4573e7ceda16fb2b399161195", size = 490823, upload-time = "2026-05-28T12:00:26.676Z" }, + { url = "https://files.pythonhosted.org/packages/0f/aa/c671bf660f12e68d3c52ff86c7066ed1372df5a0f4f2ff584e419b8207e7/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c817a189d4ee14290420e5ff051e4dd6baa13f3edf84685071dee07a6d538ee", size = 388144, upload-time = "2026-05-28T12:00:28.577Z" }, + { url = "https://files.pythonhosted.org/packages/19/c8/d63bb75b68afe77b229e3021c6031bcaf01da5db5b0e69d0d10f9ba679a7/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21846aac0ed2e0589f38c12dc44e77bb64e494b771eadbcf169cba00566ba7ba", size = 371959, upload-time = "2026-05-28T12:00:30.304Z" }, + { url = "https://files.pythonhosted.org/packages/82/35/c51122014d8274ff37dc606d60049c3db7d83da02b5b282511e5a906a9a6/rpds_py-2026.5.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b317c87a13f769a4e787819bd508aaa5d69aa09b0880de9af6d3a8a54571cdec", size = 383558, upload-time = "2026-05-28T12:00:31.764Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f9/2790cb99c136a5363acdeacf5c27c56f3de0d4118a1f48fca83404c99c89/rpds_py-2026.5.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce87129d9f2c14fa6c4a8601fb80eb4488c80d38a20cd13758ef11123e14995d", size = 402789, upload-time = "2026-05-28T12:00:33.247Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1b/e4fb584f8c75d35c38150ff6a332cda949e6f97acba1f4fd123b14ab56fe/rpds_py-2026.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9cdddb6c1207d284d94fd1530adf57fbd797fe7c4b8704ba85f49414f2557e7d", size = 551405, upload-time = "2026-05-28T12:00:34.819Z" }, + { url = "https://files.pythonhosted.org/packages/d8/f7/a6731b4216cb3793ea1af5391da240f5683dacc0d13e034fe5fc3503f240/rpds_py-2026.5.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:4e237e139f94d3c036fd28eb9f564c99055476ff4ff05cd42be55ce349b5aa02", size = 616975, upload-time = "2026-05-28T12:00:36.268Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/2e051a81d95d8e63f4b35a1c463a87e8766bc3d083c067c5dfb6bf220747/rpds_py-2026.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ed0954b524873214369184a9c82b0eaa45a3fbb9a798cd95b17e0d98499e7ea0", size = 578701, upload-time = "2026-05-28T12:00:37.82Z" }, + { url = "https://files.pythonhosted.org/packages/65/56/b5f6fdb2083e32bca8a8993d89e70db114b4756c9e2c38421328126689d2/rpds_py-2026.5.1-cp314-cp314-win32.whl", hash = "sha256:2d88621d6a7d4dfa633d21abe90f280bb205274e16b1d1e61c6ad4640b2453b7", size = 209806, upload-time = "2026-05-28T12:00:39.492Z" }, + { url = "https://files.pythonhosted.org/packages/fb/80/65a5aa96c155e611d1ed844e4e1f57f3e36b021f396d9f8585d756e6b90d/rpds_py-2026.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:cef8ac28d26f4dda3533060c20fbf80a325458fa9fd23ea72a73cdfa8e978838", size = 225985, upload-time = "2026-05-28T12:00:40.94Z" }, + { url = "https://files.pythonhosted.org/packages/27/7c/ad185212e87b05f196daef92bc5f3caf07298eb47c295b5585c3dd3093ac/rpds_py-2026.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:eaaea962c68cdc68d4a533ba985ab8e9484277910bbfaa2ab3ef7732667bfed8", size = 221219, upload-time = "2026-05-28T12:00:43.15Z" }, + { url = "https://files.pythonhosted.org/packages/23/58/e14ae18759020334646b031e708ab4158d653a938822bfb7b95ef2e93aa3/rpds_py-2026.5.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:21942f52dbbd5f8758bf021213d28bd45c39e873e65e2407faf5f1846f5761ad", size = 352148, upload-time = "2026-05-28T12:00:44.638Z" }, + { url = "https://files.pythonhosted.org/packages/31/9b/5f4a1e2f960bca3ac5d052b139dd31eed97b259f9d909173821760d542e8/rpds_py-2026.5.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f414556f6e3958300ff941e40c9f97e3dc9774ddd1b3434c475d73dd354bbed3", size = 345196, upload-time = "2026-05-28T12:00:46.14Z" }, + { url = "https://files.pythonhosted.org/packages/1a/71/1d9574d6a2fa20ab60eaa55c7467f5aa20cbc770f341a05f09c0876f59e2/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1013a8625c74043210190b246f5b1551e09757c1f356c6e4160ef96c5bc081", size = 374981, upload-time = "2026-05-28T12:00:47.531Z" }, + { url = "https://files.pythonhosted.org/packages/0c/9a/37e99f4915a80aa71670263c1267f7ae0af95f53a3f61e6c3bdc016d4515/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cc68e231a77a5f0d774ae278a1f8e55c0456501820847c1e4efb3829f3441df6", size = 379961, upload-time = "2026-05-28T12:00:49.216Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ff/6e73f74b89d2e0715e0fc86b7dde893f9a61ae2f9b256ff3bdfe41ac4e94/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9baffb505aff33acc69b422a19f77806680f3c8632227d79f48de8a810d1c2c5", size = 495965, upload-time = "2026-05-28T12:00:51.111Z" }, + { url = "https://files.pythonhosted.org/packages/ea/e0/425faba25f59d74d4638b267f7c7a80e8649d2ef4db10a19b0c4a71e6e6f/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8d2f912928d426e8cfa396f7f3f8d29a59e6689c86dcca3c420730c1096322b", size = 389526, upload-time = "2026-05-28T12:00:52.77Z" }, + { url = "https://files.pythonhosted.org/packages/c6/76/7a41960e3fddae47fab43a28684d5da981401dffd88253de0944148654cb/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90f628283be835db980c941767d41c9a27b5239e54ba0a9c1335247e82406964", size = 376190, upload-time = "2026-05-28T12:00:54.215Z" }, + { url = "https://files.pythonhosted.org/packages/27/60/5f38dc70824fc6951b51d35377e577a3a3a4c81a6769cc5a2de25ebe0ad1/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:1ebb2f0ab7e16132995a72de805170e0203df0c3dd22e1ef1cd1fdd90bd7a131", size = 383921, upload-time = "2026-05-28T12:00:55.673Z" }, + { url = "https://files.pythonhosted.org/packages/60/1a/d60a38caa1505f4b9483c3fbbde12c94e1079154f4f401a6da96f7e77621/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f3df3d16ded76f1f8c9cdebd0e1ea55fdf4c23b812de189814da7cf229c22a81", size = 404766, upload-time = "2026-05-28T12:00:57.518Z" }, + { url = "https://files.pythonhosted.org/packages/87/ff/602fd3f174d6425f0bce05ad0dfbec0e96b38d0f7d08a79af5aa20083885/rpds_py-2026.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9af8905b8f854990e40d5206aa5ac58d9b0fe0b7f351ff2bb086c20f6c8c6a47", size = 551343, upload-time = "2026-05-28T12:00:58.978Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c1/1be13327acdbead3eca1fde03b6a34dbb011f1e864e217f0d32cc1779a7f/rpds_py-2026.5.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:036a36a87fb1cd3b214d11c4b3c4f7d2ddad933625dca1c900b56a057c07740a", size = 618502, upload-time = "2026-05-28T12:01:00.656Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d7/afb49b49d7f2be8b7ba1a9f0977fa5168003437b93086726f066544e8351/rpds_py-2026.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:62ae3853454fe9ef283a03c96c2d835d39e84b14643a9d62c82ef0fb87d702ca", size = 581916, upload-time = "2026-05-28T12:01:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/25/d1/dbef8c1f8a10f07beb62b5f054e20099fd9924b3ec001b8f0b6ac7813a85/rpds_py-2026.5.1-cp314-cp314t-win32.whl", hash = "sha256:6c3d771a46ec18b12af06ce36243a9a80b07a5d0515236332d90863ca8bb326a", size = 207855, upload-time = "2026-05-28T12:01:03.821Z" }, + { url = "https://files.pythonhosted.org/packages/2a/72/bfa4e61ab8e7dc1c8adf397e05e6cbdd4239357bd72b248d3de662f23915/rpds_py-2026.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c93c629be4636cf54337bd5f06c104d55e42ced54d681f6fe21ae510a65116f6", size = 225422, upload-time = "2026-05-28T12:01:05.194Z" }, + { url = "https://files.pythonhosted.org/packages/27/3a/7b5da92b640f67b6717ccafc83cdd06bfa7ff2395c3685c68922bb54d703/rpds_py-2026.5.1-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:3574b55c604b8f75dacb007136508bbc0db406e626301778096a133327e7f2fb", size = 349576, upload-time = "2026-05-28T12:01:06.722Z" }, + { url = "https://files.pythonhosted.org/packages/d7/8a/2aafd7ad355a1bd48ca76e2262b74b15e6432b5a1efe150efd4d779cd55d/rpds_py-2026.5.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:94068eb3ae6d43f5a786b7db96a406a34e6d5c24489feef32fd6e8946ea7b291", size = 343640, upload-time = "2026-05-28T12:01:08.441Z" }, + { url = "https://files.pythonhosted.org/packages/f7/7d/6c9523c1abbe840a1b7fba3c516d48e1d3487cc80fea4366c4071cf56784/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a5b10e8ce894825f380a8f1b6444cf73c294dfea62afbb2d13e3a9e630cec1", size = 375322, upload-time = "2026-05-28T12:01:09.934Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5d/0b7b03fb1dc509321f01de3149784ab773e34c8573022029af8076afcb9c/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fc09f82e63d4bcd58149572f857a431bae851dc747e313c3b5bdf7abb907fda8", size = 379066, upload-time = "2026-05-28T12:01:11.48Z" }, + { url = "https://files.pythonhosted.org/packages/d7/e2/8ef6012999ebf1cb1c22f876d9ce5e63d960fd4631d2af3202d3f480aa25/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e10464d17df3b582745c25cec695cb9558bca2cb6ddb631aee1787fc72c767b2", size = 494586, upload-time = "2026-05-28T12:01:13.051Z" }, + { url = "https://files.pythonhosted.org/packages/80/af/1eeb029bec67582c226b7809172207cd005073af4ebd906e65ff494f4983/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ba05adbf15d994c38ec0b7ab32e858e5110c21e9009a00a86545fd220f84e038", size = 388415, upload-time = "2026-05-28T12:01:14.631Z" }, + { url = "https://files.pythonhosted.org/packages/18/23/ffbe10711c4d766c1cab0557d6906c074f795814863c67b351355d29354a/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77c004fdc7b891967106f78ddfd7b076bfe6813c6139c6fff6aed3bcaa960b26", size = 372427, upload-time = "2026-05-28T12:01:16.153Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3a/30ba4a6ad457e5b070c18d742a33fb77d8d922b565cc881f8a5313d63bfe/rpds_py-2026.5.1-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:83bcf894486c9d78dd290d3c0124ff6dd8875d3025e2090a8ec49fcc37c55fdd", size = 383615, upload-time = "2026-05-28T12:01:17.809Z" }, + { url = "https://files.pythonhosted.org/packages/d3/69/62e242b53ce39c0814bd24e1a6e6eba6c92be716277745f317f9540a2e7b/rpds_py-2026.5.1-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3df104083952a0e0c6f10de33e440eabe98fb6317d23e1a58c68f6df08d01b9", size = 402786, upload-time = "2026-05-28T12:01:19.419Z" }, + { url = "https://files.pythonhosted.org/packages/38/c1/a770b9c186928a1ed0f7e6d7ae50e7f3950ed23e3f9e366dbc8e38cb55de/rpds_py-2026.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:980450826cf22e133c57e0835070bdd0dd3f73b9b708c3ce223def2cb9469e14", size = 551583, upload-time = "2026-05-28T12:01:21.013Z" }, + { url = "https://files.pythonhosted.org/packages/21/7c/68e8579b95375b70d2a963103c42e705856cdb98569258bd807f4423891c/rpds_py-2026.5.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:205dde846f24332ab0c1188699a043b8d165b79bb84529ce272c45048ff6be01", size = 616941, upload-time = "2026-05-28T12:01:22.548Z" }, + { url = "https://files.pythonhosted.org/packages/70/a1/a6135aed5730ff03ab957182259987ac11e55fb392a28dc6f0592048a280/rpds_py-2026.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:3966b82dd563176396df030f3dd52a6e54cb69b718e95e78bd555ed3d1e0185d", size = 578349, upload-time = "2026-05-28T12:01:24.118Z" }, + { url = "https://files.pythonhosted.org/packages/09/6e/f24201a76a84e6c49d0bdfdfcb735210e21701e9b21c5bfc0ba497dd62f6/rpds_py-2026.5.1-cp315-cp315-win32.whl", hash = "sha256:7818f8d0a415be74d2be3590b0a1c1f463a642f4d0217e7d10602dceef5b79aa", size = 209922, upload-time = "2026-05-28T12:01:25.522Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e4/966bc240bb0485fc265278f6de44d05834bf0b3618886e0b22e33d54c49a/rpds_py-2026.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:b3cc20c0d800af78fd0fac68086e28c1856cec51ea528bb81ea851aa40d39325", size = 226003, upload-time = "2026-05-28T12:01:27.062Z" }, + { url = "https://files.pythonhosted.org/packages/5c/5c/a15a59269cd5e74472734516c73795c15eccfc841b3d4b0228c3f53f19d0/rpds_py-2026.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:3609e9939a8a76cd904cf98a3f1f13b5dc7e150adeaee89e0ea09652ea213e16", size = 221245, upload-time = "2026-05-28T12:01:28.51Z" }, + { url = "https://files.pythonhosted.org/packages/e0/22/135ce03804e179a71ceb13be095deda4a279bc88f7a6b8fa161c5ad44e12/rpds_py-2026.5.1-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:5d333a7127d4b307601ac37792bee01bb95c867cbfacf21b6375b804d6bbd723", size = 352015, upload-time = "2026-05-28T12:01:30.214Z" }, + { url = "https://files.pythonhosted.org/packages/3b/5f/f1f6d2652eb9d848f6eb369d8db83a2da6249bb49ad2c2a48f45d54538d3/rpds_py-2026.5.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:b5f077b44a4f7808520f66dae234988d867deb9aed9be5da057ce9ba831b2a41", size = 345016, upload-time = "2026-05-28T12:01:31.656Z" }, + { url = "https://files.pythonhosted.org/packages/88/66/b74182775691ea2290c99e52ac8d5db844e56fbec90ce421f107658c8314/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d8f9b7b78c9538fc9e04e82ec0e888ff0c3cffcfad152c77e57cd09351a98a", size = 374775, upload-time = "2026-05-28T12:01:33.136Z" }, + { url = "https://files.pythonhosted.org/packages/ff/8f/15e5a61d9f0a43902d36561d4f07cae6ae9f4716be825159fd72717f33af/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e3a8ae58895ac107ed934a6bf51e5846f95c53b9b940c2c6d310838fd5846358", size = 380270, upload-time = "2026-05-28T12:01:34.574Z" }, + { url = "https://files.pythonhosted.org/packages/02/c3/f859b12763a80540cdf2af0f15b19904cf756a71d7bdd3f82ff3e5b1bbf9/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0957cf3c2b8632ec7aaebffebea8005b353cc2a237b6e2ae3c2cac0820704cfb", size = 495285, upload-time = "2026-05-28T12:01:36.127Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c7/ff27c2ac8411d30b03b1829fd88cae8dad1a4d0da48dd25e57c4038042e6/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c396c1304de421050b3681ea70f371874b54d41b0151e96109758144c231e30b", size = 389581, upload-time = "2026-05-28T12:01:37.635Z" }, + { url = "https://files.pythonhosted.org/packages/6e/67/fe92ee32a6cc05c77228a2f8b1762e7124f386ec20ff83d0757b762d58d0/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aad1bff7f666b9598e573815affd666aac6a13a585dde336f843e33350c7fadc", size = 376041, upload-time = "2026-05-28T12:01:39.307Z" }, + { url = "https://files.pythonhosted.org/packages/f8/91/b4d6685c27aba55bd82f25b278be8237038117d05f9659a6213ad3408130/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:656a042550878f12d45752452d47094b7cfe5ad1e9d7b87b5a22ad3ae5ff8015", size = 383946, upload-time = "2026-05-28T12:01:41.043Z" }, + { url = "https://files.pythonhosted.org/packages/bd/79/2c1d832a53c8e0f8e98fc970ec257b950fecd4f62be2ab7182b500a0cbc8/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73c4bd4f70294737b5206a3e8e30ccadbf8a60301831c8ea23eec5dbeea1ecfa", size = 405526, upload-time = "2026-05-28T12:01:43.032Z" }, + { url = "https://files.pythonhosted.org/packages/78/c4/c98117b03c6a8581ab2c2dfccfe9a5ad82bd8128a3c28b46a6ad2d97c393/rpds_py-2026.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:43bca78665423cabae77146f2fe7ce55272b6c8d55d82cca83effd42c7e13972", size = 551165, upload-time = "2026-05-28T12:01:44.648Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c1/bc479ca069200af730881b1bd525e3114b2b391a351509fcb1b772f28086/rpds_py-2026.5.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:42d0f20e85e549c870749d0e247f0c10d318a45b7e9676d575d2dcb04a1b2e66", size = 618778, upload-time = "2026-05-28T12:01:46.337Z" }, + { url = "https://files.pythonhosted.org/packages/77/65/38ab2f90df44c2febfb63cc10ced40763d9b4bc94d173e734528663fe7f5/rpds_py-2026.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:b1be5c35683684d5331b93600c210e8367c254683d8a6df6bd21bd2da3a334fb", size = 581839, upload-time = "2026-05-28T12:01:48.109Z" }, + { url = "https://files.pythonhosted.org/packages/15/2d/ce1f605fe036aadd460e5822e578c6c7ec3a860936cca37d6e0f299daa77/rpds_py-2026.5.1-cp315-cp315t-win32.whl", hash = "sha256:75808f6c38ce7749bb68cc2770161aae5045e6c6f6781a9782e74b93304399df", size = 207866, upload-time = "2026-05-28T12:01:49.648Z" }, + { url = "https://files.pythonhosted.org/packages/79/cb/966040123eb102371559746908ef2c9471f4d43e17ec9a645a2258dab64b/rpds_py-2026.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:90bd6630002a1c7f09e7843dd79f0d24f3d2897cc25a753480917865d14f15b3", size = 225441, upload-time = "2026-05-28T12:01:51.408Z" }, +] + [[package]] name = "ruff" version = "0.15.13" @@ -640,6 +962,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9b/36/9c015cd052fca743dae8cb2aeb16b551444787467db42ceab0fc968865af/ruff-0.15.13-py3-none-win_arm64.whl", hash = "sha256:2471da9bd1068c8c064b5fd9c0c4b6dddffd6369cb1cd68b29993b1709ff1b21", size = 11179336, upload-time = "2026-05-14T13:44:33.026Z" }, ] +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + [[package]] name = "snowballstemmer" version = "3.0.1" @@ -660,7 +991,7 @@ wheels = [ [[package]] name = "sphinx" -version = "9.1.0" +version = "8.2.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "alabaster" }, @@ -672,7 +1003,7 @@ dependencies = [ { name = "packaging" }, { name = "pygments" }, { name = "requests" }, - { name = "roman-numerals" }, + { name = "roman-numerals-py" }, { name = "snowballstemmer" }, { name = "sphinxcontrib-applehelp" }, { name = "sphinxcontrib-devhelp" }, @@ -681,9 +1012,9 @@ dependencies = [ { name = "sphinxcontrib-qthelp" }, { name = "sphinxcontrib-serializinghtml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324, upload-time = "2025-12-31T15:09:27.646Z" } +sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876, upload-time = "2025-03-02T22:31:59.658Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/f7/b1884cb3188ab181fc81fa00c266699dab600f927a964df02ec3d5d1916a/sphinx-9.1.0-py3-none-any.whl", hash = "sha256:c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978", size = 3921742, upload-time = "2025-12-31T15:09:25.561Z" }, + { url = "https://files.pythonhosted.org/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3", size = 3589741, upload-time = "2025-03-02T22:31:56.836Z" }, ] [[package]] @@ -698,6 +1029,47 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3c/dd/018ce05c532a22007ac58d4f45232514cd9d6dd0ee1dc374e309db830983/sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b", size = 22496, upload-time = "2023-07-08T18:40:52.659Z" }, ] +[[package]] +name = "sphinx-codelinks" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "comment-parser", version = "1.2.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "comment-parser", version = "1.2.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "docutils" }, + { name = "gitignore-parser" }, + { name = "giturlparse" }, + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "pygments" }, + { name = "sphinx" }, + { name = "sphinx-needs" }, + { name = "tree-sitter" }, + { name = "tree-sitter-c-sharp" }, + { name = "tree-sitter-cpp" }, + { name = "tree-sitter-python" }, + { name = "tree-sitter-rust" }, + { name = "tree-sitter-yaml" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/34/ba/cd6a8ac1cb3bbb0ada060215e655e012c4e0058f5b1785179c2ac5487eaa/sphinx_codelinks-1.2.0.tar.gz", hash = "sha256:bf0f5ca6c7f6be139a73ce01d2cda091a318eea6824d25f154543d7334118436", size = 33534, upload-time = "2026-02-19T07:44:32.323Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/85/2765dcffe0db7e2de35cf29450fe69ddafc4d97053cf5a0ee6e7e4b9c4f4/sphinx_codelinks-1.2.0-py3-none-any.whl", hash = "sha256:046cb12f9fa3b7f36fed97522263f6a705a22c165e135395060a08830e0d42b8", size = 39681, upload-time = "2026-02-19T07:44:31.242Z" }, +] + +[[package]] +name = "sphinx-data-viewer" +version = "0.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/3a/7aeeb805327af5b0e6f072312f9bc660615045e791a5a8f7258276fdddd1/sphinx_data_viewer-0.1.5.tar.gz", hash = "sha256:a7d5e58613562bb745380bfe61ca8b69997998167fd6fa9aea55606c9a4b17e4", size = 103260, upload-time = "2024-08-28T10:34:21.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/60/5e902d53a5eb3ec7166a8eda602bd78d4b8671a73917567edc8f13a3b366/sphinx_data_viewer-0.1.5-py3-none-any.whl", hash = "sha256:b74b1d304c505c464d07c7b225ed0d84ea02dcc88bc1c49cdad7c2275fbbdad4", size = 8215, upload-time = "2024-08-28T10:34:20.136Z" }, +] + [[package]] name = "sphinx-design" version = "0.7.0" @@ -726,7 +1098,10 @@ dev = [ docs = [ { name = "furo" }, { name = "myst-parser" }, + { name = "sphinx-codelinks" }, { name = "sphinx-design" }, + { name = "sphinx-needs" }, + { name = "sphinxcontrib-mermaid" }, ] ruff = [ { name = "ruff" }, @@ -752,7 +1127,10 @@ dev = [{ name = "prek", specifier = ">=0.3.3" }] docs = [ { name = "furo", specifier = ">=2024.5.6" }, { name = "myst-parser", specifier = ">=4.0.0" }, + { name = "sphinx-codelinks", specifier = ">=1.2.0" }, { name = "sphinx-design", specifier = ">=0.6.1" }, + { name = "sphinx-needs", specifier = ">=8.1" }, + { name = "sphinxcontrib-mermaid", specifier = ">=2.0.2" }, ] ruff = [{ name = "ruff", specifier = ">=0.8.0" }] testing = [ @@ -763,6 +1141,25 @@ testing = [ ] ty = [{ name = "ty", specifier = ">=0.0.37" }] +[[package]] +name = "sphinx-needs" +version = "8.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonschema-rs" }, + { name = "minijinja" }, + { name = "requests" }, + { name = "requests-file" }, + { name = "sphinx" }, + { name = "sphinx-data-viewer" }, + { name = "sphinxcontrib-jquery" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/65/c7707a4bf7857ac6b8c30461dd90ae8f4986c7e5a386f1c0069249057afa/sphinx_needs-8.1.1.tar.gz", hash = "sha256:fd72d22c958ece9a90aea517fd276197d7f602fc6ecf9fd48c1840b85cb96574", size = 27649386, upload-time = "2026-05-20T15:54:48.5Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/e5/de7fb014e4c63e0266e61ee79b08d2b17367cec5becd171b8f17869e4953/sphinx_needs-8.1.1-py3-none-any.whl", hash = "sha256:bd5bb71aa01726e1e1b5ac4ac7ce6b1e81acb8b519b480fae2faf7553a1271c5", size = 2704205, upload-time = "2026-05-20T15:54:45.962Z" }, +] + [[package]] name = "sphinxcontrib-applehelp" version = "2.0.0" @@ -790,6 +1187,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" }, ] +[[package]] +name = "sphinxcontrib-jquery" +version = "4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", size = 122331, upload-time = "2023-03-14T15:01:01.944Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae", size = 121104, upload-time = "2023-03-14T15:01:00.356Z" }, +] + [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" @@ -799,6 +1208,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" }, ] +[[package]] +name = "sphinxcontrib-mermaid" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "pyyaml" }, + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/75/3a1cc926da8c563c58ddc124a7b3fe5ccadcae96c96e3a6f8ac3653a210a/sphinxcontrib_mermaid-2.0.2.tar.gz", hash = "sha256:f09576c78ca93fa0e3034fd9c45aaffa7c44ab449de9c43b8b8d262afe52bc66", size = 19265, upload-time = "2026-05-05T13:59:02.959Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/8d/93be7e0f7fa915a576859b3bfac7a7baa3303181c44d7db7eefbd3e8a69f/sphinxcontrib_mermaid-2.0.2-py3-none-any.whl", hash = "sha256:d862e514991279fb4816302c5cfe167d2557bf3ce7125ae0cb47dac80a0f46ce", size = 14094, upload-time = "2026-05-05T13:59:01.585Z" }, +] + [[package]] name = "sphinxcontrib-qthelp" version = "2.0.0" @@ -817,6 +1240,114 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, ] +[[package]] +name = "tree-sitter" +version = "0.25.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/7c/0350cfc47faadc0d3cf7d8237a4e34032b3014ddf4a12ded9933e1648b55/tree-sitter-0.25.2.tar.gz", hash = "sha256:fe43c158555da46723b28b52e058ad444195afd1db3ca7720c59a254544e9c20", size = 177961, upload-time = "2025-09-25T17:37:59.751Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/9e/20c2a00a862f1c2897a436b17edb774e831b22218083b459d0d081c9db33/tree_sitter-0.25.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ddabfff809ffc983fc9963455ba1cecc90295803e06e140a4c83e94c1fa3d960", size = 146941, upload-time = "2025-09-25T17:37:34.813Z" }, + { url = "https://files.pythonhosted.org/packages/ef/04/8512e2062e652a1016e840ce36ba1cc33258b0dcc4e500d8089b4054afec/tree_sitter-0.25.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c0c0ab5f94938a23fe81928a21cc0fac44143133ccc4eb7eeb1b92f84748331c", size = 137699, upload-time = "2025-09-25T17:37:36.349Z" }, + { url = "https://files.pythonhosted.org/packages/47/8a/d48c0414db19307b0fb3bb10d76a3a0cbe275bb293f145ee7fba2abd668e/tree_sitter-0.25.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dd12d80d91d4114ca097626eb82714618dcdfacd6a5e0955216c6485c350ef99", size = 607125, upload-time = "2025-09-25T17:37:37.725Z" }, + { url = "https://files.pythonhosted.org/packages/39/d1/b95f545e9fc5001b8a78636ef942a4e4e536580caa6a99e73dd0a02e87aa/tree_sitter-0.25.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b43a9e4c89d4d0839de27cd4d6902d33396de700e9ff4c5ab7631f277a85ead9", size = 635418, upload-time = "2025-09-25T17:37:38.922Z" }, + { url = "https://files.pythonhosted.org/packages/de/4d/b734bde3fb6f3513a010fa91f1f2875442cdc0382d6a949005cd84563d8f/tree_sitter-0.25.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbb1706407c0e451c4f8cc016fec27d72d4b211fdd3173320b1ada7a6c74c3ac", size = 631250, upload-time = "2025-09-25T17:37:40.039Z" }, + { url = "https://files.pythonhosted.org/packages/46/f2/5f654994f36d10c64d50a192239599fcae46677491c8dd53e7579c35a3e3/tree_sitter-0.25.2-cp312-cp312-win_amd64.whl", hash = "sha256:6d0302550bbe4620a5dc7649517c4409d74ef18558276ce758419cf09e578897", size = 127156, upload-time = "2025-09-25T17:37:41.132Z" }, + { url = "https://files.pythonhosted.org/packages/67/23/148c468d410efcf0a9535272d81c258d840c27b34781d625f1f627e2e27d/tree_sitter-0.25.2-cp312-cp312-win_arm64.whl", hash = "sha256:0c8b6682cac77e37cfe5cf7ec388844957f48b7bd8d6321d0ca2d852994e10d5", size = 113984, upload-time = "2025-09-25T17:37:42.074Z" }, + { url = "https://files.pythonhosted.org/packages/8c/67/67492014ce32729b63d7ef318a19f9cfedd855d677de5773476caf771e96/tree_sitter-0.25.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0628671f0de69bb279558ef6b640bcfc97864fe0026d840f872728a86cd6b6cd", size = 146926, upload-time = "2025-09-25T17:37:43.041Z" }, + { url = "https://files.pythonhosted.org/packages/4e/9c/a278b15e6b263e86c5e301c82a60923fa7c59d44f78d7a110a89a413e640/tree_sitter-0.25.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f5ddcd3e291a749b62521f71fc953f66f5fd9743973fd6dd962b092773569601", size = 137712, upload-time = "2025-09-25T17:37:44.039Z" }, + { url = "https://files.pythonhosted.org/packages/54/9a/423bba15d2bf6473ba67846ba5244b988cd97a4b1ea2b146822162256794/tree_sitter-0.25.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd88fbb0f6c3a0f28f0a68d72df88e9755cf5215bae146f5a1bdc8362b772053", size = 607873, upload-time = "2025-09-25T17:37:45.477Z" }, + { url = "https://files.pythonhosted.org/packages/ed/4c/b430d2cb43f8badfb3a3fa9d6cd7c8247698187b5674008c9d67b2a90c8e/tree_sitter-0.25.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b878e296e63661c8e124177cc3084b041ba3f5936b43076d57c487822426f614", size = 636313, upload-time = "2025-09-25T17:37:46.68Z" }, + { url = "https://files.pythonhosted.org/packages/9d/27/5f97098dbba807331d666a0997662e82d066e84b17d92efab575d283822f/tree_sitter-0.25.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d77605e0d353ba3fe5627e5490f0fbfe44141bafa4478d88ef7954a61a848dae", size = 631370, upload-time = "2025-09-25T17:37:47.993Z" }, + { url = "https://files.pythonhosted.org/packages/d4/3c/87caaed663fabc35e18dc704cd0e9800a0ee2f22bd18b9cbe7c10799895d/tree_sitter-0.25.2-cp313-cp313-win_amd64.whl", hash = "sha256:463c032bd02052d934daa5f45d183e0521ceb783c2548501cf034b0beba92c9b", size = 127157, upload-time = "2025-09-25T17:37:48.967Z" }, + { url = "https://files.pythonhosted.org/packages/d5/23/f8467b408b7988aff4ea40946a4bd1a2c1a73d17156a9d039bbaff1e2ceb/tree_sitter-0.25.2-cp313-cp313-win_arm64.whl", hash = "sha256:b3f63a1796886249bd22c559a5944d64d05d43f2be72961624278eff0dcc5cb8", size = 113975, upload-time = "2025-09-25T17:37:49.922Z" }, + { url = "https://files.pythonhosted.org/packages/07/e3/d9526ba71dfbbe4eba5e51d89432b4b333a49a1e70712aa5590cd22fc74f/tree_sitter-0.25.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:65d3c931013ea798b502782acab986bbf47ba2c452610ab0776cf4a8ef150fc0", size = 146776, upload-time = "2025-09-25T17:37:50.898Z" }, + { url = "https://files.pythonhosted.org/packages/42/97/4bd4ad97f85a23011dd8a535534bb1035c4e0bac1234d58f438e15cff51f/tree_sitter-0.25.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bda059af9d621918efb813b22fb06b3fe00c3e94079c6143fcb2c565eb44cb87", size = 137732, upload-time = "2025-09-25T17:37:51.877Z" }, + { url = "https://files.pythonhosted.org/packages/b6/19/1e968aa0b1b567988ed522f836498a6a9529a74aab15f09dd9ac1e41f505/tree_sitter-0.25.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eac4e8e4c7060c75f395feec46421eb61212cb73998dbe004b7384724f3682ab", size = 609456, upload-time = "2025-09-25T17:37:52.925Z" }, + { url = "https://files.pythonhosted.org/packages/48/b6/cf08f4f20f4c9094006ef8828555484e842fc468827ad6e56011ab668dbd/tree_sitter-0.25.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:260586381b23be33b6191a07cea3d44ecbd6c01aa4c6b027a0439145fcbc3358", size = 636772, upload-time = "2025-09-25T17:37:54.647Z" }, + { url = "https://files.pythonhosted.org/packages/57/e2/d42d55bf56360987c32bc7b16adb06744e425670b823fb8a5786a1cea991/tree_sitter-0.25.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7d2ee1acbacebe50ba0f85fff1bc05e65d877958f00880f49f9b2af38dce1af0", size = 631522, upload-time = "2025-09-25T17:37:55.833Z" }, + { url = "https://files.pythonhosted.org/packages/03/87/af9604ebe275a9345d88c3ace0cf2a1341aa3f8ef49dd9fc11662132df8a/tree_sitter-0.25.2-cp314-cp314-win_amd64.whl", hash = "sha256:4973b718fcadfb04e59e746abfbb0288694159c6aeecd2add59320c03368c721", size = 130864, upload-time = "2025-09-25T17:37:57.453Z" }, + { url = "https://files.pythonhosted.org/packages/a6/6e/e64621037357acb83d912276ffd30a859ef117f9c680f2e3cb955f47c680/tree_sitter-0.25.2-cp314-cp314-win_arm64.whl", hash = "sha256:b8d4429954a3beb3e844e2872610d2a4800ba4eb42bb1990c6a4b1949b18459f", size = 117470, upload-time = "2025-09-25T17:37:58.431Z" }, +] + +[[package]] +name = "tree-sitter-c-sharp" +version = "0.23.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/fb/7e2962bc1901daf264e7ce263b168e0139304a5f8f66c9b2baf20e550f87/tree_sitter_c_sharp-0.23.5.tar.gz", hash = "sha256:2635c7d5ec93e59f2e831b571bed99c4cc68a5d183a0994020aa769e1b990a71", size = 1147914, upload-time = "2026-04-14T16:11:22.441Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/c4/86d8d469400a856757a464a6ac01af97d8cdacbb595e62bdb98bf1e9db90/tree_sitter_c_sharp-0.23.5-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:61e1981cf21b09ee547b9c4c68e64fb4394325f8fc8d5f6d50d41471eba923ea", size = 333658, upload-time = "2026-04-14T16:11:11.288Z" }, + { url = "https://files.pythonhosted.org/packages/c8/13/593c8603f834eaf15082b81e079289fc9f062b4c0ab5b9489134084eec06/tree_sitter_c_sharp-0.23.5-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:a75994a11f6fed3f5b8c36ad6a00e5dc43205bd912c43af3a2a54fdf649664eb", size = 376296, upload-time = "2026-04-14T16:11:12.972Z" }, + { url = "https://files.pythonhosted.org/packages/41/5a/a8855cbb5bbab28adb29c2c7f0e7be5a9f1d21450c13b3c3e613190d9b8c/tree_sitter_c_sharp-0.23.5-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:aa88a780204cd153c4c1ae2d59c654cee1402212fa0d069823d6d34301587438", size = 358333, upload-time = "2026-04-14T16:11:14.214Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c8/e0f391e343f5424d0627e3b6886c77baeb1249a3f10986be00b0b64ecdab/tree_sitter_c_sharp-0.23.5-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea38fb095d85d360dc5a0bec2fa605e496228876f798c9e089d5f0e72bcef46", size = 359448, upload-time = "2026-04-14T16:11:15.419Z" }, + { url = "https://files.pythonhosted.org/packages/6f/fc/10f807ac79f928241c5e0d827fdaf91e97dfba662fc7e07d7bd664140ec1/tree_sitter_c_sharp-0.23.5-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:05a9256415e7f24d4f133133794a9c224c60d19f677a04e2f6a94c25090b6d65", size = 358144, upload-time = "2026-04-14T16:11:17.087Z" }, + { url = "https://files.pythonhosted.org/packages/de/2a/6c3e12ef0cf09138717fcc02e1de8b76a3928d1bed65c7e3c2bd3172bcef/tree_sitter_c_sharp-0.23.5-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8636dc70b5a373c35c1036ed5de98e801f2e4d105ae41e2e20b6804c36e3bf33", size = 357525, upload-time = "2026-04-14T16:11:18.214Z" }, + { url = "https://files.pythonhosted.org/packages/2b/e0/bd287b092d611df95a9149117fd27b5947ce75527113d6898a4b4e2c8858/tree_sitter_c_sharp-0.23.5-cp310-abi3-win_amd64.whl", hash = "sha256:41a28cfa3d9ea50f5629e44550a03188c8fbd5079803dfc03554b6fd594b33fa", size = 338756, upload-time = "2026-04-14T16:11:19.661Z" }, + { url = "https://files.pythonhosted.org/packages/7f/fb/114ff43fdd256d0befed32f77c1dadee9517867181c70794571f718ed05c/tree_sitter_c_sharp-0.23.5-cp310-abi3-win_arm64.whl", hash = "sha256:2de4ebf95ddc2e92cd3105c8a8e0e7ec646bc82f52bfaf2f3acec0fa2401ec09", size = 337260, upload-time = "2026-04-14T16:11:20.849Z" }, +] + +[[package]] +name = "tree-sitter-cpp" +version = "0.23.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/2c/4dd63d705a8933543cad9b92ff31be849b164fec91a6eb63475ebc9ce668/tree_sitter_cpp-0.23.4.tar.gz", hash = "sha256:6a59c4cebb1ad1dc2e8d586cf8a72b39d21b8108b7b139d089719e81a339e41d", size = 940358, upload-time = "2024-11-11T06:59:24.934Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/ac/11d56670f7b048362db872ca866fd00ba2002a322ab179f047b7c0fb2910/tree_sitter_cpp-0.23.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aacb1759f0efd9dbc25bd8ee88184a340483018869f75412d9c3bc32c039a520", size = 287861, upload-time = "2024-11-11T06:59:15.005Z" }, + { url = "https://files.pythonhosted.org/packages/12/1c/0337c016bdc00a77a3326d12f10ee836401dd28f27db6fd5b7734bfb21ed/tree_sitter_cpp-0.23.4-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bc3c404d9f0cbd87951213a85440afbf4c31e718f8d907fa9ee12bea4b8d276f", size = 315513, upload-time = "2024-11-11T06:59:16.679Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7b/dd38c049b10ed7fda118b903a1d28a8b55a36b98c30606ef90e8f374c6de/tree_sitter_cpp-0.23.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc43ddf1279d5d5a4ef190373f4cb16522801bec4492bcd4754edf2aeba2b7b", size = 334813, upload-time = "2024-11-11T06:59:18.253Z" }, + { url = "https://files.pythonhosted.org/packages/6a/4d/23e390234d2acd351f5563b1079c515d7c1fe13ddb7392cee543be74dda3/tree_sitter_cpp-0.23.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:773d2cafc08bbc0f998687fa33f42f378c1a371cdb582870c4d13abb06092706", size = 316110, upload-time = "2024-11-11T06:59:19.823Z" }, + { url = "https://files.pythonhosted.org/packages/32/c7/b94a7e0e803af9d3bd4608fb4f0cfb2e9e233abaf0a38c928bfb0b1a025d/tree_sitter_cpp-0.23.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:247d127f0eb6574b0f6b30c0151e0bd0774e2e7acf9c558bdf9fbb8adc2e80c0", size = 308242, upload-time = "2024-11-11T06:59:21.466Z" }, + { url = "https://files.pythonhosted.org/packages/37/7e/909e52b3dec09c475140b0e175511e275d0d00ba2dbd7c68102d377ae0f6/tree_sitter_cpp-0.23.4-cp39-abi3-win_amd64.whl", hash = "sha256:68606a45bea92669d155399e1239f771a7767d8683cd8f8e30e7d813107030ca", size = 290997, upload-time = "2024-11-11T06:59:22.432Z" }, + { url = "https://files.pythonhosted.org/packages/d4/6a/65435d4d1f4c735be7ffe52d7c2e7b8a7f7c2790343a2719c60c548611c8/tree_sitter_cpp-0.23.4-cp39-abi3-win_arm64.whl", hash = "sha256:712f84f18be94cbe2a148fa4fdf40fcf4a8c25a8f7670efb9f8a47ddec2fc281", size = 288203, upload-time = "2024-11-11T06:59:23.404Z" }, +] + +[[package]] +name = "tree-sitter-python" +version = "0.25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b8/8b/c992ff0e768cb6768d5c96234579bf8842b3a633db641455d86dd30d5dac/tree_sitter_python-0.25.0.tar.gz", hash = "sha256:b13e090f725f5b9c86aa455a268553c65cadf325471ad5b65cd29cac8a1a68ac", size = 159845, upload-time = "2025-09-11T06:47:58.159Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/64/a4e503c78a4eb3ac46d8e72a29c1b1237fa85238d8e972b063e0751f5a94/tree_sitter_python-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:14a79a47ddef72f987d5a2c122d148a812169d7484ff5c75a3db9609d419f361", size = 73790, upload-time = "2025-09-11T06:47:47.652Z" }, + { url = "https://files.pythonhosted.org/packages/e6/1d/60d8c2a0cc63d6ec4ba4e99ce61b802d2e39ef9db799bdf2a8f932a6cd4b/tree_sitter_python-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:480c21dbd995b7fe44813e741d71fed10ba695e7caab627fb034e3828469d762", size = 76691, upload-time = "2025-09-11T06:47:49.038Z" }, + { url = "https://files.pythonhosted.org/packages/aa/cb/d9b0b67d037922d60cbe0359e0c86457c2da721bc714381a63e2c8e35eba/tree_sitter_python-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:86f118e5eecad616ecdb81d171a36dde9bef5a0b21ed71ea9c3e390813c3baf5", size = 108133, upload-time = "2025-09-11T06:47:50.499Z" }, + { url = "https://files.pythonhosted.org/packages/40/bd/bf4787f57e6b2860f3f1c8c62f045b39fb32d6bac4b53d7a9e66de968440/tree_sitter_python-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be71650ca2b93b6e9649e5d65c6811aad87a7614c8c1003246b303f6b150f61b", size = 110603, upload-time = "2025-09-11T06:47:51.985Z" }, + { url = "https://files.pythonhosted.org/packages/5d/25/feff09f5c2f32484fbce15db8b49455c7572346ce61a699a41972dea7318/tree_sitter_python-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6d5b5799628cc0f24691ab2a172a8e676f668fe90dc60468bee14084a35c16d", size = 108998, upload-time = "2025-09-11T06:47:53.046Z" }, + { url = "https://files.pythonhosted.org/packages/75/69/4946da3d6c0df316ccb938316ce007fb565d08f89d02d854f2d308f0309f/tree_sitter_python-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:71959832fc5d9642e52c11f2f7d79ae520b461e63334927e93ca46cd61cd9683", size = 107268, upload-time = "2025-09-11T06:47:54.388Z" }, + { url = "https://files.pythonhosted.org/packages/ed/a2/996fc2dfa1076dc460d3e2f3c75974ea4b8f02f6bc925383aaae519920e8/tree_sitter_python-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:9bcde33f18792de54ee579b00e1b4fe186b7926825444766f849bf7181793a76", size = 76073, upload-time = "2025-09-11T06:47:55.773Z" }, + { url = "https://files.pythonhosted.org/packages/07/19/4b5569d9b1ebebb5907d11554a96ef3fa09364a30fcfabeff587495b512f/tree_sitter_python-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:0fbf6a3774ad7e89ee891851204c2e2c47e12b63a5edbe2e9156997731c128bb", size = 74169, upload-time = "2025-09-11T06:47:56.747Z" }, +] + +[[package]] +name = "tree-sitter-rust" +version = "0.24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/87/75cbd22b927267d310f76cca1ab3c1d9d41035dfa3eb9cc95f96ee199440/tree_sitter_rust-0.24.2.tar.gz", hash = "sha256:54fb02a5911e345308b405174465112479f56dc39e3f1e7744d7568595f00db9", size = 339341, upload-time = "2026-03-27T21:08:55.629Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/24/2b2d33af5e27c84a4fde4e8cd2594bb4ab1e1cf48756a9f40dadc84956cc/tree_sitter_rust-0.24.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3620cfd12340efa43082d45df76349ff511893a9c361da2f8d6d51e307020a59", size = 129507, upload-time = "2026-03-27T21:08:47.585Z" }, + { url = "https://files.pythonhosted.org/packages/78/2a/cf39f881a545360b5a86bb1accba1f4acc713daab01fb9edd35b6e84f473/tree_sitter_rust-0.24.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:01a46622735498493f29f3e628a90de95c96a07bfbeb88996243eb986b1cee36", size = 136812, upload-time = "2026-03-27T21:08:48.761Z" }, + { url = "https://files.pythonhosted.org/packages/ca/45/a051bbd3045a61182dde25b93ae9a33d2677c935b16952283e12eaf46051/tree_sitter_rust-0.24.2-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e033c5a93b57c88e0a835880de39fc802909ff69f57aaff6000211c196ea5190", size = 164706, upload-time = "2026-03-27T21:08:49.605Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f6/a5a146df5c0a5daea3ffcd5d7245775fe7f084357770d5a313dd6245ae78/tree_sitter_rust-0.24.2-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9d76d1208c3638b871236090759dfc13d478921320653a6c9da5336e7c58f65a", size = 170310, upload-time = "2026-03-27T21:08:50.424Z" }, + { url = "https://files.pythonhosted.org/packages/95/a8/f85b1ca75e01361ca5f92d226593ca4857cea49551b9f6c8fa6fc08ea917/tree_sitter_rust-0.24.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:87930163a462408c49ab62c667e74029bc26b4cc7123dd1bdc7352215786c64a", size = 168668, upload-time = "2026-03-27T21:08:51.404Z" }, + { url = "https://files.pythonhosted.org/packages/a2/e1/3519f866a4679ca36acd9f5a06a779ecb8a92b18887c5546458d521df557/tree_sitter_rust-0.24.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:da2b86099028fd42c6cd32878b7b16b01f8aac0f7b0e98742b7fa6bc3cf09b89", size = 162403, upload-time = "2026-03-27T21:08:52.588Z" }, + { url = "https://files.pythonhosted.org/packages/34/71/7ef609894dbfe5699eb16f7471f9b8af1d958d8ba3e29c238d7607e8cb47/tree_sitter_rust-0.24.2-cp39-abi3-win_amd64.whl", hash = "sha256:4529c125d928882ddfb879fdc6bc0704913261ecc078b6fa7902559e0daf200d", size = 129422, upload-time = "2026-03-27T21:08:54.031Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d8/050a781172745bc345f98abb7c56e72022ea0790f8e793de981c83c2ef15/tree_sitter_rust-0.24.2-cp39-abi3-win_arm64.whl", hash = "sha256:66ba90f61bd54f4c4f5d30434957daf64507c16b0313df76becb37d63f70a227", size = 128245, upload-time = "2026-03-27T21:08:54.803Z" }, +] + +[[package]] +name = "tree-sitter-yaml" +version = "0.7.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/b6/941d356ac70c90b9d2927375259e3a4204f38f7499ec6e7e8a95b9664689/tree_sitter_yaml-0.7.2.tar.gz", hash = "sha256:756db4c09c9d9e97c81699e8f941cb8ce4e51104927f6090eefe638ee567d32c", size = 84882, upload-time = "2025-10-07T14:40:36.071Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/29/c0b8dbff302c49ff4284666ffb6f2f21145006843bb4c3a9a85d0ec0b7ae/tree_sitter_yaml-0.7.2-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:7e269ddcfcab8edb14fbb1f1d34eed1e1e26888f78f94eedfe7cc98c60f8bc9f", size = 43898, upload-time = "2025-10-07T14:40:29.486Z" }, + { url = "https://files.pythonhosted.org/packages/18/0d/15a5add06b3932b5e4ce5f5e8e179197097decfe82a0ef000952c8b98216/tree_sitter_yaml-0.7.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:0807b7966e23ddf7dddc4545216e28b5a58cdadedcecca86b8d8c74271a07870", size = 44691, upload-time = "2025-10-07T14:40:30.369Z" }, + { url = "https://files.pythonhosted.org/packages/72/92/c4b896c90d08deb8308fadbad2210fdcc4c66c44ab4292eac4e80acb4b61/tree_sitter_yaml-0.7.2-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f1a5c60c98b6c4c037aae023569f020d0c489fad8dc26fdfd5510363c9c29a41", size = 91430, upload-time = "2025-10-07T14:40:31.16Z" }, + { url = "https://files.pythonhosted.org/packages/89/59/61f1fed31eb6d46ff080b8c0d53658cf29e10263f41ef5fe34768908037a/tree_sitter_yaml-0.7.2-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88636d19d0654fd24f4f242eaaafa90f6f5ebdba8a62e4b32d251ed156c51a2a", size = 92428, upload-time = "2025-10-07T14:40:31.954Z" }, + { url = "https://files.pythonhosted.org/packages/e3/62/a33a04d19b7f9a0ded780b9c9fcc6279e37c5d00b89b00425bb807a22cc2/tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1d2e8f0bb14aa4537320952d0f9607eef3021d5aada8383c34ebeece17db1e06", size = 90580, upload-time = "2025-10-07T14:40:33.037Z" }, + { url = "https://files.pythonhosted.org/packages/6c/e7/9525defa7b30792623f56b1fba9bbba361752348875b165b8975b87398fd/tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:74ca712c50fc9d7dbc68cb36b4a7811d6e67a5466b5a789f19bf8dd6084ef752", size = 90455, upload-time = "2025-10-07T14:40:33.778Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d6/8d1e1ace03db3b02e64e91daf21d1347941d1bbecc606a5473a1a605250d/tree_sitter_yaml-0.7.2-cp310-abi3-win_amd64.whl", hash = "sha256:7587b5ca00fc4f9a548eff649697a3b395370b2304b399ceefa2087d8a6c9186", size = 45514, upload-time = "2025-10-07T14:40:34.562Z" }, + { url = "https://files.pythonhosted.org/packages/d8/c7/dcf3ea1c4f5da9b10353b9af4455d756c92d728a8f58f03c480d3ef0ead5/tree_sitter_yaml-0.7.2-cp310-abi3-win_arm64.whl", hash = "sha256:f63c227b18e7ce7587bce124578f0bbf1f890ac63d3e3cd027417574273642c4", size = 44065, upload-time = "2025-10-07T14:40:35.337Z" }, +] + [[package]] name = "ty" version = "0.0.37" @@ -842,6 +1373,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1a/ed/5ec4b501479bc5dad55467e2fe72e797cb9c178468c0d1a514536872ebc5/ty-0.0.37-py3-none-win_arm64.whl", hash = "sha256:6c3c2b997f68c71e14242b96d48cba3c086439556af02bb4613aa458950d5c23", size = 10958817, upload-time = "2026-05-16T05:57:08.907Z" }, ] +[[package]] +name = "typer" +version = "0.26.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "rich" }, + { name = "shellingham" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/ed/ef06584ccdd5c410df0837951ecd7e15d9a6144ea1bd4c73cecab1a89891/typer-0.26.7.tar.gz", hash = "sha256:e314a34c617e419c091b2830dda3ea1f257134ff593061a8f5b9717ab8dddb3a", size = 201709, upload-time = "2026-06-03T07:18:06.843Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/25/2201973529af2c954de0bb725323c3aaed6d7f0ceee8f550dec9185df013/typer-0.26.7-py3-none-any.whl", hash = "sha256:5c87cfbc5d34491c5346ebf49c23e18d56ccb863268d3a8d592b26087c2f5e58", size = 122456, upload-time = "2026-06-03T07:18:05.732Z" }, +] + [[package]] name = "typing-extensions" version = "4.15.0"