diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/README.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/README.md index bd1a269f..a35faa56 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/README.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/README.md @@ -2,9 +2,9 @@ title: "Scientific Agent Skills" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/991bd993/README.md -upstream_sha: 991bd993 -imported_at: 2026-08-08 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/README.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: catalogue upstream_changes: accepted author: upstream @@ -14,10 +14,11 @@ validated: false # Scientific Agent Skills [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE.md) -[![Version](https://img.shields.io/badge/Version-2.62.0-blue.svg)](pyproject.toml) -[![Skills](https://img.shields.io/badge/Skills-159-brightgreen.svg)](#-whats-included) +[![Version](https://img.shields.io/badge/Version-2.63.0-blue.svg)](pyproject.toml) +[![Skills](https://img.shields.io/badge/Skills-161-brightgreen.svg)](#-whats-included) [![Databases](https://img.shields.io/badge/Databases-100%2B-orange.svg)](#-whats-included) [![Agent Skills](https://img.shields.io/badge/Standard-Agent_Skills-blueviolet.svg)](https://agentskills.io/) +[![Agent Plugins](https://img.shields.io/badge/Standard-Agent_Plugins-0A7A72.svg)](https://agent-plugins.org/) [![Security Scan](https://github.com/K-Dense-AI/scientific-agent-skills/actions/workflows/security-scan.yml/badge.svg)](https://github.com/K-Dense-AI/scientific-agent-skills/actions/workflows/security-scan.yml) [![Skill Tests](https://github.com/K-Dense-AI/scientific-agent-skills/actions/workflows/skill-tests.yml/badge.svg)](https://github.com/K-Dense-AI/scientific-agent-skills/actions/workflows/skill-tests.yml) [![Works with](https://img.shields.io/badge/Works_with-Cursor_|_Claude_Code_|_Codex_|_Google_Antigravity-blue.svg)](#-getting-started) @@ -25,23 +26,13 @@ validated: false [![LinkedIn](https://img.shields.io/badge/LinkedIn-K--Dense_Inc.-0A66C2?logo=linkedin)](https://www.linkedin.com/company/k-dense-inc) [![YouTube](https://img.shields.io/badge/YouTube-K--Dense_Inc.-FF0000?logo=youtube)](https://www.youtube.com/@K-Dense-Inc) -## Star History - - - - - - Star History Chart - - - > **๐Ÿ”” Claude Scientific Skills is now Scientific Agent Skills.** Same skills, broader compatibility โ€” now works with any AI agent that supports the open [Agent Skills](https://agentskills.io/) standard, not just Claude. -> **New: [K-Dense BYOK](https://github.com/K-Dense-AI/k-dense-byok)** โ€” A free, open-source AI co-scientist that runs on your desktop, powered by Scientific Agent Skills. Bring your own API keys, pick from 40+ models, and get a full research workspace with web search, file handling, 100+ scientific databases, and access to all 159 skills in this repo. Your data stays on your computer, and you can optionally scale to cloud compute via [Modal](https://modal.com/) for heavy workloads. [Get started here.](https://github.com/K-Dense-AI/k-dense-byok) +> **New: [K-Dense BYOK](https://github.com/K-Dense-AI/k-dense-byok)** โ€” A free, open-source AI co-scientist that runs on your desktop, powered by Scientific Agent Skills. Bring your own API keys, pick from 40+ models, and get a full research workspace with web search, file handling, 100+ scientific databases, and access to all 161 skills in this repo. Your data stays on your computer, and you can optionally scale to cloud compute via [Modal](https://modal.com/) for heavy workloads. [Get started here.](https://github.com/K-Dense-AI/k-dense-byok) > **Stay up to date:** Follow K-Dense on [X](https://x.com/k_dense_ai), [LinkedIn](https://www.linkedin.com/company/k-dense-inc), and [YouTube](https://www.youtube.com/@K-Dense-Inc) for new skills, release announcements, walkthroughs, research workflow demos, and examples you can use with your own AI agent. -A comprehensive collection of **159 ready-to-use scientific and research skills** (covering cancer genomics, individual-level 1000 Genomes queries, hosted regulatory-sequence prediction, live pathogen-variant surveillance, analytical method validation, PK/PD modelling and dose selection, full-text biomedical and regulatory literature retrieval, drug-target binding, molecular dynamics, RNA velocity, geospatial science, time series forecasting, scientific ML resource discovery via Hugging Science, 78+ scientific databases, and more) for any AI agent that supports the open [Agent Skills](https://agentskills.io/) standard, created by [K-Dense](https://k-dense.ai). Works with **Cursor, Claude Code, Codex, Google Antigravity, and more**. Transform your AI agent into a research assistant capable of executing complex multi-step scientific workflows across biology, chemistry, medicine, and beyond. +A comprehensive collection of **161 ready-to-use scientific and research skills** (covering cancer genomics, individual-level 1000 Genomes queries, hosted regulatory-sequence prediction, live pathogen-variant surveillance, analytical method validation, PK/PD modelling and dose selection, full-text biomedical and regulatory literature retrieval, drug-target binding, bounded biomedical knowledge graph search, molecular dynamics, RNA velocity, geospatial science, time series forecasting, scientific ML resource discovery via Hugging Science, 78+ scientific databases, and more) for any AI agent that supports the open [Agent Skills](https://agentskills.io/) standard, created by [K-Dense](https://k-dense.ai). The repository is also a portable [Agent Plugins](https://agent-plugins.org/) package (`plugin.json` + `skills/`), so plugin-capable clients can load the whole collection as one plugin. Works with **Cursor, Claude Code, Codex, Google Antigravity, and more**. Transform your AI agent into a research assistant capable of executing complex multi-step scientific workflows across biology, chemistry, medicine, and beyond. > โญ **Help make AI for science easier to discover:** If Scientific Agent Skills saves you time, teaches your agent a workflow, or helps your lab move faster, please [star this repository](https://github.com/K-Dense-AI/scientific-agent-skills). A star is a public signal that these open, reusable research skills are worth maintaining: it helps scientists, engineers, and open-source contributors find the project, shows which agent-skill standards are gaining real adoption, and gives us a clear reason to keep expanding the collection for the community. @@ -76,9 +67,9 @@ These skills enable your AI agent to seamlessly work with specialized scientific ## ๐Ÿ“ฆ What's Included -This repository provides **159 scientific and research skills** organized into the following categories: +This repository provides **162 scientific and research skills** organized into the following categories: -- **100+ Scientific & Financial Databases** - A unified database-lookup skill provides deterministic, provenance-rich access to 78 public databases (PubChem, ChEMBL, UniProt, COSMIC, ClinicalTrials.gov, FRED, USPTO, and more), plus dedicated skills for DepMap, Imaging Data Commons, PrimeKG, U.S. Treasury Fiscal Data, Hugging Science, OneKGPd, and Genomic Intelligence. Multi-database packages like BioServices (~40 bioinformatics services), BioPython (39 NCBI sub-databases via Entrez), and gget (20+ genomics databases) add further coverage +- **100+ Scientific & Financial Databases** - A unified database-lookup skill provides deterministic, provenance-rich access to 78 public databases (PubChem, ChEMBL, UniProt, COSMIC, ClinicalTrials.gov, FRED, USPTO, and more), plus dedicated skills for DepMap, Imaging Data Commons, PrimeKG, NCATS ARAX, U.S. Treasury Fiscal Data, Hugging Science, OneKGPd, and Genomic Intelligence. Multi-database packages like BioServices (~40 bioinformatics services), BioPython (39 NCBI sub-databases via Entrez), and gget (20+ genomics databases) add further coverage - **70+ Optimized Python Package Skills** - Explicitly defined, version-aware workflows for RDKit, Scanpy, PyTorch Lightning, scikit-learn, PyTDC, PathML, pydicom, NeuroKit2, PufferLib, QuTiP, GeoPandas, pymatgen, BioPython, Qiskit, Molecular Dynamics (OpenMM/MDAnalysis), and others. The agent can still use *any* Python package; these skills provide stronger, safer guidance for the packages listed - **9 Scientific Integration Skills** - Explicitly defined skills for Benchling, DNAnexus, LatchBio, OMERO, Protocols.io, Open Notebook, Ginkgo Cloud Lab, LabArchives, and Opentrons. Again, the agent is not limited to these โ€” any API or platform reachable from Python is fair game; these skills are the optimized, pre-documented paths - **30+ Analysis & Communication Tools** - Literature review, evidence-traceable scientific writing, confidential peer review, document processing, Paperclip (full-text papers, FDA/PMDA/EMA filings, and trial registries with line-pinned citations), Paperzilla, Exa Search, macro-free PPTX posters, slides, schematics, infographics, Mermaid diagrams, and more @@ -123,7 +114,7 @@ Each skill includes: - **Multi-Step Workflows** - Execute complex pipelines with a single prompt ### ๐ŸŽฏ **Comprehensive Coverage** -- **159 Skills** - Extensive coverage across all major scientific domains +- **161 Skills** - Extensive coverage across all major scientific domains - **100+ Databases** - Unified access to 78+ databases via database-lookup, plus dedicated data access skills and multi-database packages like BioServices, BioPython, and gget - **70+ Optimized Python Package Skills** - Current, version-scoped guidance for packages including RDKit, Scanpy, PyTorch Lightning, scikit-learn, PyTDC, pydicom, PufferLib, QuTiP, GeoPandas, pymatgen, Qiskit, Molecular Dynamics (OpenMM/MDAnalysis), scVelo, and TimesFM (the agent can use any Python package; these are the pre-documented paths) @@ -178,7 +169,7 @@ Pin to a specific release tag or commit SHA for reproducible installs: ```bash # Pin to a release tag -gh skill install K-Dense-AI/scientific-agent-skills --pin v2.62.0 +gh skill install K-Dense-AI/scientific-agent-skills --pin v2.63.0 # Pin to a commit SHA gh skill install K-Dense-AI/scientific-agent-skills --pin abc123def @@ -194,6 +185,27 @@ gh skill update gh skill update --all ``` +### Option 3: Agent Plugins (Cursor, Codex, and other plugin clients) + +This repository is a valid [Agent Plugins](https://agent-plugins.org/) 1.0.0 package: root [`plugin.json`](plugin.json) plus Agent Skills under `skills/`. Clients that support the standard discover every immediate child of `skills/` that contains a `SKILL.md`. + +**Cursor** โ€” symlink or copy the repo into the local plugins directory, then reload: + +```bash +mkdir -p ~/.cursor/plugins/local +ln -s "$(pwd)" ~/.cursor/plugins/local/scientific-agent-skills +``` + +Restart Cursor or run **Developer: Reload Window**, then confirm the plugin and its skills appear under **Customize**. See [Cursor plugins](https://cursor.com/docs/plugins). + +**Codex** โ€” install from a local checkout (confirm the current CLI flag names in Codex docs): + +```bash +codex plugins install . +``` + +Compatible clients (Cursor, Codex, GitHub Copilot, VS Code, Kiro, and others listed at [agent-plugins.org](https://agent-plugins.org/compatible-clients)) share the same package layout; installation UX stays client-specific. + ### Other Agent Skills hosts (OpenClaw, NemoClaw, Pi, Hermes, โ€ฆ) Agent hosts differ in install paths, discovery settings, and support for optional frontmatter fields. `npx skills add` (Option 1) commonly installs into the `~/.agents/skills/` convention, with project-scoped installs under `.agents/skills/`; confirm both paths against your host's current documentation. To install manually on a host configured to scan one of those locations: @@ -209,7 +221,7 @@ For Hermes versions that support skill taps, add the repository as a tap: hermes skills tap add K-Dense-AI/scientific-agent-skills ``` -Every `SKILL.md` has YAML frontmatter, but legacy and community skills vary in `metadata` formatting (block or flow style) and optional extension fields. Repository updates must keep `metadata.version` as a quoted numeric string and pass canonical `skills-ref validate ./skills/` checks. Hosts may interpret optional metadata and credential prompts differently, so verify behavior on the target host. Because 159 skills add up to a lot of standing context, consider installing a topical subset rather than the whole collection. +Every `SKILL.md` has YAML frontmatter, but legacy and community skills vary in `metadata` formatting (block or flow style) and optional extension fields. Repository updates must keep `metadata.version` as a quoted numeric string and pass canonical `skills-ref validate ./skills/` checks. Hosts may interpret optional metadata and credential prompts differently, so verify behavior on the target host. Because 161 skills add up to a lot of standing context, consider installing a topical subset rather than the whole collection. > **NemoClaw note:** NemoClaw runs agents inside NVIDIA OpenShell with default-deny outbound networking. Skills are discovered and loaded normally, but any skill that needs the network โ€” package installs via `uv`, or API calls (Exa, Parallel, Benchling, NCBI, Materials Project, โ€ฆ) โ€” only works once the operator pre-approves the relevant domains in the OpenShell TUI. @@ -448,7 +460,7 @@ networks, and search GEO for similar patterns. ## ๐Ÿ“š Available Skills -This repository contains **159 scientific and research skills** organized across multiple domains. Each skill provides comprehensive documentation, code examples, and best practices for working with scientific libraries, databases, and tools. +This repository contains **162 scientific and research skills** organized across multiple domains. Each skill provides comprehensive documentation, code examples, and best practices for working with scientific libraries, databases, and tools. ### Skill Categories @@ -512,7 +524,8 @@ This repository contains **159 scientific and research skills** organized across - Astronomy: Astropy - Quantum computing: Cirq, PennyLane, Qiskit, QuTiP 5.3 -#### โš™๏ธ **Engineering & Simulation** (5 skills) +#### โš™๏ธ **Engineering & Simulation** (6 skills) +- Lab hardware CAD: parametric build123d 0.11.1 models for microfluidic chips and molds, optomechanical mounts, microplate and cuvette adapters, and behavior rigs, checked against ANSI/SLAS and optical-table dimensional standards and reviewed with mandatory multi-view renders - Numerical computing: proprietary MATLAB R2026a and distinct GNU Octave 11.3 planning/review workflows - Computational fluid dynamics: bounded FluidSim 0.9 simulations with numerical-validity and HPC checks - Experimental flow measurement: OpenPIV (velocity fields from PIV image pairs, interrogation-window cross-correlation, spurious-vector validation, vorticity/strain-rate/turbulence statistics) @@ -564,12 +577,13 @@ This repository contains **159 scientific and research skills** organized across - Citations: Citation Management, pyzotero - Illustration: Generate Image (AI image generation with FLUX.2 Pro and Gemini 3.1 Flash Image / Nano Banana 2) -#### ๐Ÿ”ฌ **Scientific Databases & Data Access** (10 skills โ†’ 100+ databases total) +#### ๐Ÿ”ฌ **Scientific Databases & Data Access** (11 skills โ†’ 100+ databases total) > A unified database-lookup skill provides deterministic REST API access to 78 public databases across all domains, with retrieval contracts, pagination/count reconciliation, and endpoint provenance. Dedicated skills cover specialized data platforms. Multi-database packages like BioServices (~40 bioinformatics services), BioPython (39 NCBI sub-databases via Entrez), and gget (20+ genomics databases) add further coverage. - Unified access: Database Lookup (78 databases spanning chemistry, genomics, clinical, pathways, patents, economics, and more โ€” PubChem, ChEMBL, UniProt, PDB, AlphaFold, KEGG, Reactome, STRING, ClinVar, COSMIC, ClinicalTrials.gov, FDA, FRED, USPTO, SEC EDGAR, and dozens more โ€” with auditable filters and provenance) - Cancer genomics: DepMap (cancer cell line dependencies, drug sensitivity, gene effect profiles) - Cancer imaging: Imaging Data Commons (NCI radiology & pathology datasets via idc-index) - Knowledge graph: PrimeKG (precision medicine knowledge graph โ€” genes, drugs, diseases, phenotypes) +- Biomedical knowledge graph search: [NCATS ARAX](skills/ncats-arax/) (bounded, Biolink-constrained one-hop and endpoint-pinned two-hop queries over knowledge graphs with up to five explicitly selected NCATS Translator providers, with provenance preservation) - Fiscal data: U.S. Treasury Fiscal Data (national debt, Treasury statements, auctions, exchange rates) - Scientific ML resource catalog: Hugging Science (curated index of datasets, models, blog posts, and interactive Spaces across 17 scientific domains โ€” astronomy, biology, chemistry, climate, genomics, materials science, medicine, physics, scientific reasoning, and more โ€” with usage patterns for `datasets`, `transformers`, and `gradio_client`) - Individual-level population genomics: OneKGPd (3,202-person high-coverage 1000 Genomes cohort queries) @@ -620,6 +634,7 @@ Deep dives, benchmarks, and guides from the [K-Dense blog](https://www.k-dense.a - **[Agent Skills: The Final Piece for AI-Powered Scientific Research](https://www.k-dense.ai/blog/agent-skills-final-piece-for-ai-powered-research)** โ€” What Agent Skills are, why curated domain guidance beats raw model capability, and an introduction to this repository. - **[K-Dense Web vs Scientific Agent Skills: Why We Built Both (And Which One You Should Use)](https://www.k-dense.ai/blog/k-dense-web-vs-scientific-agent-skills)** โ€” When the open-source skills are the right tool, and when a hosted platform with managed compute makes more sense. +- **[AI Co-Scientists, Answered: 20 Questions from a Live Session with a University Research Center](https://www.k-dense.ai/blog/ai-co-scientists-answered-20-questions)** โ€” Practical questions from a research center evaluating AI co-scientists: what stays open source and MIT-licensed, how local and desktop deployments work, how data is handled, and how to choose between the hosted platform and the BYOK setup that runs these skills. ### Skill benchmarks and deep dives @@ -631,6 +646,13 @@ Deep dives, benchmarks, and guides from the [K-Dense blog](https://www.k-dense.a - **[Benchmarking Nano Banana 2 Lite for Scientific Image Generation](https://www.k-dense.ai/blog/benchmarking-nano-banana-2-lite-scientific-image-model)** โ€” A 240-image comparison of scientific-diagram models, useful when choosing a backend for [generate-image](skills/generate-image/): 3.8 s median latency for Nano Banana 2 Lite against 49 s for GPT Image 2, with a quality tradeoff. - **[Benchmarking NVIDIA BioNeMo Agent Toolkit Skills for NIM microservices](https://www.k-dense.ai/blog/benchmarking-nvidia-bionemo-nim-skill)** โ€” A separate NVIDIA skill set rather than one of these, but the findings generalize: skills help most with routing to non-obvious endpoints and with weak-model reliability, and do not improve the underlying scientific model's accuracy. +### Why the workflow layer matters + +- **[The Model Is No Longer the Bottleneck](https://www.k-dense.ai/blog/the-model-is-no-longer-the-bottleneck)** โ€” The case for why a repository like this one exists: frontier models now match specialized scientific software on raw capability (ยฑ0.079 ppm on NMR hydrogen shift prediction), so the limiting factor has moved to the workflow around the model โ€” data access, code execution, verification, and auditable output. +- **[The AI Co-Scientist Is Here. The Bottleneck Is Verification.](https://www.k-dense.ai/blog/ai-co-scientist-verification-bottleneck)** โ€” A 10-point checklist for evaluating a research agent, built around exposing sources, code, data provenance, and intermediate work rather than a polished final answer โ€” the same reasoning behind the provenance and retrieval-contract requirements in skills like [database-lookup](skills/database-lookup/) and [scientific-writing](skills/scientific-writing/). +- **[Reproduction, Not Generation, Is AI's Killer App for Science](https://www.k-dense.ai/blog/reproduction-not-generation-ai-for-science)** โ€” Why re-running published analyses is the highest-value use of an agent: 78% of papers and 93% of individual analysis tasks reproduced across a 221-study benchmark, because a reproduction can be checked against known numbers while a generated claim cannot. +- **[Introducing K-Bench 01: Nine Frontier Models, 178 Real Scientific Tasks, and a Lot of Confident Wrong Answers](https://www.k-dense.ai/blog/introducing-k-bench-01-internal-benchmark)** โ€” Nine frontier models on 178 real user tasks, with overclaiming in 40% of runs. Useful calibration for what to check when an agent reports success, and context for the verification boundaries written into the clinical, regulatory, and research-methodology skills above. + ### Security and safe deployment - **[Security in the Science Agent Era: What Every Lab Needs to Know Before Installing Skills](https://www.k-dense.ai/blog/skill-security-before-you-install)** โ€” The practical review checklist behind this repo's [Security Disclaimer](#%EF%B8%8F-security-disclaimer): read the full `SKILL.md` and `scripts/`, scan before installing, and pin versions instead of tracking a branch. @@ -842,7 +864,7 @@ Recommended practice: title = {Scientific Agent Skills: A Comprehensive Collection of Scientific Tools for AI Agents}, year = {2026}, url = {https://github.com/K-Dense-AI/scientific-agent-skills}, - note = {159 skills covering databases, packages, integrations, and analysis tools} + note = {161 skills covering databases, packages, integrations, and analysis tools} } ``` diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/docs/examples.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/docs/examples.md index 86405e48..ffdc1894 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/docs/examples.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/docs/examples.md @@ -2,9 +2,9 @@ title: "Real-World Scientific Examples" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/991bd993/docs/examples.md -upstream_sha: 991bd993 -imported_at: 2026-08-08 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/docs/examples.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: catalogue upstream_changes: accepted author: upstream @@ -94,31 +94,32 @@ build that check in as a numbered step. 8. [Materials Science & Chemistry](#materials-science--chemistry) 9. [Digital Pathology](#digital-pathology) 10. [Lab Automation & Protocol Design](#lab-automation--protocol-design) -11. [Agricultural Genomics](#agricultural-genomics) -12. [Neuroscience & Brain Imaging](#neuroscience--brain-imaging) -13. [Environmental Microbiology](#environmental-microbiology) -14. [Infectious Disease Research](#infectious-disease-research) -15. [Multi-Omics Integration](#multi-omics-integration) -16. [Regulatory Genomics & Variant-to-Function](#regulatory-genomics--variant-to-function) -17. [Experimental Physics & Data Analysis](#experimental-physics--data-analysis) -18. [Chemical Engineering & Process Optimization](#chemical-engineering--process-optimization) -19. [Fluid Mechanics & Bioprocess Engineering](#fluid-mechanics--bioprocess-engineering) -20. [Scientific Illustration & Visual Communication](#scientific-illustration--visual-communication) -21. [Quantum Computing for Chemistry](#quantum-computing-for-chemistry) -22. [Open Quantum Systems & Cross-Framework Benchmarking](#open-quantum-systems--cross-framework-benchmarking) -23. [Research Grant Writing](#research-grant-writing) -24. [Flow Cytometry & Immunophenotyping](#flow-cytometry--immunophenotyping) -25. [Geospatial & Earth Observation](#geospatial--earth-observation) -26. [Time-Series Forecasting & Sensor Analytics](#time-series-forecasting--sensor-analytics) -27. [Cloud-Scale Bioinformatics](#cloud-scale-bioinformatics) -28. [Functional Genomics & Knowledge Graphs](#functional-genomics--knowledge-graphs) -29. [Molecular Modeling & Simulation](#molecular-modeling--simulation) -30. [Protein Engineering & Cloud Wet-Lab](#protein-engineering--cloud-wet-lab) -31. [Medical Imaging & Clinical AI](#medical-imaging--clinical-ai) -32. [Research Ideation & Study Planning](#research-ideation--study-planning) -33. [Literature & Knowledge Management](#literature--knowledge-management) -34. [Regulatory & Quality Management](#regulatory--quality-management) -35. [Scientific Communication & Tooling](#scientific-communication--tooling) +11. [Preclinical In Vivo Studies & Animal Welfare](#preclinical-in-vivo-studies--animal-welfare) +12. [Agricultural Genomics](#agricultural-genomics) +13. [Neuroscience & Brain Imaging](#neuroscience--brain-imaging) +14. [Environmental Microbiology](#environmental-microbiology) +15. [Infectious Disease Research](#infectious-disease-research) +16. [Multi-Omics Integration](#multi-omics-integration) +17. [Regulatory Genomics & Variant-to-Function](#regulatory-genomics--variant-to-function) +18. [Experimental Physics & Data Analysis](#experimental-physics--data-analysis) +19. [Chemical Engineering & Process Optimization](#chemical-engineering--process-optimization) +20. [Fluid Mechanics & Bioprocess Engineering](#fluid-mechanics--bioprocess-engineering) +21. [Scientific Illustration & Visual Communication](#scientific-illustration--visual-communication) +22. [Quantum Computing for Chemistry](#quantum-computing-for-chemistry) +23. [Open Quantum Systems & Cross-Framework Benchmarking](#open-quantum-systems--cross-framework-benchmarking) +24. [Research Grant Writing](#research-grant-writing) +25. [Flow Cytometry & Immunophenotyping](#flow-cytometry--immunophenotyping) +26. [Geospatial & Earth Observation](#geospatial--earth-observation) +27. [Time-Series Forecasting & Sensor Analytics](#time-series-forecasting--sensor-analytics) +28. [Cloud-Scale Bioinformatics](#cloud-scale-bioinformatics) +29. [Functional Genomics & Knowledge Graphs](#functional-genomics--knowledge-graphs) +30. [Molecular Modeling & Simulation](#molecular-modeling--simulation) +31. [Protein Engineering & Cloud Wet-Lab](#protein-engineering--cloud-wet-lab) +32. [Medical Imaging & Clinical AI](#medical-imaging--clinical-ai) +33. [Research Ideation & Study Planning](#research-ideation--study-planning) +34. [Literature & Knowledge Management](#literature--knowledge-management) +35. [Regulatory & Quality Management](#regulatory--quality-management) +36. [Scientific Communication & Tooling](#scientific-communication--tooling) --- @@ -1949,6 +1950,131 @@ Expected Output: --- +### Example 42: Virtual Spatial Transcriptomics from Archival H&E Slides + +**Objective**: Predict transcriptome-wide spatial expression across an archival H&E cohort that was never assayed spatially, and establish โ€” before using it โ€” how much of the predicted signal is biology rather than model prior. Output is model prediction, not measurement. + +**Disciplines**: computational pathology ยท spatial transcriptomics ยท machine learning ยท tumour biology ยท research licensing + +**Starting prompt**: + +```text +Use the histolab, deepspot-m, anndata, scanpy, pathway-enrichment, +scientific-visualization, and scientific-writing skills. Slides stay in +approved storage. Noncommercial research use only. + +Goal: a tiles-by-genes virtual expression map per slide, plus an honest +statement of what it can and cannot support. +Criteria: tiles must be 224x224 RGB at ~0.5 um/px, taken from the pyramid +level nearest that resolution โ€” not resampled from a coarser one. Every +queried symbol must be in model.gene_names. +Deliver: per-slide AnnData with tile coordinates, log1p-CPM values, the +embedding source used, and the gene list; marker maps; a concordance +analysis against something independently known about each slide. +Report: label every value as predicted, never measured. Give the agreement +between at least two embedding sources for the genes you draw conclusions +from, and name the genes where they disagree. +Do not: treat a predicted expression map as a spatial assay result, or use +the model or its outputs commercially. +``` + +**Skills Used**: +- `histolab` - Grid tiling of whole slide images with coordinates retained +- `deepspot-m` - Virtual spatial transcriptomics from H&E tiles (log1p-CPM, ~19k-gene panel) +- `anndata` - Tiles-by-genes matrix with spatial coordinates in `.obsm` +- `scanpy` - Neighbourhood structure, clustering, and marker analysis on the predicted matrix +- `pathway-enrichment` - Enrichment of spatially coherent gene programmes +- `pathml` - Stain normalization and slide handling for the tiling stage +- `scientific-visualization` - Publication-quality & interactive visualization +- `statistical-analysis` - Patient-level inference on tile-derived quantities +- `scientific-writing` - Evidence-traceable research reports + +**Workflow**: + +```text +Step 1: Clear the licence and access gates before any compute +- DeepSpot-M code is PolyForm Noncommercial 1.0.0 and the weights are + CC-BY-NC-SA-4.0. Confirm the work โ€” and anything derived from the outputs โ€” + is noncommercial, and check both licences before redistributing a map +- Weights are gated: request access at the model page, then authenticate the + machine once with `huggingface-cli login` +- Record the model version, the embedding source, and the gene panel file that + ships with the weights; the panel defines what can be asked at all + +Step 2: Tile the slides at the resolution the model expects +- Use HistoLab to extract a grid of 224x224 RGB tiles, keeping each tile's (x, y) + coordinates โ€” the coordinates are what make the output spatial +- Extract from the pyramid level nearest 0.5 um/px. Resampling a coarser level to + 224x224 gives the right array shape and the wrong texture, and the backbone reads + texture; the shape check will pass and the result will be quietly wrong +- Filter background and artefact tiles before inference, not after +- Apply stain normalization consistently across slides, and record which method + +Step 3: Fix the gene list up front +- Query `model.gene_names` and intersect it with your genes of interest; an + unknown symbol raises KeyError naming the offending genes +- Resolve aliases to current HGNC symbols before the intersection, and report any + gene you wanted that the panel does not carry +- Keep the ordered gene list beside the output โ€” `predict_genes` returns values + aligned to the list you passed, and an unlabelled column is unusable + +Step 4: Run batched inference +- Stack processed tiles into batches and call `predict_genes` once per batch with + the same gene list; concatenate into a tiles-by-genes matrix +- Attach tile coordinates and assemble an AnnData object per slide, with the + embedding source, model version, and units (log1p-CPM) in `.uns` +- Import deepspotm inside the function that needs it and turn ImportError into a + message naming the install, the access request, and the login + +Step 5: Repeat under a second embedding source +- The gene router builds projections from one of five frozen embeddings (evo2, + orthrus, prott5, scgpt, apertus), each a different view of gene identity +- Rerun the same tiles under a second source and correlate the two maps per gene. + Genes where the sources disagree are genes the morphology does not constrain; + they are not evidence, and the disagreement is itself a reportable result + +Step 6: Establish what the map reproduces that you already knew +- Before any discovery claim, check the predictions against something independent: + an IHC-confirmed region, a pathologist annotation, matched bulk RNA-seq for the + same block, or a marker whose spatial pattern is obvious from morphology +- A model that recovers EPCAM in epithelium and CD3D in a lymphoid aggregate has + earned a little trust for that slide; one that does not has failed a positive + control, and no downstream analysis rescues it + +Step 7: Spatial analysis on the predicted matrix +- Build a spatial neighbour graph from tile coordinates and cluster with Scanpy +- Run pathway-enrichment on genes that mark spatially coherent regions +- Do not test the genes that defined a cluster for enrichment in that same cluster + and report a p-value โ€” that is the double-dipping failure with a spatial coat on +- Treat compartment fractions as compositional: one region expanding forces the + others down + +Step 8: Get the unit of analysis right for any cohort claim +- Tiles within a slide are heavily correlated, and slides within a patient more so. + Aggregate to the patient before comparing groups, and bootstrap at the patient + level +- Break results down by site, scanner, and stain batch as in Example 11. A model + reading stain signature will produce spatially smooth, entirely artefactual maps + +Step 9: Report +- Per-slide virtual expression maps with the gene list, source, and units labelled +- Two-source agreement table and the genes that failed it +- Positive-control results, per-site breakdown, and patient-level intervals +- A limitations section stating plainly that these are predictions from morphology, + that the released panel bounds what could be asked, and that a spatial assay is + the only thing that measures spatial expression + +Expected Output: +- One AnnData per slide: tiles x genes, log1p-CPM, coordinates, provenance in .uns +- Marker and region maps with explicit "predicted" labelling +- Cross-source concordance analysis +- Enrichment results for spatially coherent programmes +- Research report bounded to noncommercial use with prediction-vs-measurement + stated in the methods and again in the conclusions +``` + +--- + ## Lab Automation & Protocol Design ### Example 12: Automated High-Throughput Screening Protocol @@ -2137,6 +2263,312 @@ Expected Output: --- +### Example 43: A Custom Carrier That Has to Fit Labware You Did Not Design + +**Objective**: Design a fabrication-ready part that mates with a standardized microplate on one face and a vendor imaging stage on the other, verify both interfaces before anything is cut, and hand the geometry to the automation layer as a deck resource. No fabrication or robot motion without explicit trained-operator authorization. + +**Disciplines**: mechanical design ยท laboratory automation ยท metrology ยท assay biology ยท manufacturing process selection + +**Starting prompt**: + +```text +Use the lab-hardware-cad, uncertainty-and-units, pylabrobot, +protocolsio-integration, scientific-schematics, and scientific-writing skills. + +Goal: a temperature-tolerant carrier holding one SLAS-footprint microplate, +bolting to our imaging stage, printable in-house this week. +Data: stage bolt pattern is on the vendor drawing at docs/stage.pdf; I +measured 40.0 mm centres. Plate footprint comes from the standard. +Criteria: every interface dimension must carry its source โ€” a standard ID, a +vendor drawing, or my measurement. Do not write one from memory; if it is not +in the standards database or the family reference, ask me for the drawing. +Deliver: parametric model source, STEP, STL, the manifest, a passing +interface check, and a rendered snapshot I can look at. +Report: the tolerance stack-up for the plate pocket, and what fraction of +conforming plates the pocket accepts. +Do not: send anything to a printer, or move any instrument. +``` + +**Skills Used**: +- `lab-hardware-cad` - Parametric build123d modelling, standards lookup, interface checking, STEP/STL/DXF export +- `uncertainty-and-units` - Tolerance stack-up and unit discipline across the dimension chain +- `pylabrobot` - Offline deck-layout planning with the new carrier as a resource +- `opentrons-integration` - Reviewed protocol planning where the carrier sits on an OT deck +- `protocolsio-integration` - Recording the assembly and use procedure +- `scientific-schematics` - Assembly and interface drawings for the build package +- `scientific-writing` - Fabrication package documentation + +**Workflow**: + +```text +Step 1: Route to exactly one device family +- Classify the part and load one family reference โ€” labware adapters here, not + microfluidics, optomechanics, or behaviour rigs. Loading all four mixes + conventions and is a reliable source of error +- Where a part genuinely spans two families, load the family that owns the + critical interface and read only the interface section of the second +- State the routing decision in the response, so the reader knows which + conventions the dimensions follow + +Step 2: Write down every interface before writing any geometry +- For each mating interface record three things: the source of the dimension (a + published standard ID, a vendor drawing, or a user measurement), the nominal + value with its tolerance, and the intended clearance with the reason for it +- Look the numbers up: `check.py standards --list`, then + `check.py standards --show slas-microplate-footprint`. Never write an interface + dimension from memory โ€” a guessed interface number is the most expensive + failure this workflow has +- If a number is in neither the standards database nor the family reference, stop + and ask for the drawing or the measurement. "Approximately" is not a dimension +- Size a feature that must receive a standardized component against that + component's maximum material condition โ€” nominal plus its plus-tolerance โ€” and + add clearance to that. A pocket sized from nominal accepts only the smaller half + of conforming plates, which is why this appears in the report + +Step 3: Choose the process and material before the geometry +- Read the fabrication-limits reference: process sets minimum wall, minimum + feature, achievable tolerance, autoclavability, and solvent compatibility +- FDM will not hold ยฑ0.05 mm; SLA resin is not safe for cell contact without + post-cure and testing. Pick the process the tolerance budget actually permits, + rather than designing first and discovering the budget afterwards +- Record process, material, and layer height in the model docstring + +Step 4: Author the model as parametric source +- The source is the authoritative artifact. Never hand-edit an exported STEP and + never regenerate a model from a mesh +- Every changeable dimension is a module-level named constant with units in the + name, split into an INTERFACE block (fixed by a standard, annotated with its ID) + and a DESIGN block (free choices) +- Derive computed dimensions inside functions, not at module level, so `--param` + overrides actually propagate +- Declare `interfaces()` returning each dimension the part must fit, with its + standard ID and intent. This is what makes step 5 a check rather than an opinion +- Treat model files as executable: `gen.py` imports and runs them. Only run models + authored in this session or supplied from a trusted location, and read any file + from elsewhere before running it โ€” then say that you did + +Step 5: Generate, then check the interfaces +- `gen.py` writes the STEP (authoritative), the STL (preview and printing), and a + manifest recording source hash, resolved parameters, declared interfaces, library + versions, and measured bounding box, volume, and validity. Keep the manifest with + the artifact; it is the provenance record +- `check.py facts` must report `is_valid: true`. Broken geometry gets fixed in the + source, not patched downstream +- `check.py interfaces` is the gate on fabrication. Use it rather than `check.py + fit` for anything internal: a pocket, bore, or slot does not appear in the outer + bounding box, so `fit` on a carrier measures the outside of the walls and fails + against the plate footprint for a part that is entirely correct + +Step 6: Look at it +- Render a snapshot and inspect it. A numeric pass does not waive this โ€” a model + can satisfy every declared interface and still have the pocket in the wrong face, + a boss where the plate skirt lands, or a wall that closes over a bolt hole +- Check clearance for the plate skirt, lid, and any gripper or pipette approach + +Step 7: Close the tolerance argument in units +- Use uncertainty-and-units to stack the chain: plate tolerance, print tolerance, + thermal expansion of the chosen polymer over the assay temperature range, and + the stage bolt-pattern uncertainty +- Report the fraction of conforming plates the pocket accepts, not just a + pass/fail. A part that fits the plate on the bench and jams on 30% of the lot is + a part that has not been checked +- Millimetres throughout, and state it; a mixed-unit dimension chain fails silently + +Step 8: Wire the part into the automation layer, offline +- Define the carrier as a resource in a PyLabRobot deck layout and simulate the + moves that touch it โ€” approach heights, gripper clearance, plate seating +- Keep this in offline planning mode. Simulation clearing is not authorization to + move hardware; a physical trial needs a trained operator and explicit approval + +Step 9: Build the fabrication package +- STEP as the authoritative artifact, STL for the printer, manifest for provenance, + the model source as the design of record +- Assembly and interface drawings via scientific-schematics, dimensioned to the + same sources named in step 2 +- Print orientation, support strategy, post-processing, and the cleaning or + autoclave procedure that the material actually tolerates +- Record the assembly and use procedure with protocolsio-integration as a reviewed + draft; publish only with explicit authorization + +Expected Output: +- Parametric model source with INTERFACE/DESIGN separation and an interfaces() contract +- STEP, STL, and manifest, with a passing facts and interfaces check +- Rendered snapshots reviewed visually +- Tolerance stack-up with the accepted-plate fraction stated +- Offline PyLabRobot deck layout including the carrier, with no hardware action taken +- Fabrication package: process, material, orientation, post-processing, drawings +``` + +--- + +## Preclinical In Vivo Studies & Animal Welfare + +### Example 44: Multivariate Severity Scoring and Humane-Endpoint Forecasting + +**Objective**: Combine several welfare readouts into one severity score per animal per day, forecast which animals are heading toward a humane endpoint early enough to act, and write the severity-assessment section of a refinement report โ€” without either output becoming a decision rule. + +**Disciplines**: laboratory animal science ยท welfare biology ยท time-series statistics ยท physiological signal processing ยท research ethics and regulatory reporting + +**Starting prompt**: + +```text +Use the relsa-severity-assessment, experimental-design, statistical-power, +neurokit2, statistical-analysis, scientific-visualization, and +scientific-writing skills. + +Goal: one severity score per animal per day across the cohort, plus a +next-day forecast with an interval for the animals still on study. +Data: daily body weight, body temperature, a 0-8 clinical score, an IL-6 +readout, and telemetry. This is a sepsis model โ€” temperature falls. +Criteria: state the directionality of every variable and why. Use the +endpoint-reaching group as the reference set and save it, so later cohorts +stay on the same scale. Score only variables measured throughout. +Deliver: per-animal RELSA trajectories with each variable's weight, forecasts +with RMSE, PICP and MPIW together, and KDE zone candidates with a bandwidth +sweep. +Report: the reference-set table, so the scale is auditable. Flag any variable +whose max reached sits on the wrong side of 100 for its declared direction. +Do not: present a zone as a regulatory severity grading, or a forecast as a +euthanasia decision. +``` + +**Skills Used**: +- `relsa-severity-assessment` - RELSA scoring, ARIMA endpoint forecasting, KDE severity zones +- `experimental-design` - Which readouts, at what frequency, decided before the study runs +- `statistical-power` - Cohort size for the group comparison the severity data is meant to support +- `neurokit2` - Heart rate and HRV features from telemetry, averaged to the scoring interval +- `statsmodels` - ARIMA fitting and diagnostics behind the forecast +- `statistical-analysis` - Group comparison with the animal as the unit of analysis +- `scientific-visualization` - Trajectory, forecast-interval, and density plots +- `scientific-writing` - Severity-assessment and 3Rs/refinement reporting + +**Workflow**: + +```text +Step 1: Decide the monitoring scheme before the study, not after +- Use experimental-design to fix which readouts are collected, at what frequency, + and for how long โ€” including the baseline window. A variable that appears + mid-study cannot enter a severity score cleanly +- Use statistical-power for the comparison the welfare data is meant to support, + at the level of the animal +- Keep the prospective severity classification required by your authorization + separate from anything computed here; RELSA does not replace it, and the humane + endpoint criteria actually applied to the study are recorded independently + +Step 2: Assemble one row per animal per time point +- Columns: id, time, the outcome measures, and optional treatment/condition labels. + The RELSA convention codes the baseline time point as -1 +- Derive telemetry features with NeuroKit2 โ€” heart rate, HRV โ€” and average them to + one value per scoring interval, as the published models do. Sum activity rather + than averaging it +- Leave missing measurements empty. They are dropped from the score, never imputed; + a missing value silently treated as "no deviation" biases severity downward, + which is the dangerous direction + +Step 3: Make the four decisions that determine the result, explicitly +- Directionality: falling is the default (weight, activity, food intake, + burrowing). Variables that rise under worsening must be declared as turned โ€” + clinical scores, inflammatory biomarkers, fever, tachycardia. Get it wrong and + the variable contributes exactly zero, silently, because deviations the "wrong" + way are floored. Body temperature is model-dependent: it falls in sepsis and + endotoxaemia, rises in fever models, and no property of the data settles it +- Reference set: RELSA is relative by construction and means nothing without one. + Use the group assumed to carry the greatest burden โ€” typically the highest-dose + or endpoint-reaching group. Too mild a reference pushes every score above 1; too + severe compresses everything toward 0. Save it and reuse it for later cohorts +- Zero-baseline ordinal scores: a clinical score of 0 in a healthy animal makes the + ratio undefined. Map the score's scale instead (healthy to 100%, worst possible + to 200%), which also marks it turned โ€” and state that this is a modelling choice + about what one score point is worth against one percent of body weight. The + alternative is to keep the score out of RELSA and use it as an independent + endpoint criterion +- Constant composition: the score averages over whichever variables are available, + so a variable that appears or disappears mid-trajectory moves the score by itself. + Score the variables present throughout, and heed the composition-change warning + +Step 4: Compute the scores and audit the scale +- Run the scoring with the declared variables, normalizations, turned list, score + mapping, baseline time, and reference group; save the reference model +- Read the echoed reference table rather than skipping to the scores. For a falling + variable `max reached` should be below 100, for a turned one above it, and + `max delta` should be a plausible magnitude for that measure. A variable that + fails this test has its direction declared backwards +- Keep the per-variable weights alongside each score โ€” they are what makes a score + explainable, and a weight of 1.00 means that variable hit the reference maximum +- Do not normalize something already on a percent scale; that flattens it + +Step 5: Forecast the animals still on study +- Fit ARIMA per animal on the trajectory up to the time point before the endpoint + and predict the next score with a 95% interval; use rolling one-step-ahead mode + for live monitoring +- Report RMSE, PICP, and MPIW together, always. A model reaches PICP = 100% by + making the interval so wide it says nothing, and MPIW in RELSA units is what + exposes that โ€” a published row with PICP 100% and MPIW 7.35 covers 735% of the + scale +- Interpolation is on by default because daily sampling is far too sparse for + ARIMA; it buys model selection and narrower intervals at the cost of honest + uncertainty. Turn it off when measurement frequency allows, and state which +- ARIMA assumes stationarity and linearity and therefore cannot predict a cliff: + an abrupt collapse in the last hours will not be forecast from a smooth prior + trajectory. Act on the upper bound of the interval, and never let a low forecast + override an animal that looks unwell + +Step 6: Put scores in context with severity zones +- Estimate the score density and take thresholds at its minima โ€” the sparse valleys + between clusters. Include endpoint animals, survivors, and shams; the zones exist + to separate those states, so all of them must be represented +- Check the bandwidth before believing a threshold. On the published sepsis data a + 10% larger bandwidth removes both minima entirely. Run the sensitivity sweep and + report the sweep, not a bare pair of numbers +- An empty threshold list is a legitimate result: the scores form one cluster and + there is no data-driven place to cut + +Step 7: Compare groups at the right unit of analysis +- The animal is the unit, not the animal-day. Repeated daily scores from one animal + are not independent observations; use a model that accounts for the repeated + measures, and report n as animals +- Report the trajectory shape, not only a peak: an animal peaking at 0.53 on day 3 + and recovering to 0.11 by day 7 is a different welfare story from one climbing + monotonically to 1.00, and a single summary statistic erases the difference + +Step 8: Report against the checklist +- Outcome measures with units and declared directionality, and why +- Baseline time point or window, and which variables were normalized +- Any ordinal score mapping, with its scale +- The reference set: which animals, which group, how many, and why they are assumed + to carry the greatest burden +- The humane endpoint criteria actually applied in the study, stated separately +- For forecasts: interpolation step, selected ARIMA order per animal, and RMSE, + PICP and MPIW +- For thresholds: bandwidth, number of scores, and the sensitivity sweep +- Software versions, plus the explicit statements that follow + +Step 9: State the boundaries in the report itself +- RELSA is an aid to severity assessment, not a decisive parameter. An animal with + a low score that shows other signs of distress is handled accordingly +- Neither procedure is a validated predictor of death +- KDE zones are not regulatory severity gradings; EU Directive 2010/63/EU + categories are assigned prospectively by a different process and the published + work is explicit that its thresholds do not translate to them +- Scores are not comparable across reference sets, models, or laboratories โ€” + always report the reference set with the score +- The published evidence is a proof of concept: 13 animals across seven models, + several rows resting on one or two animals +- An underestimated score is the dangerous error, because it discourages attention + and can delay a decision; an overestimate merely prompts extra care + +Expected Output: +- Per-animal RELSA trajectories with per-variable weights and n_vars per time point +- A saved reference model, so later cohorts sit on the same scale +- Endpoint forecasts with intervals, reported with RMSE, PICP and MPIW together +- Candidate attention/danger zones with a bandwidth sensitivity sweep +- Animal-level group comparison with repeated measures handled +- A severity-assessment section for a 3Rs/refinement or welfare report, with the + boundaries above stated in it rather than in a footnote +``` + +--- + ## Agricultural Genomics ### Example 13: GWAS for Crop Yield Improvement @@ -5002,6 +5434,135 @@ Expected Output: --- +### Example 45: Checking a Mechanistic Hypothesis Against a Federated Knowledge Graph + +**Objective**: Take a mechanism proposed by an internal analysis and establish what a public biomedical knowledge graph does and does not assert about it, with every returned edge traceable to a knowledge source โ€” then verify the parts that matter outside the graph. Nothing proprietary or patient-specific is submitted. + +**Disciplines**: knowledge representation ยท pharmacology ยท biocuration ยท evidence appraisal ยท research information security + +**Starting prompt**: + +```text +Use the ncats-arax, ontology-term-resolution, primekg, database-lookup, +paper-lookup, scientific-critical-thinking, and scientific-writing skills. + +Goal: what the public graph asserts about the drug-gene-disease chain we +proposed, with provenance for each assertion. +Data: the hypothesis is a published-target question and is safe to send to a +public service. Nothing from the internal programme goes into a query. +Criteria: type both nodes with Biolink categories and pin at least one +endpoint. Two-hop queries get exactly one typed, unpinned intermediate. +Deliver: the exact TRAPI payloads, the bounded summaries, a table of edges +with predicate, qualifiers, publications, and knowledge source, and a short +appraisal of which edges are load-bearing. +Report: response order, not rank โ€” the service does not return one. A zero +means "not returned under these constraints", not "no relationship exists". +Do not: run a variant query after an empty result without telling me you are +doing it, or present a returned path as a validated mechanism. +``` + +**Skills Used**: +- `ncats-arax` - Bounded, typed, provenance-rich TRAPI queries against the NCATS Translator ARAX production API +- `ontology-term-resolution` - Mapping free text to reviewed identifiers before anything is queried +- `primekg` - Independent knowledge-graph cross-check from a different construction +- `database-lookup` - Verification against primary resources (Open Targets, DrugBank, ClinVar, UniProt) +- `paper-lookup` - Retrieving and reading the publications an edge rests on +- `scientific-critical-thinking` - Structured appraisal of what the graph does and does not support +- `networkx` - Local analysis of the retrieved subgraph +- `scientific-writing` - Evidence-traceable write-up + +**Workflow**: + +```text +Step 1: Clear the confidentiality gate first +- Queries and caller metadata may be publicly visible even when storage is + declined. Confirm the question is a public, nonsensitive research question +- Nothing patient-specific, no confidential research questions, no unpublished + compound programmes, no proprietary target hypotheses. If the real question is + proprietary, reformulate it as a published-entity question or do not send it +- Acknowledge the public nature of the query explicitly, and choose a new or empty + output directory so artifacts from different queries do not blend + +Step 2: Preflight the service without asking a biomedical question +- Verify the endpoint identifies itself as ARAX, exposes /query, and reports a + supported TRAPI version +- A nonproduction endpoint or an untested TRAPI series requires an explicit + override, and no override changes the fixed query shapes or operations + +Step 3: Resolve entities to identifiers, as a separate reviewed step +- Use ontology-term-resolution on the free text, then normalize through the client + with the expected Biolink category +- Normalization is review-only and triggers no graph query. Read the canonical + identifier, name, category, and synonym preview, and report every CURIE and + category regardless of what happens next +- A category warning or a zero result is a reason to curate the identifier by hand, + not to chain automatically into a query with a doubtful CURIE + +Step 4: Ask the one-hop question with both nodes typed +- Pin at least one endpoint, give both nodes Biolink categories, and state the + predicate. Add qualifiers where the direction of effect is the point โ€” + activity_or_abundance decreased is a different claim from increased, and an + unqualified predicate collapses them +- Default lookup mode fixes expansion to RTX-KG2 and returns 20 results; the hard + cap is 50 in either mode + +Step 5: Ask the two-hop question with both endpoints pinned +- Exactly one typed, unpinned intermediate node โ€” this is a constrained check of a + specific chain, not open-ended pathfinding +- Right-first expansion is the default. If an empty result genuinely merits another + attempt, run left-first as a new, separately recorded query. Do not silently + change provider selection or expansion order after a failure and present the + second run as the first + +Step 6: Federate only when you have a reason and can name the providers +- Federation is explicit and takes two to five named providers; it defaults to the + 50-result cap +- Provider errors can coexist with useful results. Such a run is marked partial and + exits non-zero after retaining its artifacts โ€” report it as partial rather than + as a clean negative + +Step 7: Read the provenance, not the ordering +- Inspect the bounded summary for query-edge bindings and provenance, and the saved + TRAPI payload for the exact exchange +- Build the edge table: subject, predicate, qualifiers, object, supporting + publications, and knowledge source for each +- Position in the response is unscored response order. Describe it that way; calling + it a rank invents a confidence the service never expressed +- A zero result is "not returned under these constraints". Recording the constraints + is what makes that statement useful and what stops it being read as evidence of + absence + +Step 8: Verify the load-bearing edges outside the graph +- Cross-check the same chain in PrimeKG, which is built differently โ€” agreement + between two graphs that share an upstream source is not independent replication, + so check what each one's source actually was +- Pull the cited publications with paper-lookup and read them. A knowledge-graph + edge frequently rests on a single sentence in a review that was itself citing + something else, and the assertion can be weaker than its presence implies +- Confirm entity-level facts against primary resources with database-lookup + +Step 9: Appraise and report +- Use scientific-critical-thinking to separate three things: what the graph asserts, + what the underlying literature supports, and what your hypothesis needs. They are + rarely the same set +- Analyse the retrieved subgraph locally with NetworkX if structure matters, and + keep it labelled as retrieved rather than inferred +- Write up with the CURIEs, categories, predicates, qualifiers, constraints, dates, + and knowledge sources attached to every claim, and state plainly that a returned + path is a candidate for verification, not a validated mechanism or clinical + guidance + +Expected Output: +- Reviewed CURIE/category table for every entity, produced before any graph query +- Exact TRAPI payloads and bounded summaries per query, in separate directories +- Edge table with predicates, qualifiers, publications, and knowledge sources +- Independent verification notes from PrimeKG, primary databases, and the papers +- An appraisal separating graph assertion, literature support, and hypothesis need, + with constraints recorded for every negative result +``` + +--- + ## Molecular Modeling & Simulation ### Example 31: Molecular Dynamics and Binding Free Energy for Lead Optimization @@ -5979,8 +6540,9 @@ Metabolomics Workbench and more) ยท `paper-lookup` (PubMed, PMC, bioRxiv, medRxi OpenAlex, Crossref, Semantic Scholar, CORE, Unpaywall) **Specialist data sources** -`cellxgene-census` ยท `depmap` ยท `primekg` ยท `imaging-data-commons` ยท `onekgpd` ยท -`genomic-intelligence` ยท `pathogen-variant-surveillance` ยท `usfiscaldata` ยท `bioservices` +`cellxgene-census` ยท `depmap` ยท `primekg` ยท `ncats-arax` ยท `imaging-data-commons` ยท +`onekgpd` ยท `genomic-intelligence` ยท `pathogen-variant-surveillance` ยท `usfiscaldata` ยท +`bioservices` **Cheminformatics & drug discovery** `rdkit` ยท `datamol` ยท `medchem` ยท `molfeat` ยท `deepchem` ยท `torchdrug` ยท `pytdc` ยท @@ -6033,12 +6595,15 @@ OpenAlex, Crossref, Semantic Scholar, CORE, Unpaywall) `bids` ยท `neurokit2` ยท `neuropixels-analysis` **Imaging, pathology & cytometry** -`histolab` ยท `pathml` ยท `pydicom` ยท `omero-integration` ยท `flowio` +`histolab` ยท `pathml` ยท `deepspot-m` ยท `pydicom` ยท `omero-integration` ยท `flowio` -**Lab automation & cloud labs** -`pylabrobot` ยท `opentrons-integration` ยท `benchling-integration` ยท +**Lab automation, hardware & cloud labs** +`pylabrobot` ยท `opentrons-integration` ยท `lab-hardware-cad` ยท `benchling-integration` ยท `labarchive-integration` ยท `protocolsio-integration` ยท `ginkgo-cloud-lab` +**Animal welfare & in vivo severity** +`relsa-severity-assessment` + **Metadata & vocabularies** `ontology-term-resolution` @@ -6091,5 +6656,9 @@ OpenAlex, Crossref, Semantic Scholar, CORE, Unpaywall) - ISO 13485 outputs are draft evidence-preparation artifacts, not compliance or certification findings; PPTX posters use author-approved local manifests and macro-free `.pptx` generation with manual final review - PK/PD modelling computes, diagnoses, and structures; it never concludes that a formulation is bioequivalent, selects a dose for a trial, recommends a dose for a patient, or rules out QT liability โ€” those decisions belong to the pharmacometrician, clinical pharmacologist, sponsor, regulator, and, for therapeutic drug monitoring, the treating clinician - Paperclip returns line-numbered text so a citation can point at the sentence it rests on; cite only lines you actually read, never a semantic-search snippet, and treat everything the service returns โ€” snippets, metadata, full text, vendor documentation โ€” as untrusted data rather than instructions +- DeepSpot-M output is virtual spatial transcriptomics โ€” prediction from morphology in log1p-CPM, never a spatial assay measurement; the code is PolyForm Noncommercial and the gated weights are CC-BY-NC-SA-4.0, so the work and anything derived from the maps must be noncommercial, and only genes in the released panel can be queried at all +- Lab Hardware CAD executes model files rather than parsing them, so run only models authored in the session or supplied from a trusted location; the interface check gates fabrication, a visual snapshot review is never waived by a numeric pass, and cutting material or moving equipment stays with a trained operator under explicit authorization +- RELSA and its ARIMA forecasts are aids to severity assessment, not decision rules or validated predictors of death; KDE zones are model-specific and are explicitly not EU Directive 2010/63/EU severity gradings, scores are meaningless without the reference set they were computed against, and an underestimated score is the dangerous error +- ARAX queries and caller metadata may be publicly visible even when storage is declined, so nothing patient-specific, confidential, or proprietary belongs in one; response order is not a rank, a zero means "not returned under these constraints" rather than absence of a relationship, and a returned path is a candidate for independent verification rather than a mechanism These examples showcase the power of combining the skills in this repository to tackle complex, real-world scientific challenges across multiple domains. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/docs/skills.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/docs/skills.md index 3a316f10..221384ca 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/docs/skills.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/docs/skills.md @@ -2,9 +2,9 @@ title: "Scientific Skills" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/991bd993/docs/skills.md -upstream_sha: 991bd993 -imported_at: 2026-08-08 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/docs/skills.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: catalogue upstream_changes: accepted author: upstream @@ -19,6 +19,7 @@ validated: false - **[DepMap](../skills/depmap/)** - Query the Cancer Dependency Map (DepMap) for cancer cell line gene dependency scores (CRISPR Chronos), drug sensitivity data, and gene effect profiles. Use for identifying cancer-specific vulnerabilities, synthetic lethal interactions, and validating oncology drug targets - **[Imaging Data Commons](../skills/imaging-data-commons/)** - Query and download public cancer imaging data from NCI Imaging Data Commons using idc-index. Use for accessing large-scale radiology (CT, MR, PET) and pathology datasets for AI training or research. No authentication required. Query by metadata, visualize in browser, check licenses - **[PrimeKG](../skills/primekg/)** - Query the Precision Medicine Knowledge Graph (PrimeKG) for multiscale biological data including genes, drugs, diseases, phenotypes, and more. Integrates 20+ biomedical resources into a single knowledge graph for drug repurposing, disease mechanism exploration, and target identification +- **[NCATS ARAX](../skills/ncats-arax/)** - Query the NCATS Biomedical Data Translator ARAX production API for bounded, typed, provenance-rich biomedical knowledge-graph relationships. Supports Biolink-constrained one-hop lookups over RTX-KG2, endpoint-pinned two-hop traversal, explicit selection of two to five ARAX federation providers, separate entity normalization of free text to canonical CURIEs before any graph query, and qualifier-aware edges. The bundled standard-library client (`arax_client.py`, Python 3.10+, no API key) runs a `preflight` check against the production OpenAPI, performs review-only `normalize` calls, and saves both a bounded `summary.json` of query-edge bindings, publications, and knowledge-source provenance and the exact TRAPI `response.json`. Queries and caller metadata may be publicly visible, so the skill is for public, nonsensitive research questions only; returned paths are candidates to verify against literature and authoritative databases, response order is not a rank, and a zero result means "not returned under these constraints" rather than absence of a relationship. Not for inference, ranking, open-ended pathfinding, or clinical guidance - **[U.S. Treasury Fiscal Data](../skills/usfiscaldata/)** - Free, open REST API from the U.S. Department of the Treasury providing 54 datasets and 179 data tables covering federal fiscal data. No API key required. Access national debt (Debt to the Penny back to 1993, Historical Debt back to 1790), Daily Treasury Statements (TGA balances, deposits/withdrawals), Monthly Treasury Statements (federal budget receipts and outlays), Treasury securities auctions data (bills, notes, bonds, TIPS, FRNs since 1979), average interest rates on Treasury securities, Treasury reporting exchange rates (quarterly for 170+ currencies), I Bond and savings bond rates, TIPS/CPI data, and more. Supports filtering, sorting, pagination, and CSV/XML/JSON output formats - **[Ontology Term Resolution](../skills/ontology-term-resolution/)** - Resolve free-text scientific labels to ontology term IDs and validate existing CURIEs against the EBI Ontology Lookup Service (OLS4). Annotate tissue, cell type, disease, phenotype, assay, chemical, organism, sex, and developmental-stage fields; prepare metadata for GEO, ENA, BioSamples, CELLxGENE, HCA, or ISA-Tab submission; audit a metadata table of term IDs; check whether a term is obsolete and what replaced it; and map between ontologies (UBERON, CL, MONDO, HPO, EFO, ChEBI, NCBITaxon, GO, PATO). Python 3.11+ standard-library scripts with no third-party packages; needs public network access to the OLS4 API, no API key - **[Pathogen Variant Surveillance](../skills/pathogen-variant-surveillance/)** - Query live pathogen genomic surveillance data through the GenSpectrum LAPIS API to find which viral lineages are circulating now, how fast they are growing, and what mutations they carry. Covers 15 instances spanning SARS-CoV-2 (open GenBank data via CoV-Spectrum), influenza A including H5N1 and the seasonal H3N2/H1N1pdm clades, and the Pathoplexus organisms โ€” RSV-A/B, mpox, measles, dengue, West Nile, hMPV, Ebola Zaire/Sudan, and CCHF. Field names, lineage columns, and date columns are read from each instance's live schema rather than assumed, because they differ: `dateFrom` is correct on SARS-CoV-2 and a hard error on H5N1. Lineage names are resolved against the live pango-designation nomenclature, which catches the withdrawn and redesignated names that make remembered lineage facts actively wrong rather than merely stale, and the recombinant parentage that the query API does not carry. Bundled Python 3.11+ scripts are standard-library only and need no API key: `resolve_lineage.py` (is this name still valid, what does it expand to), `lineage_prevalence.py` (discovers the most common lineages in a window with `--top`, then reports weekly prevalence with Wilson intervals, coverage flags, and a dispersion-guarded log-odds growth fit), `mutation_profile.py` (defining mutations, or a diff between two lineages, for assay-match questions), and `reporting_lag.py` (measures how long sequences take to arrive and derives a trust cutoff). Use cases: variant situation reports, vaccine and assay target monitoring, checking whether a lineage in a manuscript is still designated, H5N1 clade tracking by host and region, and any question whose answer changed after the model was trained. Sequence counts are not case counts, and outputs are surveillance research, not clinical or public-health guidance @@ -98,6 +99,9 @@ validated: false ### Pharmacology & Pharmacometrics - **[PK/PD Modelling](../skills/pkpd-modeling/)** - Pharmacokinetic and pharmacodynamic modelling and simulation across the full development path: non-compartmental analysis, compartmental fitting and model selection, population PK dataset preparation, regimen simulation, exposure-response, bioequivalence, allometric scaling and first-in-human dose, static drug-interaction prediction, and Bayesian therapeutic drug monitoring. Nine Python 3.11+ scripts (numpy and scipy, network-free, no proprietary software) each make the choices that decide the answer explicit rather than implicit: `nca.py` requires the AUC method, BLQ rule, and lambda_z window up front and selects the terminal phase by *adjusted* r-squared with Tmax excluded, then flags excessive AUCinf extrapolation and a terminal window shorter than two half-lives; `fit_compartmental.py` estimates on the log scale and compares models by AIC, BIC, and F test together โ€” they disagree, and it reports per-parameter RSE and correlations because convergence is not identifiability โ€” while separating structural misspecification (a residual runs test) from a wrong error model; `check_popk_dataset.py` catches the NM-TRAN defects that never stop a run (non-numeric DV read as zero, a blank covariate read as 0 kg, `ADDL` without `II`, duplicate timestamps applied in file order); `simulate_regimen.py` reports population target attainment rather than the typical patient, with analytical superposition for linear models and integrated Michaelis-Menten where superposition is invalid; `exposure_response.py` fits Emax/sigmoid models, flags a plateau outside the observed data, and evaluates concentration-QTc against the ICH E14 10 ms threshold using the upper bound of the 90% CI; `bioequivalence.py` keeps average BE, EMA ABEL, and FDA RSABE strictly apart and refuses reference-scaling on a 2x2 design, with sample size integrated over the sampling distribution of the SD; `allometry_and_fih.py` adds Anderson-Holford maturation below 20 kg and pairs a NOAEL-derived MRSD with MABEL for immunomodulators; `ddi_static.py` applies the ICH M12 basic and mechanistic static models with their cut-offs and the fm-implied ceiling; and `tdm_bayes.py` performs MAP Bayesian individualisation, flagging a single level as unable to separate clearance from volume. Fourteen reference files cover popPK estimation and BLQ methods, PBPK, TMDD and biologics, special populations, dataset standards, regulatory guidance, and the software ecosystem (Pharmpy, NONMEM, nlmixr2, Monolix, Simcyp, GastroPlus โ€” oriented towards, never invoked). The skill computes, diagnoses, and structures; it does not conclude bioequivalence, select a trial dose, recommend a dose for a patient, rule out QT liability, or replace a qualified pharmacometrician, clinical pharmacologist, or the regulatory review +### Preclinical Research & Animal Welfare +- **[RELSA Severity Assessment](../skills/relsa-severity-assessment/)** - Multivariate severity assessment and humane endpoint prediction for laboratory animal studies, implementing the RELSA (RELative Severity Assessment) score (Talbot et al., 2022) and ARIMA-based foRcast forecasting (Lutscher et al., 2026). Combines welfare readouts โ€” body weight, temperature, clinical or nesting scores, biomarkers, activity, heart rate, burrowing, wheel running โ€” into one score per animal per time point, expressed relative to a reference set of known burden (0 = baseline, 1 = the reference set's maximum deviation), with each variable's weight reported alongside the score so a score is explainable rather than opaque. Forecasts an individual animal's next score with a 95% prediction interval so animals heading for a humane endpoint can be flagged before they arrive, and derives candidate attention and danger zones on the RELSA scale by kernel density estimation. Three Python scripts (`relsa_score.py`, `forecast_relsa.py`, `kde_thresholds.py`; numpy/pandas/scipy, statsmodels for forecasting, matplotlib for figures, no network) make the four choices that actually determine the result explicit: variable directionality (a `--turned` variable that rises under worsening contributes nothing at all if misdeclared, silently, because deviations the wrong way are floored at zero โ€” and body temperature falls in sepsis but rises in fever models), the reference set that fixes the scale, how a clinical score with a zero baseline is mapped rather than ratio-normalized, and which variables are measured throughout, since changing composition moves the score by itself. Missing values are dropped, never imputed. Forecast quality is evaluated by RMSE, PICP, and MPIW. Use cases: severity scoring across a cohort, endpoint risk triage, comparing burden between treatment groups on a common relative scale, threshold and zone definition, and writing the severity-assessment section of a 3Rs/refinement analysis or an EU Directive 2010/63/EU application. Both procedures are aids to severity assessment, not decision rules + ### Proteomics & Mass Spectrometry - **[matchms](../skills/matchms/)** - Reproducible MS/MS processing and spectral-library search with metadata/peak filtering, cosine and exact/greedy modified-cosine scoring, fast BLINK and Flash modes, structured sparse scores, spectral networks, and MGF/MSP/mzML/mzXML/JSON/mzSpecLib/USI workflows - **[pyOpenMS](../skills/pyopenms/)** - Comprehensive mass spectrometry data analysis for proteomics and metabolomics (LC-MS/MS processing, peptide identification, feature detection, quantification, chemical calculations, and integration with search engines like Comet, Mascot, MSGF+) @@ -154,6 +158,7 @@ validated: false - **[Pymatgen](../skills/pymatgen/)** - Analyze, validate, convert, and transform structures and computed materials data with the current split stack: `pymatgen==2026.5.4`, `pymatgen-core==2026.7.16`, and `mp-api==0.46.4` on Python 3.11+. Covers provenance-preserving local phase diagrams, symmetry sensitivity, transformations, and electronic-structure I/O; Materials Project queries are explicitly bounded and require user-approved network access plus the named `MP_API_KEY` ### Engineering & Simulation +- **[Lab Hardware CAD](../skills/lab-hardware-cad/)** - Design custom laboratory hardware as parametric build123d 0.11.1 models and export fabrication-ready STEP, STL, and DXF: microfluidic chips and molds, optomechanical mounts and breadboard adapters, cuvette and microplate holders, tube racks, animal-behavior rigs, and 3D-printed instrument fixtures. The hard part is rarely the geometry โ€” it is that the part must mate with equipment whose dimensions are fixed by a published standard or vendor drawing โ€” so the workflow routes to exactly one device family reference (microfluidics, optomechanics, labware adapters, behavior rigs), requires every interface dimension to be looked up in the bundled `standards.json` (ANSI/SLAS microplate footprints, optical-table and cage-system patterns) rather than written from memory, sizes receiving features at the mating part's maximum material condition before clearance, and fixes the fabrication process and material first because process sets minimum wall, minimum feature, achievable tolerance, and autoclave/solvent compatibility. Models expose `build()` plus a machine-checkable `interfaces()` declaration with named unit-suffixed constants split into INTERFACE and DESIGN blocks; `gen.py` emits the STEP alongside a manifest recording source hash, resolved parameters, library versions, and measured bounding box, volume, and validity, `check.py facts`/`interfaces` gates fabrication against the standards database, and `snapshot.py` produces the required multi-view renders. Python 3.10-3.14; the standards lookup and interface check run on the standard library alone and no network access is needed. Model files are executed, not parsed, so only run sources authored in-session or from a trusted location. Not for FEA, CFD, molecular structure, or plotting - **[MATLAB/Octave](../skills/matlab/)** - Build, review, migrate, and safely plan numerical workflows against proprietary MATLAB R2026a or the distinct free GNU Octave 11.3.0 surface. Covers arrays, tables/timetables, tests, projects, graphics, MAT-file inventory, and explicit Python interoperability. Bundled Python 3.11+ tools are local static/dry-run helpers and never launch either runtime; do not assume product, toolbox, license, API, numerical, or graphics equivalence - **[FluidSim](../skills/fluidsim/)** - Plan, configure, inspect, restart, and analyze bounded FluidSim 0.9.0 pseudospectral CFD simulations, with the verified FluidFFT 0.4.5/pyFFTW 0.15.1 stack. Requires explicit equations, units, solver parameters, convergence tests, and CPU/RAM/disk/wall-time bounds; MPI/native FFT execution must follow an approved site scheduler/toolchain workflow. A completed or stable run is not proof of numerical convergence or physical validity - **[OpenPIV](../skills/openpiv/)** - Particle Image Velocimetry (PIV) analysis with OpenPIV 0.25.4 on Python 3.10+. Extract velocity fields from PIV image pairs by cross-correlating interrogation windows, validate and replace spurious vectors, and compute vorticity, strain rate, and turbulence statistics from measured velocity fields. Use for fluid-dynamics and flow-visualization experiments; numpy, scipy, scikit-image, and matplotlib arrive as dependencies, and no network access is needed after install diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/SKILL.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/SKILL.md new file mode 100644 index 00000000..a3734e1d --- /dev/null +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/SKILL.md @@ -0,0 +1,318 @@ +--- +lineage_type: import +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/lab-hardware-cad/SKILL.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 +prompt_class: catalogue +upstream_changes: accepted +name: lab-hardware-cad +description: Design custom laboratory hardware as parametric build123d models and export fabrication-ready STEP, STL, and DXF files - microfluidic chips and molds, optomechanical mounts and breadboard adapters, cuvette and microplate holders, tube racks, animal-behavior rigs, and 3D-printed instrument fixtures. Use when a research task needs a physical part that must mate with standardized labware, an optical table, a cage system, or a printer, CNC, or laser process. +license: MIT +compatibility: Python 3.10-3.14 with build123d 0.11.1 and matplotlib for snapshots. Geometry commands require build123d; the standards lookup and the interface check run on the standard library alone. No network access needed. +allowed-tools: Read Write Edit Bash Glob Grep +metadata: + version: "1.1" + skill-author: K-Dense Inc. + last-reviewed: "2026-08-14" + build123d-version: "0.11.1" +--- + +# Lab Hardware CAD + +Design physical research hardware as **parametric Python source**, export STEP as the +authoritative artifact, and verify the result both numerically and visually before anything +is fabricated. + +The hard part of lab hardware is almost never the geometry. It is that the part must mate with +equipment whose dimensions are fixed by a published standard or a vendor drawing. A holder that +is 0.5 mm too wide does not fit the plate reader; a channel with the wrong aspect ratio collapses +during bonding; a mount whose bolt pattern is 25.4 mm instead of 25.0 mm will not reach the +optical table. This skill exists to keep those numbers correct and checked. + +## When to use + +Use for any request to design, model, or fabricate a physical part for a lab: chip, mold, mount, +adapter, holder, rack, bracket, enclosure, jig, fixture, arena, or maze. Also use to inspect or +modify an existing STEP file. + +Do **not** use for finite-element analysis, computational fluid dynamics, molecular structure, +or scientific plotting. Those are different skills. + +## Setup + +```bash +uv venv --python 3.12 .venv-labcad +uv pip install --python .venv-labcad/bin/python "build123d==0.11.1" "matplotlib>=3.8" +``` + +build123d 0.11.1 requires Python >=3.10,<3.15 and pulls in the OpenCascade kernel through +`cadquery-ocp-novtk`. The wheel is large; install once per project and reuse it. + +All bundled scripts take `--help`. `check.py standards` runs without build123d installed. + +**Model files are executed, not parsed.** `gen.py`, `check.py`, and `snapshot.py` import a +`*_model.py` and call its `build()`, which runs arbitrary Python in the current environment. That +is inherent to parametric CAD โ€” the source is the design. Only run model files authored in this +session or supplied by the user from a trusted location. If a model came from the internet, a +shared drive, or an untrusted colleague, read it before running it and say that you did. + +## Required workflow + +Follow these steps in order. Steps 5 and 6 are not optional, and step 6 is not waived by step 5 +passing. + +### 1. Route to a device family + +Read the request, classify it, and load **exactly one** family reference. Do not load all four โ€” +they are long, and mixing conventions between families is a common source of error. + +| If the part is | Load | +| --- | --- | +| A chip, mold, channel network, flow cell, gasket, or anything with fluid ports | `references/microfluidics.md` | +| A mount, post, breadboard adapter, cage-system part, filter or sample holder in a beam path | `references/optomechanics.md` | +| An adapter, insert, rack, or holder for plates, cuvettes, tubes, slides, or dishes | `references/labware-adapters.md` | +| An arena, maze, head-fixation part, spout, tether, or extrusion-mounted enclosure for animal work | `references/behavior-rigs.md` | + +If the part genuinely spans two families โ€” a microfluidic chip that bolts to an optical table โ€” +load the family that owns the **critical interface**, then read only the interface section of the +second. State in your response which family you routed to. + +### 2. Establish the interface dimensions before any geometry + +Every part has at least one mating interface. Before writing code, write down for each interface: + +- the **source** of the dimension: a published standard, a vendor drawing, or a user measurement; +- the **nominal value and tolerance**; +- the **clearance or interference** you intend, and why. + +Look the number up in `assets/standards.json` or the family reference. **Never write an interface +dimension from memory.** If the number is not in the standards file or the reference, ask the user +for the vendor drawing or the measurement rather than guessing. A guessed interface dimension is +the single most expensive failure mode in this skill. + +A feature that must *receive* a standardised component is sized against that component's +**maximum material condition** โ€” nominal plus its plus-tolerance โ€” and only then given clearance. +Sized from nominal instead, it fits only the smaller half of conforming parts. + +```bash +python scripts/check.py standards --list +python scripts/check.py standards --show slas-microplate-footprint +``` + +### 3. Choose the process before choosing the geometry + +Read `references/fabrication-limits.md`. Process determines minimum wall, minimum feature, +achievable tolerance, and whether the part survives autoclaving or contact with your solvent. +FDM cannot hold ยฑ0.05 mm; SLA resin is generally not safe for cell contact without post-cure and +testing. Record the process and material in the model docstring. + +### 4. Author a parametric model + +Write `_model.py`. The source is the authoritative artifact โ€” **never hand-edit an exported +STEP file**, and never regenerate from a mesh. + +Requirements: + +- Every dimension that a user might change is a **module-level named constant** with units in the + name: `bore_d_mm`, `wall_t_mm`, `post_h_mm`. No bare numbers in the body except 0, 1, and 2. +- Expose `build() -> Part`. `gen.py` calls it. +- Group parameters into an `INTERFACE` block (dimensions fixed by a standard, annotated with the + standard ID) and a `DESIGN` block (dimensions you are free to choose). +- **Derive every computed dimension inside a function**, never at module level, so `--param` + overrides actually reach it. +- Declare an `interfaces()` function returning the dimensions the part must fit, each with its + standard ID and intent. This is what makes the interface machine-checkable in step 5. +- Put the process, material, and every interface source in the module docstring. + +```python +"""SLAS microplate carrier for a custom stage insert. + +Process: FDM, PETG, 0.2 mm layer. Tolerance budget +/-0.3 mm. +Interfaces: + - Plate pocket: ANSI/SLAS 1-2004 (R2012) footprint 127.76 x 85.48 mm, +/-0.25. + - Stage bolts: user-measured, 40.0 mm centres (drawing in docs/stage.pdf). +""" +from build123d import * + +# --- INTERFACE (fixed by standard; do not tune) --- +plate_l_mm = 127.76 # ANSI/SLAS 1-2004 nominal +plate_w_mm = 85.48 # ANSI/SLAS 1-2004 nominal +plate_tol_mm = 0.25 # ANSI/SLAS 1-2004; the pocket is sized to nominal + this +# --- DESIGN (free) --- +pocket_clearance_mm = 0.40 # per-side; FDM, see fabrication-limits.md +wall_t_mm = 3.0 +floor_t_mm = 2.5 +body_h_mm = 12.0 + + +def pocket_mm() -> tuple[float, float]: + """Pocket at the plate's maximum material condition plus clearance per side. + + A pocket sized from nominal jams on roughly half of conforming plates. + """ + growth = plate_tol_mm + 2 * pocket_clearance_mm + return plate_l_mm + growth, plate_w_mm + growth + + +def interfaces() -> list[dict]: + """What this part must fit. `check.py interfaces` verifies every entry.""" + pocket_l, pocket_w = pocket_mm() + return [ + {"feature": "plate pocket length", "standard": "slas-microplate-footprint", + "dimension": "footprint_length", "value": pocket_l, + "intent": "envelope", "clearance": 2 * pocket_clearance_mm}, + {"feature": "plate pocket width", "standard": "slas-microplate-footprint", + "dimension": "footprint_width", "value": pocket_w, + "intent": "envelope", "clearance": 2 * pocket_clearance_mm}, + ] + + +def build() -> Part: + pocket_l, pocket_w = pocket_mm() + with BuildPart() as carrier: + Box(pocket_l + 2 * wall_t_mm, pocket_w + 2 * wall_t_mm, body_h_mm) + with Locations((0, 0, floor_t_mm)): + Box(pocket_l, pocket_w, body_h_mm, mode=Mode.SUBTRACT, + align=(Align.CENTER, Align.CENTER, Align.MIN)) + return carrier.part +``` + +See `references/build123d-patterns.md` for the builder-vs-algebra choice, the `interfaces()` +contract, sketching, selectors, fillets, and threaded-insert bores. + +### 5. Generate and check the interfaces + +```bash +python scripts/gen.py carrier_model.py --outdir out/ +python scripts/check.py facts out/carrier.step +python scripts/check.py interfaces out/carrier.manifest.json +``` + +`gen.py` writes `carrier.step` (authoritative), `carrier.stl` (mesh preview and printing), and +`carrier.manifest.json` recording the source hash, resolved parameters, declared interfaces, +library versions, and measured bounding box, volume, and validity. The manifest is the provenance +record โ€” keep it with the artifact. + +`check.py facts` reports `is_valid`, bounding box, volume, surface area, centre of mass, and +solid count. A part that reports `is_valid: false` is broken geometry; fix the source before going +further. + +`check.py interfaces` is the check that gates fabrication. It evaluates every entry the model +declared against the standards database and exits non-zero on failure. Use it rather than +`check.py fit` for anything internal: **the interface is almost always a pocket, bore, or slot, +and none of those appear in the part's outer bounding box.** `fit` measures that outer envelope, so +running it on a carrier reports the outside of the walls and fails against the plate footprint. +Reach for `fit` only to check one number by hand, or when the part's own outline is the interface โ€” +a gasket cut to a plate footprint, for instance: + +```bash +# check one dimension by hand, without a geometry kernel +python scripts/check.py fit --standard slas-microplate-footprint \ + --intent envelope --clearance 0.8 --value footprint_length=128.81 +``` + +For assemblies, check that parts do not interfere: + +```bash +python scripts/check.py clearance out/carrier.step out/lid.step --min 0.3 +``` + +### 6. Snapshot and actually look at it + +```bash +python scripts/snapshot.py out/carrier.step --out out/carrier.png +``` + +Then **read the PNG**. This step is mandatory after every generation and every modification. +Deterministic checks passing is not a reason to skip it: `is_valid` and a correct bounding box are +both fully consistent with a pocket cut on the wrong face, an inverted mold polarity, a boss +placed outside the body, or a fillet that ate a feature. Those errors are obvious in a picture and +invisible in the numbers. + +The six views are true orthographic projections, and the outlines are the model's real edges drawn +**without hidden-line removal**. So a circle visible "through" material is a bore on the far side, +not a window โ€” the part is not transparent. Read it that way rather than reporting a hole that +is not there. + +State in your response what you saw in the snapshot, not merely that you generated one. + +### 7. Repair through the source + +If any check fails, edit the parameters or the model code, rerun `gen.py`, and rerun **both** +step 5 and step 6. Never patch the STEP. + +### 8. Report before fabrication + +Work through `references/validation.md` and give the user: the process and material, every +interface dimension with its source and tolerance, the clearances chosen, what the snapshot showed, +and any check that did not pass. + +Flag explicitly every interface the automatic check could not cover โ€” a vendor drawing, a user +measurement, a standard not in the bundled database. `check.py interfaces` reports only what the +model declared against a known standard, so silence there is not confirmation; a dimension nobody +could check has to be named as such. + +## Units + +build123d is unitless internally and everything in this skill is **millimetres and degrees**. +`export_step` is called with `Unit.MM`. Imperial hardware appears throughout optomechanics +(1/4-20 screws, 1 inch grids, SM1 threads); convert to millimetres in a single named constant at +the point of definition and never mix systems inside an expression. 1 inch is exactly 25.4 mm, and +a 25 mm metric optical grid is **not** interchangeable with a 1 inch imperial grid โ€” the error +accumulates to 1.6 mm over four holes. + +## Tolerances and fits + +A nominal dimension is not a fit. Every mating dimension needs a deliberate clearance chosen from +the process tolerance in `references/fabrication-limits.md`. Common defaults, per side: + +| Fit | FDM | SLA | CNC | +| --- | --- | --- | --- | +| Free-sliding (plate in a pocket) | 0.40 mm | 0.20 mm | 0.10 mm | +| Located but removable | 0.25 mm | 0.10 mm | 0.05 mm | +| Press / interference | -0.05 mm | -0.03 mm | -0.02 mm | + +These are starting points for a first article, not guarantees. Say so when you report them, and +recommend printing a test coupon of the critical interface before committing to a full part. + +## Scientific caveats + +- **Material compatibility governs.** A geometrically perfect part in the wrong polymer fails in + service: autoclave cycles distort PLA, many solvents craze acrylic, and uncured SLA resin is + cytotoxic. Check `references/fabrication-limits.md` before recommending a material for anything + contacting cells, tissue, solvents, or heat. +- **Optical parts have non-geometric requirements.** Autofluorescence, surface roughness, and + stray-light scatter are not visible in a STEP file. Black resin is not automatically low-scatter. +- **Vendor labware varies.** The SLAS standards fix the plate footprint but not well geometry, + skirt profile, or lid fit, and consumable tubes differ between suppliers. Design to the standard + where one exists; otherwise require a measurement. +- **A passing bounding box is not a passing part.** `fit` checks the dimensions it is given. It + cannot see a missing feature, and it does not replace the snapshot. + +## References + +| File | Contents | +| --- | --- | +| `references/microfluidics.md` | Channel cross-sections and aspect ratios, mold vs chip polarity, minimum features by process, port and tubing interfaces, bonding lands, dead volume | +| `references/optomechanics.md` | Breadboard grids and screw clearances, post and pedestal heights, 30 mm cage geometry, SM lens-tube threads, beam height | +| `references/labware-adapters.md` | ANSI/SLAS 1-4 microplate dimensions, cuvettes, tubes, slides, dishes, deck and stage constraints | +| `references/behavior-rigs.md` | Arena and maze geometry, head-fixation interfaces, spouts and ports, T-slot extrusion, cleaning and durability | +| `references/fabrication-limits.md` | Process tolerances, minimum walls and features, clearance and thread inserts, materials, autoclave and solvent and biocompatibility | +| `references/validation.md` | Pre-fabrication checklist and the failure modes each item catches | +| `references/build123d-patterns.md` | build123d 0.11.1 API cookbook: builder vs algebra, sketches, selectors, joints, exports | + +## Scripts + +| Command | Purpose | +| --- | --- | +| `gen.py --outdir DIR` | Run `build()`, export STEP and STL, write the provenance manifest | +| `gen.py --dxf [--dxf-z MM]` | Also slice a 2D DXF profile for laser cutting (default plane: mid-height) | +| `check.py facts ` | Validity, bounding box, volume, area, centre of mass, solid count | +| `check.py interfaces ` | Check every interface the model declares; non-zero exit on failure | +| `check.py fit --standard ID --value DIM=MM` | Check one dimension by hand, or a part whose outer envelope is the interface | +| `check.py clearance --min MM` | Minimum distance between two solids; detects interference | +| `check.py standards [--list\|--show ID]` | Browse the bundled standards data (standard library only) | +| `snapshot.py --out PNG` | Six-view orthographic and isometric render for visual review | + +All commands accept `--json` for machine-readable output and write progress to stderr. +`check.py standards`, and `check.py interfaces` on a manifest, run without build123d installed. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/assets/standards.json b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/assets/standards.json new file mode 100644 index 00000000..7f6dc97f --- /dev/null +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/assets/standards.json @@ -0,0 +1,210 @@ +--- +title: "Standards" +task: "" +lineage_type: import +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/lab-hardware-cad/assets/standards.json +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 +prompt_class: unknown +upstream_changes: accepted +author: upstream +validated: false +--- + +{ + "schema_version": "1.0", + "units": "mm", + "note": "Dimensional standards for lab-hardware interfaces. Every entry carries a source. Entries with verified=false were not confirmed against the primary document during authoring and must be checked before use.", + "last_reviewed": "2026-08-13", + "standards": { + "slas-microplate-footprint": { + "title": "Microplate footprint (base outline)", + "authority": "ANSI/SLAS", + "document": "ANSI/SLAS 1-2004 (R2012) Footprint Dimensions", + "url": "https://www.slas.org/SLAS/assets/File/public/standards/ANSI_SLAS_1-2004_FootprintDimensions.pdf", + "verified": true, + "dimensions": { + "footprint_length": { + "nominal": 127.76, + "tol_plus": 0.25, + "tol_minus": 0.25, + "note": "Measured within 12.7 mm of the outside corners. Relaxes to +/-0.5 mm at any point along the side." + }, + "footprint_width": { + "nominal": 85.48, + "tol_plus": 0.25, + "tol_minus": 0.25, + "note": "Measured within 12.7 mm of the outside corners. Relaxes to +/-0.5 mm at any point along the side." + }, + "corner_radius": { + "nominal": 3.18, + "tol_plus": 1.6, + "tol_minus": 1.6, + "note": "Outside radius of the four bottom-flange corners. The wide tolerance means a pocket must be cut to the maximum radius, not the nominal." + } + }, + "fit_checks": [ + {"measure": "bbox_x", "dimension": "footprint_length"}, + {"measure": "bbox_y", "dimension": "footprint_width"} + ], + "design_note": "For a pocket that receives a plate, add clearance per side on top of the maximum material condition (127.76 + 0.25 = 128.01). Check the pocket, not the plate." + }, + "slas-microplate-height": { + "title": "Microplate height", + "authority": "ANSI/SLAS", + "document": "ANSI/SLAS 2-2004 (R2012) Height Dimensions", + "url": "https://www.slas.org/SLAS/assets/File/public/standards/ANSI_SLAS_2-2004_HeightDimensions.pdf", + "verified": true, + "dimensions": { + "plate_height": { + "nominal": 14.35, + "tol_plus": 0.25, + "tol_minus": 0.25, + "note": "Datum A (resting plane) to the maximum protrusion of the perimeter wells. Secondary sources also quote +/-0.76 mm; consult the document before relying on the tighter band. Lidded and deep-well plates are taller and out of scope of this dimension." + } + }, + "fit_checks": [ + {"measure": "bbox_z", "dimension": "plate_height"} + ] + }, + "slas-microplate-flange": { + "title": "Microplate bottom outside flange height", + "authority": "ANSI/SLAS", + "document": "ANSI/SLAS 3-2004 (R2012) Bottom Outside Flange Dimensions", + "url": "https://www.slas.org/SLAS/assets/File/public/standards/ANSI_SLAS_3-2004_BottomOutsideFlangeDimensions.pdf", + "verified": true, + "dimensions": { + "flange_height_short": {"nominal": 2.41, "tol_plus": 0.38, "tol_minus": 0.38}, + "flange_height_medium": {"nominal": 6.10, "tol_plus": 0.38, "tol_minus": 0.38}, + "flange_height_tall": {"nominal": 7.62, "tol_plus": 0.38, "tol_minus": 0.38} + }, + "fit_checks": [], + "design_note": "Three flange heights are standardised. A gripper or carrier that assumes one will drop plates built to another. Ask which the user has." + }, + "slas-well-positions-96": { + "title": "96-well plate well positions", + "authority": "ANSI/SLAS", + "document": "ANSI/SLAS 4-2004 (R2012) Well Positions", + "url": "https://www.slas.org/SLAS/assets/File/public/standards/ANSI_SLAS_4-2004_WellPositions.pdf", + "verified": true, + "dimensions": { + "well_pitch": {"nominal": 9.0, "tol_plus": 0.0, "tol_minus": 0.0, "note": "Centre-to-centre in both x and y."}, + "a1_offset_x": {"nominal": 14.38, "tol_plus": 0.0, "tol_minus": 0.0, "note": "Left outside edge to the centre of column 1."}, + "a1_offset_y": {"nominal": 11.24, "tol_plus": 0.0, "tol_minus": 0.0, "note": "Top outside edge to the centre of row A."}, + "well_position_tolerance": {"nominal": 0.70, "tol_plus": 0.0, "tol_minus": 0.0, "note": "Each well centre lies within a 0.70 mm diameter of nominal. This is a positional tolerance zone, not a +/- band."} + }, + "fit_checks": [], + "design_note": "Grid layout: x = a1_offset_x + 9.0 * column_index, y = a1_offset_y + 9.0 * row_index, measured from the plate outline corner." + }, + "slas-well-positions-384": { + "title": "384-well plate well positions", + "authority": "ANSI/SLAS", + "document": "ANSI/SLAS 4-2004 (R2012) Well Positions", + "url": "https://www.slas.org/SLAS/assets/File/public/standards/ANSI_SLAS_4-2004_WellPositions.pdf", + "verified": false, + "dimensions": { + "well_pitch": {"nominal": 4.5, "tol_plus": 0.0, "tol_minus": 0.0, "note": "Centre-to-centre in both x and y."}, + "a1_offset_x": {"nominal": 12.13, "tol_plus": 0.0, "tol_minus": 0.0, "note": "UNVERIFIED. Derived as the 96-well offset minus half the 96-well pitch. Confirm against ANSI/SLAS 4-2004 before cutting metal."}, + "a1_offset_y": {"nominal": 8.99, "tol_plus": 0.0, "tol_minus": 0.0, "note": "UNVERIFIED. Derived, as above. Confirm against the document."} + }, + "fit_checks": [], + "design_note": "Only well_pitch is confirmed here. Read the standard for the A1 offsets before relying on them." + }, + "slas-well-positions-1536": { + "title": "1536-well plate well positions", + "authority": "ANSI/SLAS", + "document": "ANSI/SLAS 4-2004 (R2012) Well Positions", + "url": "https://www.slas.org/SLAS/assets/File/public/standards/ANSI_SLAS_4-2004_WellPositions.pdf", + "verified": false, + "dimensions": { + "well_pitch": {"nominal": 2.25, "tol_plus": 0.0, "tol_minus": 0.0, "note": "Centre-to-centre in both x and y."}, + "a1_offset_x": {"nominal": 11.005, "tol_plus": 0.0, "tol_minus": 0.0, "note": "UNVERIFIED. Derived from the 96-well offset. Confirm against ANSI/SLAS 4-2004."}, + "a1_offset_y": {"nominal": 7.865, "tol_plus": 0.0, "tol_minus": 0.0, "note": "UNVERIFIED. Derived, as above. Confirm against the document."} + }, + "fit_checks": [], + "design_note": "Only well_pitch is confirmed here." + }, + "cuvette-standard-10mm": { + "title": "Standard 10 mm path-length spectrophotometer cuvette", + "authority": "De facto industry convention", + "document": "No single ANSI/ISO document fixes this; it is a near-universal convention across suppliers.", + "url": "https://spectrecology.com/blog/guide-to-cuvettes/", + "verified": true, + "dimensions": { + "external_width": {"nominal": 12.5, "tol_plus": 0.1, "tol_minus": 0.1, "note": "Tolerance is indicative; suppliers vary."}, + "external_depth": {"nominal": 12.5, "tol_plus": 0.1, "tol_minus": 0.1}, + "external_height": {"nominal": 45.0, "tol_plus": 0.5, "tol_minus": 0.5, "note": "Body height excluding any cap or stopper. Semi-micro and micro cuvettes share the external footprint but differ in height and internal geometry."}, + "path_length": {"nominal": 10.0, "tol_plus": 0.0, "tol_minus": 0.0, "note": "Internal optical path. 12.5 external minus 2 x 1.25 mm wall."}, + "wall_thickness": {"nominal": 1.25, "tol_plus": 0.0, "tol_minus": 0.0} + }, + "fit_checks": [ + {"measure": "bbox_x", "dimension": "external_width"}, + {"measure": "bbox_y", "dimension": "external_depth"} + ], + "design_note": "Because this is a convention rather than a standard, a holder should be designed with generous clearance or a compliant feature. Confirm against the user's actual cuvettes." + }, + "optical-breadboard-metric": { + "title": "Metric optical breadboard hole grid", + "authority": "De facto industry convention", + "document": "Universal across Thorlabs, Newport, Edmund and others for metric tables.", + "url": "https://www.thorlabs.com/imperial-and-metric-threading", + "verified": true, + "dimensions": { + "grid_pitch": {"nominal": 25.0, "tol_plus": 0.0, "tol_minus": 0.0, "note": "Metric grid. NOT interchangeable with the 25.4 mm imperial grid."}, + "thread": {"nominal": 6.0, "tol_plus": 0.0, "tol_minus": 0.0, "note": "M6 x 1.0 tapped holes."}, + "clearance_hole_close": {"nominal": 6.4, "tol_plus": 0.0, "tol_minus": 0.0, "note": "Close-fit clearance for an M6 cap screw."}, + "clearance_hole_normal": {"nominal": 6.6, "tol_plus": 0.0, "tol_minus": 0.0, "note": "Normal-fit clearance for M6. Prefer this on printed parts."}, + "counterbore_dia": {"nominal": 11.0, "tol_plus": 0.0, "tol_minus": 0.0, "note": "For an M6 socket head cap screw head (nominal head dia 10 mm)."}, + "border": {"nominal": 12.5, "tol_plus": 0.0, "tol_minus": 0.0, "note": "Typical edge-to-first-hole distance."} + }, + "fit_checks": [], + "design_note": "Slot rather than hole one of any pair of mounting features to absorb grid and print tolerance." + }, + "optical-breadboard-imperial": { + "title": "Imperial optical breadboard hole grid", + "authority": "De facto industry convention", + "document": "Universal across Thorlabs, Newport, Edmund and others for imperial tables.", + "url": "https://www.thorlabs.com/imperial-and-metric-threading", + "verified": true, + "dimensions": { + "grid_pitch": {"nominal": 25.4, "tol_plus": 0.0, "tol_minus": 0.0, "note": "1 inch exactly. Over four holes this differs from the metric grid by 1.6 mm."}, + "thread_major_dia": {"nominal": 6.35, "tol_plus": 0.0, "tol_minus": 0.0, "note": "1/4-20 UNC: 0.25 inch major diameter, 20 threads per inch."}, + "clearance_hole_normal": {"nominal": 6.8, "tol_plus": 0.0, "tol_minus": 0.0, "note": "Normal-fit clearance for a 1/4-20 screw."}, + "counterbore_dia": {"nominal": 11.2, "tol_plus": 0.0, "tol_minus": 0.0, "note": "For a 1/4-20 socket head cap screw head."}, + "border": {"nominal": 12.7, "tol_plus": 0.0, "tol_minus": 0.0, "note": "Typical 0.5 inch edge-to-first-hole distance."} + }, + "fit_checks": [], + "design_note": "Ask which table the user has. Assuming the wrong system is the most common optomechanical design error." + }, + "cage-system-30mm": { + "title": "30 mm cage system", + "authority": "Thorlabs (de facto standard, second-sourced by others)", + "document": "Thorlabs 30 mm cage system construction rods and cage plates", + "url": "https://www.thorlabs.com/newgrouppage9.cfm?objectgroup_ID=2273", + "verified": true, + "dimensions": { + "rod_spacing": {"nominal": 30.0, "tol_plus": 0.0, "tol_minus": 0.0, "note": "Rod centre to rod centre, on a square pattern. 1.18 inch."}, + "rod_diameter": {"nominal": 6.0, "tol_plus": 0.0, "tol_minus": 0.0, "note": "ER series cage rods."}, + "rod_bore_clearance": {"nominal": 6.1, "tol_plus": 0.0, "tol_minus": 0.0, "note": "Suggested bore for a printed plate to slide on 6 mm rods. Widen for FDM; see fabrication-limits.md."}, + "plate_thickness_typical": {"nominal": 8.9, "tol_plus": 0.0, "tol_minus": 0.0, "note": "0.35 inch, the CP33 standard cage plate. Not a constraint on custom plates, but matching it keeps optical path budgets simple."} + }, + "fit_checks": [], + "design_note": "The 30 mm rod square is centred on the optical axis. A custom plate must place its aperture at the centroid of the four rod bores." + }, + "sm1-lens-tube-thread": { + "title": "SM1 lens tube thread", + "authority": "Thorlabs (de facto standard)", + "document": "Thorlabs SM1 series threading", + "url": "https://www.thorlabs.com/newgrouppage9.cfm?objectgroup_id=4114", + "verified": true, + "dimensions": { + "thread_major_dia": {"nominal": 26.289, "tol_plus": 0.0, "tol_minus": 0.0, "note": "1.035 inch-40 thread. Holds 1 inch diameter optics."}, + "threads_per_inch": {"nominal": 40.0, "tol_plus": 0.0, "tol_minus": 0.0}, + "pitch": {"nominal": 0.635, "tol_plus": 0.0, "tol_minus": 0.0, "note": "25.4 / 40 mm."}, + "optic_dia": {"nominal": 25.4, "tol_plus": 0.0, "tol_minus": 0.0, "note": "1 inch optic."} + }, + "fit_checks": [], + "design_note": "A 40 TPI thread has a 0.635 mm pitch, which is at or below the resolution of most FDM printers. Print a clearance bore and use a purchased SM1 adapter or a tapped insert rather than printing the thread." + } + } +} diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/behavior-rigs.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/behavior-rigs.md new file mode 100644 index 00000000..7b21dbd8 --- /dev/null +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/behavior-rigs.md @@ -0,0 +1,149 @@ +--- +title: "Animal-behavior rigs and enclosures" +task: "" +lineage_type: import +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/lab-hardware-cad/references/behavior-rigs.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 +prompt_class: unknown +upstream_changes: accepted +author: upstream +validated: false +--- + +# Animal-behavior rigs and enclosures + +Arenas, mazes, head-fixation hardware, spouts and ports, and the extrusion frames that carry them. + +## Dimensions come from the protocol, not from this file + +Behavioral apparatus dimensions are **not standardised**. They are set by the published protocol +the experiment replicates, and they differ between species, strains, ages, and labs. An +elevated plus maze sized for rats is wrong for mice; an open field sized from one paper will not +reproduce another paper's results. + +**Ask which protocol or paper the rig replicates, and take the dimensions from it.** If the user +does not have one, say plainly that the geometry is a design choice affecting comparability, and +get their sign-off on the numbers before modelling. Do not supply "standard" maze dimensions from +memory โ€” there is no such standard, and a plausible-looking wrong number is worse here than an +admitted gap, because it silently breaks comparison with prior work. + +What this file does cover is the engineering that is common across rigs. + +## Regulatory and welfare context + +Any apparatus that contacts animals falls under the institution's approved protocol. Before +fabrication: + +- The design must be consistent with the **approved IACUC (or local equivalent) protocol**. A + geometry change โ€” a narrower arm, a different head-plate, a new restraint โ€” may require an + amendment. Flag this; it is not the modeller's call to make. +- **Materials must be non-toxic and non-irritant**, including after repeated cleaning. +- No **entrapment or pinch geometry**: no gaps that can catch a limb, tail, or head; no wedge- + shaped gaps that narrow into a trap. Break sharp edges everywhere an animal can reach. +- Anything load-bearing over an animal needs a real margin, not a printed part at minimum wall. + +Raise these actively rather than waiting to be asked. + +## Materials and cleaning + +This dominates material choice, and it eliminates most of the obvious options: + +- **Cleaning agents** are the constraint. Ethanol (70%) crazes many plastics; quaternary ammonium + and chlorine dioxide disinfectants attack others; autoclaving distorts anything with a low glass + transition temperature. PLA in particular softens well below autoclave temperature and should be + treated as single-use. +- **Porosity carries odour.** FDM parts are porous by construction, hold odour cues between + animals, and cannot be reliably disinfected. Odour is a genuine confound in behavior work. Prefer + a non-porous process, or seal the surface, or treat FDM parts as consumable and per-cohort. +- **Chew resistance.** Rodents will chew anything reachable. Printed polymer at an exposed edge + will be destroyed and, worse, ingested. Put metal, glass, or a hard sacrificial edge wherever an + animal can bite, and keep printed material out of reach where possible. +- **Uncured resin is cytotoxic and an irritant.** SLA parts that contact animals need full post- + cure and thorough washing. See `references/fabrication-limits.md`. + +## Video tracking and optics + +Most rigs are recorded, and the geometry either helps or fights the tracking: + +- **Contrast**: match the surface to the animal's coat so the tracker can segment it. Matte white + or light grey floors for dark animals, matte dark for albino. **Matte, not gloss** โ€” specular + highlights are tracked as objects. +- **Avoid shadow-casting geometry** near the floor. Deep walls at low camera angles create shadow + bands that trackers segment as the animal. +- **Infrared**: if illumination is IR, remember that many "opaque" black plastics transmit IR, and + that IR-transparent floors change the apparent image. Verify with the actual camera, not by + assumption. +- Leave a clear, unobstructed camera line to the whole arena, and model the camera mount as part + of the rig so the field of view is checked before fabrication, not after. + +## T-slot extrusion frames + +Most rigs are built on aluminium extrusion. The critical fact: **slot width is not implied by +profile size.** + +| Profile | Common slot widths | Typical fastener | +| --- | --- | --- | +| 20 x 20 mm | 5 mm or 6 mm depending on series | M4 or M5 T-nut | +| 30 x 30 mm | 8 mm typical | M6 T-nut | +| 40 x 40 mm | 8 mm or 10 mm depending on series | M6 or M8 T-nut | + +A 20 mm profile from one supplier takes a 6 mm slot nut; from another, 5 mm. **Measure the slot, +or get the part number.** A bracket modelled for the wrong slot is scrap. + +Design notes: + +- Slot the bracket's mounting features along the extrusion axis. That is the whole point of + extrusion โ€” position is continuously adjustable, and a fixed hole throws that away. +- Extrusion faces are the datum. Design brackets to register flat against a face and, where + possible, into the slot, so the part cannot rotate under load. +- Printed brackets carrying a camera or a heavy component should be treated as prototypes. Polymer + creeps under sustained load and the camera will slowly droop out of alignment. + +## Head fixation + +The highest-consequence geometry in this file, and entirely lab-specific. + +- The head-plate or head-post interface must come from the **actual implant** the lab uses, as a + drawing or a measurement. There is no standard. Get the part. +- The kinematic requirement is to constrain the implant **repeatably and without play**, with + clamping force that does not deflect the plate. Play translates directly into imaging or + recording motion artefact. +- Fixation hardware must be **quick to release**, both for routine handling and in an emergency. +- Printed clamps flex. For any part carrying head-fixation load, recommend machined metal and + present the printed version as a fit-check prototype only. Say this explicitly โ€” it is a welfare + issue as well as a data-quality one. + +## Spouts, ports, and reward delivery + +- Spout material must be non-toxic and cleanable; stainless steel tubing is the usual choice, held + by a printed carrier that never itself contacts the animal's mouth. +- Position is a calibrated experimental variable. Make spout position **adjustable and readable**, + and record it in the manifest, so it can be reproduced across sessions and animals. +- Model the reward line's dead volume โ€” the delay between valve and spout is an experimental + parameter. See the dead-volume formula in `references/microfluidics.md`. +- If lick detection is capacitive, keep conductive material away from the sensing element and give + the wire a defined, strain-relieved route in the model. + +## Checks to run + +```bash +python scripts/gen.py arena_model.py --outdir out/ +python scripts/check.py facts out/arena.step +python scripts/check.py clearance out/arena.step out/camera_mount.step --min 1.0 +python scripts/snapshot.py out/arena.step --out out/arena.png +``` + +Confirm in the snapshot: + +1. No gap an animal can get a limb, tail, or head into. +2. All animal-reachable edges broken; no sharp corners. +3. Camera has an unobstructed view of the whole floor. +4. Extrusion mounting features are slotted, and on the faces you can actually reach with a tool. +5. Nothing printed sits where it will be chewed. + +## Sources + +Deliberately none for dimensions. Arena, maze, and head-fixation geometry must come from the +protocol being replicated or from the physical implant, not from a general reference. The +material, cleaning, tracking, and extrusion guidance above is general engineering practice. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/build123d-patterns.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/build123d-patterns.md new file mode 100644 index 00000000..bdaa3944 --- /dev/null +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/build123d-patterns.md @@ -0,0 +1,273 @@ +--- +title: "build123d 0.11.1 patterns" +task: "" +lineage_type: import +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/lab-hardware-cad/references/build123d-patterns.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 +prompt_class: unknown +upstream_changes: accepted +author: upstream +validated: false +--- + +# build123d 0.11.1 patterns + +An API cookbook for the geometry this skill actually needs. Every snippet here was run against +build123d 0.11.1 on Python 3.12. + +## Builder mode or algebra mode + +build123d offers two equivalent APIs. + +```python +# Builder mode: a context manager collects operations. mode= controls the boolean. +with BuildPart() as ex: + Box(80.0, 60.0, 10.0) + Cylinder(radius=11.0, height=10.0, mode=Mode.SUBTRACT) +part = ex.part + +# Algebra mode: plain objects and operators. +part = Box(80.0, 60.0, 10.0) - Cylinder(radius=11.0, height=10.0) +``` + +**Use builder mode for parts in this skill.** Selectors (`ex.edges()`, `ex.faces()`) read naturally +from the builder, which is what you need for fillets and for placing features on found faces. +Algebra mode is a good fit for short, purely constructive shapes. + +Do not mix the two styles inside one `build()`. + +## The model file contract + +`gen.py` imports the module, calls `build()`, and then reads `interfaces()`. Parameters must be +module-level so they can be overridden with `--param`. + +```python +"""One-line description of the part. + +Process: SLA, tough resin. Orientation: bore axis vertical. +Interfaces: + - Rod bores: 30 mm cage system, Thorlabs ER series (cage-system-30mm). +""" +from build123d import * + +# --- INTERFACE (fixed; do not tune) --- +rod_spacing_mm = 30.0 # cage-system-30mm +rod_bore_d_mm = 6.1 # cage-system-30mm, clearance on 6.0 mm rods +# --- DESIGN (free) --- +plate_t_mm = 8.9 +aperture_d_mm = 25.4 + + +def interfaces() -> list[dict]: + return [ + {"feature": "cage rod bore spacing", "standard": "cage-system-30mm", + "dimension": "rod_spacing", "value": rod_spacing_mm, "intent": "match"}, + {"feature": "cage rod bore diameter", "standard": "cage-system-30mm", + "dimension": "rod_diameter", "value": rod_bore_d_mm, + "intent": "envelope", "clearance": 0.1}, + ] + + +def build() -> Part: + half = rod_spacing_mm / 2 + with BuildPart() as plate: + Box(rod_spacing_mm + 12.0, rod_spacing_mm + 12.0, plate_t_mm) + with Locations((half, half), (-half, half), (half, -half), (-half, -half)): + Hole(radius=rod_bore_d_mm / 2) + Hole(radius=aperture_d_mm / 2) + return plate.part +``` + +## Declaring interfaces + +Most lab-hardware interfaces are **internal features** โ€” a pocket, a bore, a slot โ€” and none of +them appear in the part's outer bounding box. So `check.py fit` cannot find them by measuring the +STEP, and hand-copying the number into `--value` reintroduces exactly the transcription error the +skill exists to prevent. Declaring them closes the loop: `gen.py` records the declaration in the +manifest, and `check.py interfaces` verifies every entry. + +Each entry needs `standard`, `dimension`, and `value`; `feature`, `intent`, and `clearance` are +optional: + +| Key | Meaning | +| --- | --- | +| `standard` | ID from `check.py standards --list` | +| `dimension` | a dimension name inside that standard | +| `value` | the number **this model computed**, in mm | +| `feature` | human label for the check output (default: the dimension name) | +| `intent` | `match` if this part must itself conform; `envelope` if the feature must accept any conforming part (default: `match`) | +| `clearance` | total intended clearance in mm, both sides (default: 0) | + +**Write `interfaces()` as a function, and compute derived dimensions inside functions.** A +module-level `INTERFACES = [...]` list is also accepted, but it is evaluated at import โ€” before +`--param` is applied โ€” so any value derived from an overridden parameter is recorded wrong. The same +applies to the geometry: derive inside `build()` or a helper, never at module level. + +```python +# Wrong: --param plate_tol_mm=0 silently leaves pocket_l_mm at the old value +pocket_l_mm = plate_l_mm + plate_tol_mm + 2 * pocket_clearance_mm + +# Right: recomputed on every call, so overrides land +def pocket_l_mm() -> float: + return plate_l_mm + plate_tol_mm + 2 * pocket_clearance_mm +``` + +`gen.py` warns when it sees a static `INTERFACES` list together with `--param`. + +## Positioning + +`Locations` places the objects created inside it. It is the workhorse for bolt patterns. + +```python +with Locations((10.0, 0.0), (-10.0, 0.0)): # two positions on the current plane + Hole(radius=3.3) + +with Locations((0.0, 0.0, floor_t_mm)): # offset in z + Box(10.0, 10.0, 5.0, mode=Mode.SUBTRACT) + +with GridLocations(9.0, 9.0, 12, 8): # x spacing, y spacing, x count, y count + Hole(radius=1.5) +``` + +`GridLocations` centres the grid on the origin. A microplate well grid is dimensioned from the +plate corner instead, so compute absolute positions and pass them to `Locations`: + +```python +a1_x_mm, a1_y_mm, pitch_mm = 14.38, 11.24, 9.0 # slas-well-positions-96 +origin_x = -plate_l_mm / 2 +origin_y = plate_w_mm / 2 +wells = [ + (origin_x + a1_x_mm + pitch_mm * col, origin_y - a1_y_mm - pitch_mm * row) + for row in range(8) for col in range(12) +] +with Locations(*wells): + Hole(radius=well_clear_d_mm / 2) +``` + +## Alignment + +By default objects are centred on the origin. `align` moves the datum, which is usually what you +want for a pocket that starts at a floor: + +```python +Box(x, y, z, align=(Align.CENTER, Align.CENTER, Align.MIN)) # sits on z = 0 +Box(x, y, z, align=(Align.MIN, Align.MIN, Align.MIN)) # corner at the origin +``` + +Getting this wrong is the classic "pocket cut through the floor" bug, and it is exactly what the +snapshot catches. + +## Holes + +`Hole` cuts through the whole part; `CounterBoreHole` and `CounterSinkHole` add a head recess. + +```python +with BuildPart() as plate: + Box(60.0, 60.0, 10.0) + with Locations((20.0, 20.0)): + CounterBoreHole(radius=6.6 / 2, counter_bore_radius=11.0 / 2, counter_bore_depth=4.0) +``` + +Remember that printed holes come out undersize โ€” see `references/fabrication-limits.md`. + +## Selectors + +Selectors find edges and faces to fillet, chamfer, or build on. The three you need: + +```python +part.edges().filter_by(Axis.Z) # keep edges parallel to Z (the vertical corners) +part.edges().group_by(Axis.Z)[-1] # the group with the highest Z (the top edges) +part.faces().sort_by(Axis.Z)[-1] # the single highest face +part.edges().filter_by(GeomType.CIRCLE) # only circular edges +``` + +`filter_by` keeps everything matching. `group_by` partitions into lists ordered by the key, so +`[-1]` is the last group and `[0]` the first. `sort_by` orders individual items. + +```python +with BuildPart() as ex: + Box(80.0, 60.0, 10.0) + chamfer(ex.edges().group_by(Axis.Z)[-1], length=4.0) # chamfer the top face edges + fillet(ex.edges().filter_by(Axis.Z), radius=5.0) # round the vertical corners +``` + +**Fillet last, and check the snapshot.** A radius larger than the adjacent feature silently +consumes it, or throws a kernel error. Fillet radii should be named parameters so they are easy to +back off. + +## Sketch then extrude + +For a profile that is not a primitive, sketch it and extrude: + +```python +with BuildPart() as bracket: + with BuildSketch() as profile: + Rectangle(40.0, 20.0) + with Locations((15.0, 0.0)): + Circle(radius=4.0, mode=Mode.SUBTRACT) + extrude(amount=6.0) +``` + +This is also the route to a laser-cut DXF: the sketch is the cut profile. + +## Exports + +`gen.py` handles these, but for reference: + +```python +export_step(part, "part.step", unit=Unit.MM) # authoritative +export_stl(part, "part.stl", tolerance=1e-3, angular_tolerance=0.1) + +# 2D profile for laser cutting. section() is a module-level operation, NOT a +# method on the shape -- part.section(...) raises AttributeError. +profile = section(part, Plane.XY.offset(z_mm), mode=Mode.PRIVATE) +exporter = ExportDXF(unit=Unit.MM) +exporter.add_shape(profile) +exporter.write("part.dxf") +``` + +Cut the section through material, not at `z = 0`: a part modelled sitting on the build plate has +only a degenerate face there. `gen.py --dxf` defaults to the part's mid-height and takes `--dxf-z` +to override. + +STEP preserves exact BREP geometry; STL is a triangulated approximation. **Always keep STEP as the +source of truth** and regenerate meshes from it, never the reverse. + +## Measuring in code + +Useful for asserting an interface inside the model itself: + +```python +bbox = part.bounding_box() +print(bbox.size.X, bbox.size.Y, bbox.size.Z) +print(part.volume, part.area) +print(part.is_valid) # a property in 0.11.1, not a method +print(part.center(CenterOf.MASS)) +``` + +`is_valid` being a property rather than a method is a real difference from older releases and from +some documentation. Access it without parentheses. + +## Things that bite + +- **`is_valid` is a property.** `part.is_valid()` raises `TypeError: 'bool' object is not callable`. +- **`section()` is a module-level operation, not a method.** `part.section(Plane.XY)` raises + `AttributeError`. Call `section(part, plane, mode=Mode.PRIVATE)`. +- **`intersect()` returns a `ShapeList`** with no `.volume`; the `&` operator returns a `Solid` that + has one. `check.py clearance` handles both. +- **Never name a script `inspect.py`** in a directory that lands on `sys.path`. It shadows the + standard library `inspect` module, which breaks `typing_extensions` and therefore build123d + itself. This is why the bundled script is `check.py`. +- **Builder objects are not parts.** Return `builder.part`, not the builder. +- **`Mode.SUBTRACT` needs an existing body.** Subtracting from an empty context does nothing + silently. +- The OpenCascade kernel raises assorted exception types. Catch broadly around boolean operations + and report the failure rather than letting a traceback escape. + +## Sources + +- build123d documentation โ€” +- Introductory examples (builder vs algebra, selectors, fillets) โ€” + +- Import/export reference โ€” diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/fabrication-limits.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/fabrication-limits.md new file mode 100644 index 00000000..f7200ae8 --- /dev/null +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/fabrication-limits.md @@ -0,0 +1,150 @@ +--- +title: "Fabrication limits, tolerances, and materials" +task: "" +lineage_type: import +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/lab-hardware-cad/references/fabrication-limits.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 +prompt_class: unknown +upstream_changes: accepted +author: upstream +validated: false +--- + +# Fabrication limits, tolerances, and materials + +Read this before finalising any geometry. Process determines what geometry is possible; material +determines whether the part survives the lab. + +## Process tolerances + +Achievable tolerance and minimum feature size, as planning figures. **Every number here depends on +the specific machine, material, and operator.** Use them to choose a process and to size a first +article, then verify with a test coupon. + +| Process | Typical tolerance | Min wall | Min feature | Notes | +| --- | --- | --- | --- | --- | +| FDM | ยฑ0.3 mm (often worse over 100 mm) | 1.2 mm (3 x 0.4 mm nozzle) | ~0.8 mm | Anisotropic: much weaker across layers. Porous. | +| SLA / DLP | ยฑ0.1 mm | 0.8 mm | ~0.3 mm | Better surface and detail. Resin choice dominates properties. | +| SLS (nylon) | ยฑ0.2 mm | 0.8 mm | ~0.5 mm | Isotropic, no supports, slightly porous surface. | +| CNC milling | ยฑ0.05 mm or better | 0.8 mm in metal | Set by tool diameter | Internal corners carry the tool radius โ€” you cannot mill a sharp internal corner. | +| Laser cutting | ยฑ0.1 mm | n/a | Kerf ~0.1-0.3 mm | 2D only. Edge taper on thick stock. Kerf offset must be applied. | + +Two consequences that catch people: + +- **Holes print undersize** on both FDM and SLA. A 6.0 mm modelled hole typically measures under + 6.0 mm. Oversize functional bores, or plan to ream them. +- **Internal corners cannot be sharp in milling.** If a milled pocket must accept a square part, + add corner relief cuts. This is the same problem as the microplate corner radius in + `references/labware-adapters.md`, from the other direction. + +## Fits and clearances + +Nominal dimensions do not produce fits. Choose a clearance deliberately, per side: + +| Fit | FDM | SLA | CNC | +| --- | --- | --- | --- | +| Free-sliding (a plate dropping into a pocket) | 0.40 mm | 0.20 mm | 0.10 mm | +| Located but removable by hand | 0.25 mm | 0.10 mm | 0.05 mm | +| Press / interference | -0.05 mm | -0.03 mm | -0.02 mm | + +Then remember the **other** part has tolerance too. When mating to a standardised component, +design the receiving feature against the component's **maximum material condition**, not its +nominal โ€” a pocket sized from nominal fits only the smaller half of conforming parts. This is what +`intent: "envelope"` enforces. Declare it in the model and check the manifest: + +```bash +python scripts/check.py interfaces out/part.manifest.json +``` + +Or check a single number by hand: + +```bash +python scripts/check.py fit --standard slas-microplate-footprint \ + --intent envelope --clearance 0.8 --value footprint_length=128.81 +``` + +## Threads and inserts + +**Printed threads are usually a mistake.** Layer resolution is comparable to the thread pitch, so +printed threads are weak, dimensionally unreliable, and shed particles. + +In descending order of preference: + +1. **Heat-set threaded inserts** โ€” the standard solution for printed parts. Model a straight bore + to the insert manufacturer's specified diameter (it varies by insert; get the datasheet) and + provide enough surrounding wall, typically at least 2 mm. +2. **Clearance hole plus a captive nut** in a hex pocket. Reliable and cheap. +3. **Tapping the printed material directly** โ€” acceptable for light, infrequently-assembled joints. +4. **Printing the thread** โ€” only for coarse threads (roughly M6 and above), never for fine + threads like the 0.635 mm pitch SM1 (see `references/optomechanics.md`). + +## Orientation and anisotropy + +For FDM especially, orientation is a design decision, not a printing detail: + +- Parts are substantially weaker **across** layers than along them. Orient so that load runs + along layers, and state the intended orientation in the model docstring. +- Overhangs beyond roughly 45 degrees need support, and supported surfaces come out rough and + dimensionally poor. If a surface is a sealing or mating face, orient it so it is not supported. +- Holes printed with their axis vertical are round; printed horizontally they come out with a + drooped top. Teardrop or chamfer horizontal holes that must stay round. +- **Every enclosed cavity needs a drain path** in resin printing. See + `references/microfluidics.md`. + +## Materials + +### Thermal + +| Material | Approximate service limit | Autoclave (121 ยฐC)? | +| --- | --- | --- | +| PLA | ~50-60 ยฐC | **No** โ€” distorts well below autoclave temperature | +| PETG | ~70-80 ยฐC | No | +| ABS / ASA | ~90-100 ยฐC | Marginal, generally no | +| Polypropylene | ~100 ยฐC | Marginal | +| Nylon (SLS) | ~120-160 ยฐC | Sometimes; verify per grade | +| PEEK | >250 ยฐC | Yes | +| Stainless steel, aluminium, glass | High | Yes | + +**Assume a printed part is not autoclavable unless it is a verified high-temperature material.** +Offer chemical or gas sterilisation as the alternative, and check that against the solvent notes +below. + +### Chemical + +- **Acrylic (PMMA)** crazes on contact with alcohols, including 70% ethanol โ€” a serious problem in + a lab that disinfects everything with ethanol. +- **Polycarbonate** is attacked by many solvents and by some alkaline cleaners. +- **PLA** hydrolyses; it degrades in warm, wet, or repeatedly-cleaned service. +- **PP, PTFE, PEEK** have broad chemical resistance and are the safe choices for solvent contact. + +Always ask what the part will be cleaned with, not just what it will contain. Cleaning agent +compatibility is more often the failure than the sample. + +### Biocompatibility + +- **Uncured SLA resin is cytotoxic.** Even nominally biocompatible resins require the + manufacturer's full post-cure and wash protocol, and leachables can still affect sensitive cell + assays. +- For anything contacting cells, tissue, or animals: prefer glass, medical-grade polymer, or PTFE + for the contact surface, and use the printed part as a holder that does not touch the sample. +- "Biocompatible" on a resin datasheet refers to a specific certified process and application. It + does not transfer to your printer, your cure schedule, or your assay. Say this rather than + implying a printed part is cell-safe. + +### Optical + +- Printed and milled surfaces scatter; they are not optical surfaces. +- Most printed resins **autofluoresce**, often strongly, which contaminates fluorescence readouts. +- Black is not automatically non-reflective. +- Where an optical surface is needed, use glass or a bonded film and model the holder around it. + +## Cost and lead-time reality + +Mention these when recommending a process: FDM is hours and pennies; SLA is hours and modest cost; +SLS and CNC are typically outsourced with days of lead time and much higher cost. A design that +needs ยฑ0.05 mm has committed the user to CNC โ€” flag that trade before they discover it at quoting. + +## Before fabrication + +Work through `references/validation.md`. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/labware-adapters.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/labware-adapters.md new file mode 100644 index 00000000..526620da --- /dev/null +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/labware-adapters.md @@ -0,0 +1,188 @@ +--- +title: "Labware adapters, holders, and racks" +task: "" +lineage_type: import +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/lab-hardware-cad/references/labware-adapters.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 +prompt_class: unknown +upstream_changes: accepted +author: upstream +validated: false +--- + +# Labware adapters, holders, and racks + +Parts that receive standard consumables: microplates, cuvettes, tubes, slides, dishes. + +The governing principle: **where a published standard exists, design to the standard; where it +does not, require a measurement.** Microplate footprints are standardised. Well geometry, skirt +profiles, tube dimensions, and lid fits are not. + +Verified dimensions live in `assets/standards.json`. Query them rather than copying numbers: + +```bash +python scripts/check.py standards --show slas-microplate-footprint +``` + +## Microplates (ANSI/SLAS 1-4) + +Four documents split the plate geometry. All are ANSI-approved and were reaffirmed in 2012. + +| Document | Governs | Key numbers | +| --- | --- | --- | +| ANSI/SLAS 1-2004 | Footprint | 127.76 x 85.48 mm ยฑ0.25; corner radius 3.18 ยฑ1.6 mm | +| ANSI/SLAS 2-2004 | Height | 14.35 ยฑ0.25 mm, resting plane to top of perimeter wells | +| ANSI/SLAS 3-2004 | Bottom outside flange | Short 2.41, medium 6.10, tall 7.62 mm, each ยฑ0.38 | +| ANSI/SLAS 4-2004 | Well positions | 96-well: 9.0 mm pitch, A1 at 14.38 mm from left, 11.24 mm from top | + +### Designing a plate pocket + +Three traps, in the order people fall into them. + +**1. Design to maximum material, not to nominal.** A plate at the top of tolerance is +127.76 + 0.25 = 128.01 mm. A pocket cut at 127.76 + clearance will jam on roughly half the plates +you try. Compute: + +```python +plate_l_mm = 127.76 # ANSI/SLAS 1-2004 nominal +plate_tol_mm = 0.25 # ANSI/SLAS 1-2004 +fit_clearance_mm = 0.40 # per side; FDM, see fabrication-limits.md +pocket_l_mm = plate_l_mm + plate_tol_mm + 2 * fit_clearance_mm # 128.81 +``` + +**2. The corner radius tolerance is enormous.** 3.18 ยฑ1.6 mm means a real plate corner is anywhere +from 1.58 to 4.78 mm. A pocket with sharp internal corners will not seat a plate with 4.78 mm +corners. Cut the pocket corners to at least the maximum, 4.78 mm โ€” or relieve them entirely with a +corner slot, which is more forgiving and prints better than a large internal fillet. + +```python +with BuildPart() as pocket: + # ... pocket geometry ... + fillet(pocket.edges().filter_by(Axis.Z).group_by(Axis.Z)[-1], radius=5.0) +``` + +**3. Height depends on the flange, not just the plate.** ANSI/SLAS 3 standardises three flange +heights. A carrier that grips the flange must be told which one. Ask; do not assume medium. + +### Well grid + +For a part that must reach individual wells โ€” a magnet block, a lid with access holes, a light +guide โ€” lay out from the plate's outline corner, not from the plate centre: + +```python +a1_x_mm, a1_y_mm, pitch_mm = 14.38, 11.24, 9.0 # ANSI/SLAS 4-2004, 96-well +locations = [ + (a1_x_mm + pitch_mm * col, a1_y_mm + pitch_mm * row) + for row in range(8) for col in range(12) +] +``` + +The standard's positional tolerance is a **0.70 mm diameter zone** around each nominal centre, not +a ยฑ0.70 mm band. A feature that must clear every well needs at least 0.35 mm of radial margin on +top of your own process tolerance. + +384-well pitch is 4.5 mm and 1536-well pitch is 2.25 mm. **The A1 offsets for those formats in +`standards.json` are marked unverified** โ€” they were derived, not read from the document. Read +ANSI/SLAS 4-2004 before relying on them. + +### What the standards do not fix + +Well diameter, well depth, well bottom shape (flat, round, conical), skirt height, lid geometry, +optical bottom thickness, and deep-well plate height. All vary by manufacturer and product line. +If the part touches any of these, get the vendor drawing or measure it. + +## Cuvettes + +The standard macro cuvette is a convention rather than a published standard, but it is close to +universal: **12.5 x 12.5 mm external, 45 mm tall, 1.25 mm wall, 10 mm optical path**. + +Design notes: + +- Holders should be generous or compliant. Because no document fixes the tolerance, a 0.1 mm + interference fit designed against nominal will fail on some suppliers' cuvettes. +- Semi-micro and micro cuvettes keep the 12.5 mm external footprint but change internal geometry + and often height. A holder designed for the external footprint accommodates all of them; one + designed around the sample volume does not. +- Cuvettes are usually held with a spring or leaf on one face so the two optical faces register + against fixed datums. Copy that: locate on two adjacent faces, preload from the opposite corner. + A four-sided pocket with clearance lets the cuvette rotate and shifts the path length. +- **Never print the optical path.** Printed surfaces scatter. The cuvette provides the optical + faces; the holder provides position only, and must not obstruct the beam window. + +## Tubes + +Tube dimensions are **not standardised** and differ measurably between suppliers, and often +between product lines from the same supplier. Approximate outside diameters near the tube rim: + +| Tube | Approximate OD | Note | +| --- | --- | --- | +| 0.2 mL PCR | 6 mm | Often supplied in strips or as a 96-format plate | +| 1.5 mL microcentrifuge | 11 mm | Rim is wider than the body; the body tapers | +| 2.0 mL microcentrifuge | 11 mm | Same rim as 1.5 mL, taller body | +| 15 mL conical | 17 mm | Cap is wider than the tube | +| 50 mL conical | 30 mm | Cap is wider than the tube | + +**Treat every number in this table as a starting point for a first article, not a design input.** +Ask the user for the supplier and catalogue number, or ask them to measure with calipers. Then +design a rack that holds the tube by the **rim or the cap**, which is dimensionally stable, rather +than by the tapered body, which is not. + +For a rack, the useful pattern is a through-hole sized to the body plus clearance and a counterbore +that catches the rim, so the tube hangs rather than bottoms out. + +## Microscope slides and coverslips + +Standard slide: **75 x 25 mm, 1.0 mm thick** (ISO 8037-1 covers slide dimensions; thickness classes +vary, and 1.0-1.2 mm is typical). Coverslips are specified by thickness number, not dimension: +#1 is roughly 0.13-0.17 mm and #1.5 roughly 0.16-0.19 mm. + +Objective working distance is unforgiving. A holder that adds even 0.2 mm under the slide can put +the sample outside a high-NA objective's working distance. Design slide holders so the slide +registers directly against the stage datum, with the holder clamping from above. + +## Petri dishes and stage inserts + +Standard dish outside diameters are approximately 35, 60, 90, and 100 mm, but the flange profile +and lid fit vary. Dishes are also slightly out of round. Locate on three points rather than a +continuous circular pocket: a three-point nest is insensitive to ovality, a close-fitting bore is +not. + +For stage inserts, the interface that matters is the **microscope stage opening**, which is +instrument-specific and must be measured. Many stages accept a standard SLAS-footprint insert; +confirm before assuming it. + +## Checks to run + +Declare the pocket in the model's `interfaces()` and let the check read it: + +```bash +python scripts/gen.py carrier_model.py --outdir out/ +python scripts/check.py interfaces out/carrier.manifest.json +``` + +**Do not point `check.py fit` at the carrier's STEP.** `fit` measures the outer bounding box, which +for a carrier is the outside of its walls โ€” 6 mm larger than the pocket here โ€” so it fails against +the plate footprint no matter how correct the pocket is. The dimension that matters is internal, so +it has to be declared, not measured from the envelope. + +To check the number by hand instead: + +```bash +python scripts/check.py fit --standard slas-microplate-footprint \ + --intent envelope --clearance 0.8 --value footprint_length=128.81 +``` + +`--intent envelope` checks one-sided against maximum material condition, and `--clearance` is the +total intended clearance: 0.40 mm per side is 0.80 mm. Passing means the pocket is the size you +intended, not that the plate fits โ€” only a test print shows that. + +Then always run `snapshot.py` and confirm the pocket is on the face you meant. + +## Sources + +- ANSI/SLAS 1-2004 (R2012) Footprint Dimensions โ€” +- ANSI/SLAS 2-2004 (R2012) Height Dimensions โ€” +- ANSI/SLAS 3-2004 (R2012) Bottom Outside Flange Dimensions โ€” +- ANSI/SLAS 4-2004 (R2012) Well Positions โ€” +- SLAS microplate standards overview โ€” diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/microfluidics.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/microfluidics.md new file mode 100644 index 00000000..b9af1ae2 --- /dev/null +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/microfluidics.md @@ -0,0 +1,162 @@ +--- +title: "Microfluidic chips, molds, and flow cells" +task: "" +lineage_type: import +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/lab-hardware-cad/references/microfluidics.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 +prompt_class: unknown +upstream_changes: accepted +author: upstream +validated: false +--- + +# Microfluidic chips, molds, and flow cells + +Channel networks, soft-lithography molds, printed chips, gaskets, and manifolds. + +## First: decide what you are actually modelling + +This is the error that wastes the most time in microfluidic CAD. Three different objects get +called "the chip": + +| Object | Channels are | Made by | +| --- | --- | --- | +| **Mold / master** | **Raised ridges** (positive relief) | Photolithography on a wafer, SLA print, or micromilling | +| **Cast chip** | **Recessed grooves** (negative of the mold) | PDMS cast against the mold, then bonded to a substrate | +| **Directly-fabricated chip** | **Recessed grooves or enclosed lumens** | Printed, milled, or laser-cut directly | + +A model that is correct as a chip is exactly wrong as a mold. Put the polarity in the module +docstring and in a named parameter, and **verify it in the snapshot** โ€” inverted polarity is +invisible in the bounding box, the volume, and the validity check, and obvious in the render. + +```python +polarity = "mold" # "mold" = raised ridges; "chip" = recessed grooves +``` + +If casting PDMS, the mold also needs a **surrounding wall or a casting frame** to contain the +uncured polymer, and enough flat land around the features for the cast part to release. + +## Channel cross-section and aspect ratio + +Channels are usually rectangular because that is what planar fabrication produces. Two failure +modes bound the aspect ratio, and both are geometric: + +- **Roof sag / collapse** โ€” a channel much wider than it is tall has an unsupported ceiling. In + PDMS the roof bows down and can stick to the floor. Commonly cited guidance keeps + **width : height below roughly 10 : 1**; wide channels need support pillars. +- **Sidewall collapse** โ€” a mold ridge much taller than it is wide falls over or fails to release. + Keep **height : width below roughly 10 : 1** on the mold. + +Treat both as rules of thumb, not guarantees: the real limits depend on PDMS mixing ratio, cure +schedule, and applied pressure. For anything load-bearing or high-pressure, prototype. + +Also keep **channel-to-channel spacing at least the channel height**, so the wall between two +channels does not deflect or leak, and leave a flat **bonding land** โ€” typically 1 mm or more of +uninterrupted flat surface around the network perimeter โ€” for plasma or adhesive bonding. + +## Minimum features by process + +Achievable feature size drives the entire design, and the range across processes is three orders +of magnitude. Confirm against your specific tool before committing. + +| Process | Practical minimum channel | Notes | +| --- | --- | --- | +| SU-8 photolithography | ~1-10 ยตm wide, 1-200+ ยตm tall | The reference process for soft lithography. Feature height is set by spin speed and resist grade. | +| Two-photon / ยตSLA | ~10-50 ยตm | Small build volume, slow, expensive. | +| Desktop SLA / DLP | ~200-500 ยตm | Uncured resin is very hard to clear from smaller lumens. Enclosed channels below ~0.5 mm frequently print blocked. | +| Micromilling | ~100 ยตm | Set by end-mill diameter; depth limited by tool aspect ratio. Leaves tool marks that scatter light. | +| FDM | Not suitable for sealed channels | Layer porosity leaks. Use only for holders and manifolds. | +| Laser-cut film / gasket | ~200 ยตm | Excellent for stacked-layer devices and gaskets. | + +**Design enclosed printed channels for drainage.** Every lumen needs a path for uncured resin to +escape, and orientation on the build plate determines whether it drains. If the user is printing, +say which way up. + +## Ports and tubing + +The port is where most chips leak. Options, roughly in order of how common they are in a research +lab: + +- **Direct tubing insertion** โ€” a bore slightly *under* the tubing OD so the tubing seals by + interference. For 1/16 inch OD tubing (1.5875 mm), a bore around 1.5 mm in PDMS is typical. This + works in elastomer and fails in rigid printed parts, which crack instead of gripping. +- **Luer taper** โ€” the standard syringe interface, a **6% taper** (ISO 80369-7 supersedes the + legacy ISO 594 series for medical use). Convenient, low pressure only. If you model a Luer taper, + get the profile from the standard, not from memory. +- **Threaded fittings** โ€” flat-bottom **1/4-28 UNF** is the common lab standard for low-pressure + fluidics; **10-32 coned** is used at higher pressures. These need a tapped or heat-set-insert + port and a matching flat sealing face. +- **Barbs** โ€” reliable with soft tubing and a clamp, bulky. + +Whichever you choose, the sealing surface must be **flat and normal to the port axis**. A port +face left at a printed layer angle will not seal. + +## Dead volume + +Dead volume dominates the response time of any perfusion or gradient device, and it is trivially +computable, so compute it rather than estimating: + +``` +V = pi * r^2 * L # round tubing / bore +V = w * h * L # rectangular channel +``` + +Report the volume of every connecting bore alongside the channel network volume. A 20 mm long +1 mm bore holds ~15.7 ยตL, which is often larger than the entire channel network it feeds. + +## Flow regime sanity check + +Microfluidic flow is almost always laminar, but state it rather than assuming: + +``` +Re = rho * v * D_h / mu +D_h = 2 * w * h / (w + h) # hydraulic diameter, rectangular channel +``` + +For water in a 100 ยตm channel at 1 mm/s, Re is of order 0.1 โ€” deeply laminar, so mixing is +diffusive only. If the design depends on mixing, it needs a mixer geometry (serpentine, +herringbone, or split-and-recombine); relying on turbulence will not work at these scales. + +Pressure drop for a rectangular channel scales steeply with the smaller dimension. **Halving +channel height raises pressure drop by roughly an order of magnitude.** Check that the intended +pump or syringe can actually deliver it before finalising the cross-section. + +## Material and optical constraints + +- **PDMS** absorbs small hydrophobic molecules and is gas-permeable. Both are sometimes features + (oxygenation in organ-on-chip) and sometimes fatal to an assay (drug studies). +- **SLA resins** are frequently cytotoxic uncured and often still after a nominal cure. For cell + work, require post-cure plus a documented biocompatibility check, or use a different process. + See `references/fabrication-limits.md`. +- **Autofluorescence** matters for any fluorescence readout. Most printed resins autofluoresce + strongly. Image through glass or a thin COC/COP film, not through printed material. +- **Optical path**: printed and milled surfaces scatter. Any imaging window should be a bonded + coverslip or film, and the model must specify its thickness so the objective working distance + works out. + +## Checks to run + +```bash +python scripts/gen.py chip_model.py --outdir out/ +python scripts/check.py facts out/chip.step +python scripts/snapshot.py out/chip.step --out out/chip.png +``` + +`facts` gives the volume; compare it against your hand-computed channel volume as an independent +check that the network is actually open and connected. A network modelled as a solid rather than a +cavity shows up immediately as a volume far larger than expected. + +Then read the snapshot and confirm, explicitly: + +1. **Polarity** โ€” ridges for a mold, grooves for a chip. +2. Every port lands on the channel it should, and passes fully through to the surface. +3. The bonding land is continuous around the network. +4. No channel has been closed off or consumed by a fillet. + +## Sources + +- ISO 80369-7 (Luer connectors for intravascular applications) supersedes the ISO 594 series. + Obtain the taper profile from the standard itself. +- Aspect-ratio and spacing guidance here is standard soft-lithography practice; the numerical + limits are rules of thumb and depend on material and process. Prototype before committing. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/optomechanics.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/optomechanics.md new file mode 100644 index 00000000..99e3afbc --- /dev/null +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/optomechanics.md @@ -0,0 +1,159 @@ +--- +title: "Optomechanical mounts and breadboard hardware" +task: "" +lineage_type: import +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/lab-hardware-cad/references/optomechanics.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 +prompt_class: unknown +upstream_changes: accepted +author: upstream +validated: false +--- + +# Optomechanical mounts and breadboard hardware + +Parts that bolt to an optical table, join a cage system, hold an optic or a sample in a beam path, +or carry a camera or objective. + +Verified dimensions are in `assets/standards.json`: + +```bash +python scripts/check.py standards --show optical-breadboard-metric +python scripts/check.py standards --show cage-system-30mm +python scripts/check.py standards --show sm1-lens-tube-thread +``` + +## Ask which system before you model anything + +**Metric and imperial optical hardware are not interchangeable, and the difference is small enough +to look like a rounding error and large enough to prevent assembly.** + +| | Metric | Imperial | +| --- | --- | --- | +| Grid pitch | 25.0 mm | 25.4 mm (1 inch) | +| Tapped hole | M6 x 1.0 | 1/4-20 UNC | +| Typical border | 12.5 mm | 12.7 mm | + +Over a four-hole span the grids differ by **1.6 mm** โ€” far more than any clearance hole absorbs. +There is no way to infer which the user has from the request. Ask. If the answer is unavailable, +model the mounting features as **slots along the bolt line** rather than round holes, which +tolerates both, and say that is what you did and why. + +## Mounting to the table + +- Use **clearance holes, not tapped holes**, in the part. The table is tapped; the part is + clearanced. For M6 use 6.6 mm (normal fit) in a printed part rather than 6.4 mm โ€” printed holes + come out undersize. +- **Counterbore for the screw head** if the part surface must stay clear: roughly 11 mm diameter + for an M6 socket head cap screw, 11.2 mm for 1/4-20. +- **Never rely on more than two holes to locate a part.** Grid tolerance plus print tolerance means + a rigid four-hole pattern will bind. Round hole + slot is the standard fix: one hole locates, the + slot takes up the error. +- Printed parts are compliant. For anything where pointing stability matters, a printed mount is a + prototyping aid, not a final part โ€” thermal drift and creep in polymer are large compared with + optical alignment tolerances. Say so when recommending one. + +## Posts and pedestals + +Common conventions, which vary by vendor โ€” **confirm against the catalogue before use**: + +- Imperial posts are ร˜1/2 inch (12.7 mm), typically tapped 8-32 at one end with a 1/4-20 stud or + clearance at the other. +- Metric posts are ร˜12 mm, typically tapped M4 with an M6 interface to the table. +- A post-holder plus post is height-adjustable but adds a compliant joint; a pedestal or a + solid machined riser is stiffer. + +**Beam height** is a project-wide constant, not a per-part choice. Every mount on the table must +put its optic at the same height. Common conventions are 3 inches (76.2 mm) or 100 mm, but this is +a lab-by-lab choice. Ask for the number, define it once as `beam_height_mm`, and derive every +mount's optic centre from it. + +## 30 mm cage system + +The dominant convention for small free-space assemblies: + +- **Rod spacing 30.0 mm** on a square, centred on the optical axis. +- **Rods ร˜6 mm** (ER series). +- Standard cage plates are 0.35 inch (8.9 mm) thick. + +For a custom cage plate: place four bores on a 30 mm square, put the aperture at the **centroid** +of those four bores, and bore them slightly oversize โ€” around 6.1 mm as a starting point, more for +FDM. A cage plate that binds on the rods is worse than useless because it transmits stress into +the whole assembly. + +Cage plates stack along the rods, so a custom plate's thickness directly consumes optical path +length. Budget it. + +## Lens tube threads (SM series) + +**SM1 is a 1.035 inch-40 thread**, which holds ร˜1 inch (25.4 mm) optics. That is a **0.635 mm +pitch**. + +**Do not print SM threads.** A 0.635 mm pitch is at or below the practical resolution of FDM and +marginal on desktop SLA; a printed SM1 thread will either not engage or will gall and shed +particles into the beam path. Instead: + +- bore a clearance hole and use a purchased SM1 adapter or retaining ring, or +- design for a threaded metal insert, or +- clamp the optic directly with a retaining flange and screws. + +If the design truly requires a printed thread, say explicitly that it needs test printing and is +likely to fail. + +## Holding an optic + +- **Never clamp an optic on its clear aperture.** Contact only the outer annulus of the face or the + edge. Define `clear_aperture_mm` as a named parameter and confirm in the snapshot that nothing + intrudes on it. +- Three-point contact is kinematically correct and does not deform the optic. A continuous + circular seat over-constrains it and induces stress birefringence, which matters for + polarisation work. +- Leave clearance for thermal expansion. A metal-in-polymer mount that is a press fit at 20 ยฐC can + crack or bind across a temperature swing. +- Retaining forces should be light and distributed. A single set screw pressing on glass is a way + to chip glass. + +## Stray light and scatter + +Geometry is not the whole design here, and a STEP file cannot show any of this: + +- Printed surfaces scatter strongly. Any surface that sees the beam should be baffled, angled away + from the optical axis, or treated. +- **Black does not mean non-reflective.** Black resin and black filament are often quite specular. + Specify a genuinely absorbing surface treatment where it matters. +- Thread and layer lines act as diffraction structures near a focus. +- For fluorescence work, printed material near the sample can autofluoresce into the detection + path. + +Flag these to the user; do not silently assume a printed enclosure is light-tight. + +## Checks to run + +```bash +python scripts/gen.py mount_model.py --outdir out/ +python scripts/check.py facts out/mount.step +python scripts/check.py interfaces out/mount.manifest.json +python scripts/snapshot.py out/mount.step --out out/mount.png +``` + +Declare the grid pitch, rod spacing, and bore diameters in the model's `interfaces()` against +`optical-breadboard-metric`, `optical-breadboard-imperial`, or `cage-system-30mm`, so the check +catches a 25.0-for-25.4 substitution rather than leaving it to a reader. + +There is still **no automatic bolt-pattern check** โ€” the interface check compares dimensions, not +hole positions. Compute the pattern in the model from a named `grid_pitch_mm` constant, and confirm +in the snapshot that: + +1. All mounting holes are present and pass fully through. +2. The optic aperture is centred where you intended, and unobstructed. +3. Counterbores are on the accessible face. +4. Nothing intrudes into the clear aperture or the beam path. + +## Sources + +- Thorlabs imperial and metric threading โ€” +- Thorlabs standard 30 mm cage plates โ€” +- Thorlabs SM1 lens tube compatible cage plates โ€” +- Post dimensions, beam heights, and vendor-specific thread conventions in this file are common + conventions rather than published standards. Confirm against the catalogue. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/validation.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/validation.md new file mode 100644 index 00000000..4bb83c4f --- /dev/null +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/lab-hardware-cad/references/validation.md @@ -0,0 +1,135 @@ +--- +title: "Pre-fabrication validation checklist" +task: "" +lineage_type: import +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/lab-hardware-cad/references/validation.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 +prompt_class: unknown +upstream_changes: accepted +author: upstream +validated: false +--- + +# Pre-fabrication validation checklist + +Work through this before telling a user a part is ready to fabricate. Each item names the failure +it catches, because a checklist without consequences gets skipped. + +## 1. Provenance + +- [ ] The STEP was produced by `gen.py` from the current model source. + *Catches: a stale artifact that no longer matches the code you just edited.* +- [ ] A `*.manifest.json` exists alongside it, and its `source.sha256` matches the model file. + *Catches: silently editing an exported STEP, which makes the design unreproducible.* +- [ ] The manifest's `interfaces` block is non-empty and its values are the ones the model + computed after any `--param` override. + *Catches: a static `INTERFACES` list frozen at import, recording pre-override numbers.* +- [ ] Every parameter in the model is named with units. + *Catches: the bare `12.7` nobody can later identify as half an inch.* + +```bash +python scripts/gen.py part_model.py --outdir out/ +``` + +## 2. Geometry is sound + +- [ ] `is_valid` is true. + *Catches: self-intersecting or non-manifold solids that slicers and CAM silently mangle.* +- [ ] `solid_count` is what you expect โ€” usually 1. + *Catches: a boolean that failed and left two disjoint lumps, or a feature floating free of + the body.* +- [ ] Volume is plausible for the part's size and wall thickness. + *Catches: a cavity modelled solid, or a subtract that did nothing.* + +```bash +python scripts/check.py facts out/part.step +``` + +## 3. Interfaces + +- [ ] Every interface dimension has a written source: a standard ID, a vendor drawing, or a user + measurement. **None came from memory.** + *Catches: the single most expensive failure mode in this skill.* +- [ ] Every interface covered by a standard is declared in the model's `interfaces()` and passes + `check.py interfaces`. + *Catches: an interface nobody checked because the outer bounding box could not see it.* +- [ ] Features that receive a standardised component use `intent: "envelope"`. + *Catches: a pocket sized to nominal, which fits only the smaller half of conforming parts.* +- [ ] Any standard entry marked `verified: false` was confirmed against the primary document, or + the user was told it is unconfirmed. + *Catches: propagating a derived number as if it were read from the standard.* +- [ ] Metric vs imperial is confirmed where both exist, and no expression mixes them. + *Catches: the 25.0 vs 25.4 mm grid error, which accumulates to 1.6 mm over four holes.* +- [ ] Interfaces not covered by any bundled standard โ€” a vendor drawing, a measurement โ€” were + reported to the user as unchecked, with the number and its source. + *Catches: a silent gap where the automatic check simply had nothing to say.* + +```bash +python scripts/check.py interfaces out/part.manifest.json + +# one dimension by hand, when it is not declared in the model +python scripts/check.py fit --standard --intent envelope --clearance --value = +``` + +## 4. Fits and assembly + +- [ ] Every mating dimension has a deliberate clearance chosen for the process. + *Catches: nominal-to-nominal fits, which do not assemble.* +- [ ] Multi-part assemblies were checked for interference. + *Catches: parts that overlap in CAD and therefore cannot exist together.* +- [ ] Rigid multi-hole mounting patterns have at least one slot. + *Catches: a four-hole bolt pattern binding on accumulated tolerance.* + +```bash +python scripts/check.py clearance out/a.step out/b.step --min 0.3 +``` + +## 5. Manufacturability + +- [ ] Minimum wall and feature sizes are within the chosen process (`fabrication-limits.md`). +- [ ] Print or machining orientation is stated, and load runs along layers, not across them. +- [ ] Threads use inserts or captive nuts rather than printed threads, unless coarse. +- [ ] Enclosed cavities have a drain path for resin, and support-free access where possible. +- [ ] Milled internal corners have relief for the tool radius. + +## 6. Material + +- [ ] Material is compatible with the **cleaning agent**, not only the sample. + *Catches: acrylic crazing on 70% ethanol; PLA distorting in an autoclave.* +- [ ] Sterilisation method is stated and the material actually survives it. +- [ ] Anything contacting cells, tissue, or animals has a justified material, or contact is + designed out. + *Catches: assuming a printed resin part is cell-safe.* +- [ ] Optical requirements โ€” autofluorescence, scatter, transmission โ€” are addressed if the part is + near a beam or a detector. + +## 7. Visual review โ€” mandatory + +- [ ] A snapshot was rendered **and read** after the most recent generation. +- [ ] Confirmed in the image: features on the intended faces; correct mold/chip polarity; every + port, bore, and boss present, inside the body, and passing through; nothing consumed by a + fillet; clear apertures unobstructed. + +```bash +python scripts/snapshot.py out/part.step --out out/part.png +``` + +**This step is never waived by the numeric checks passing.** `is_valid: true` with a correct +bounding box is fully consistent with a pocket cut on the wrong face or an inverted mold. Those +errors are obvious in the picture and invisible in the numbers. + +## 8. Report + +Give the user, explicitly: + +1. Process and material, and why. +2. Every interface dimension with its source and tolerance. +3. Clearances chosen, and the fit class they came from. +4. What the snapshot showed โ€” described, not merely "a snapshot was generated". +5. Every check that did not pass, and every dimension you could not verify. +6. A recommendation to print a test coupon of the critical interface before committing to the full + part, whenever the design depends on a fit. + +State the unverified items plainly. A part list with one honest "this dimension needs +confirmation" is far more useful than a confident one that is silently wrong. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/SKILL.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/SKILL.md index c6fc9747..d0c26cfe 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/SKILL.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/SKILL.md @@ -1,15 +1,17 @@ --- lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/pi-agent/SKILL.md -upstream_sha: 9c9bd2e9 -imported_at: 2026-06-27 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/SKILL.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: unknown upstream_changes: accepted name: pi-agent -description: Build with and use Pi, the minimal terminal coding harness. Use for installing Pi, configuring providers/models/settings, creating Pi skills/extensions/packages/themes/prompt templates, embedding Pi through the SDK, integrating over RPC or JSON event streams, parsing sessions, developing custom Pi providers and TUI components, or using ecosystem packages such as pi-subagents (delegation/orchestration), pi-mcp-adapter (MCP servers), pi-interview (interactive forms), and pi-web-access (web search, fetching, video understanding). +description: Build with and use Pi, the minimal terminal coding harness. Use for installing Pi, configuring providers/models/settings/environment variables, creating Pi skills/extensions/packages/themes/prompt templates, embedding Pi through the SDK, integrating over RPC or JSON event streams, parsing sessions, running local models through the llama.cpp router, developing custom Pi providers and TUI components, or using ecosystem packages such as pi-subagents (delegation/orchestration), pi-mcp-adapter (MCP servers), pi-interview (interactive forms), and pi-web-access (web search, fetching, video understanding). license: MIT -compatibility: Requires Node.js/npm for Pi CLI and SDK usage. Pi package name is @earendil-works/pi-coding-agent. -metadata: {"version": "1.1", "skill-author": "K-Dense Inc."} +compatibility: Requires Node.js >= 22.19 and npm for Pi CLI and SDK usage. Pi package name is @earendil-works/pi-coding-agent. +metadata: + version: "1.3" + skill-author: K-Dense Inc. --- # Pi Agent @@ -22,10 +24,14 @@ Pick the reference before answering or coding: | User intent | Read | |---|---| +| What Pi is, docs map, install methods | `references/overview.md` | | Install, authenticate, first run | `references/quickstart.md` | -| Day-to-day CLI usage, commands, modes, flags | `references/usage.md` | +| Day-to-day CLI usage, commands, modes, flags, project trust | `references/usage.md` | | Provider auth, API keys, cloud provider setup | `references/providers.md` | -| Custom model entries, local models, proxies | `references/models.md` | +| Custom model entries, local models, proxies, compat flags | `references/models.md` | +| Local llama.cpp router, `/llama`, model download/load | `references/llama-cpp.md` | +| Settings keys and defaults | `references/settings.md` | +| `PI_*` and other environment variables | `references/environment-variables.md` | | Extension development, custom tools, events, commands | `references/extensions.md` | | Custom provider implementation, OAuth, custom streaming | `references/custom-provider.md` | | Embed Pi in Node/TypeScript | `references/sdk.md` | @@ -46,9 +52,9 @@ Pick the reference before answering or coding: ## Build-On-Pi Defaults -Prefer the SDK for Node/TypeScript apps that need type safety, direct state access, in-process custom tools/extensions, or custom resource loading. Use `createAgentSession()` for a single stable session; use `createAgentSessionRuntime()` when the app must replace sessions through new/resume/fork/clone/import flows. +Prefer the SDK for Node/TypeScript apps that need type safety, direct state access, in-process custom tools/extensions, or custom resource loading. Use `createAgentSession()` for a single stable session; use `createAgentSessionRuntime()` when the app must replace sessions through new/resume/fork/clone/import flows. Auth and model lookup go through `ModelRuntime.create()`. -Prefer RPC mode when the client is not Node.js, needs process isolation, or wants a language-agnostic JSONL protocol. Start with `pi --mode rpc --no-session` for stateless subprocess integration, then add session flags when persistence matters. +Prefer RPC mode when the client is not Node.js, needs process isolation, or wants a language-agnostic JSONL protocol. Start with `pi --mode rpc --no-session` for stateless subprocess integration, then add session flags when persistence matters. Split records on `\n` only โ€” Node `readline` is not protocol-compliant. Prefer JSON mode for one-shot command-line pipelines that only need streamed events, not bidirectional control: `pi --mode json "prompt"`. @@ -58,7 +64,7 @@ Use packages when sharing or installing reusable extensions, skills, prompt temp ## Safety Defaults -Pi is local and not sandboxed by default. Treat extensions, packages, skills, shell commands, and project-local `.pi` resources as code with the permissions of the Pi process. For untrusted repos or unattended automation, isolate with Docker, OpenShell, Gondolin, a VM, or a remote sandbox. +Pi is local and not sandboxed by default. Treat extensions, packages, skills, shell commands, and project-local `.pi` resources as code with the permissions of the Pi process. Project trust only guards which project inputs load โ€” it is not a sandbox. For untrusted repos or unattended automation, isolate with Docker, OpenShell, Gondolin, a VM, or a remote sandbox. Do not store secrets in project files. Prefer env vars, `~/.pi/agent/auth.json`, OAuth via `/login`, or command-backed secret lookups in `models.json`/provider config. @@ -71,9 +77,13 @@ pi -p "Summarize this codebase" pi --mode json "List files" pi --mode rpc --no-session pi --provider anthropic --model claude-sonnet-4-5 +pi --model sonnet:high "Solve this complex problem" pi --tools read,grep,find,ls -p "Review this repository" +pi --tui-mode fullscreen +pi install npm:pi-subagents +pi update --all ``` ## Source Coverage -These references summarize the Pi documentation at `https://pi.dev/docs/latest` and each docs page found under it as of this skill version, plus the package pages for `pi-subagents`, `pi-mcp-adapter`, `pi-interview`, and `pi-web-access` at `https://pi.dev/packages/`. When exact API behavior matters, prefer the cited reference page and inspect installed TypeScript definitions under `node_modules/@earendil-works/pi-coding-agent/dist/` and `node_modules/@earendil-works/pi-ai/dist/`. +These references summarize the Pi documentation at `https://pi.dev/docs/latest` and every docs page found under it, as of Pi **0.84.2** (docs source: `packages/coding-agent/docs/` in `https://github.com/earendil-works/pi`, formerly `pi-mono`). They also cover the package pages for `pi-subagents`, `pi-mcp-adapter`, `pi-interview`, and `pi-web-access` at `https://pi.dev/packages/`, cross-checked against the published npm READMEs and package docs (`pi-web-access` 0.22.0, `pi-mcp-adapter` 2.25.0, `pi-subagents` 0.49.0, `pi-interview` 0.11.0). When exact API behavior matters, prefer the cited reference page and inspect installed TypeScript definitions under `node_modules/@earendil-works/pi-coding-agent/dist/` and `node_modules/@earendil-works/pi-ai/dist/`. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/compaction.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/compaction.md index 6c940db7..c8de2d88 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/compaction.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/compaction.md @@ -2,9 +2,9 @@ title: "Compaction and Branch Summarization" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/pi-agent/references/compaction.md -upstream_sha: 9c9bd2e9 -imported_at: 2026-06-27 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/references/compaction.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: unknown upstream_changes: accepted author: upstream @@ -15,46 +15,66 @@ validated: false Source: https://pi.dev/docs/latest/compaction -Pi uses compaction to summarize older content when context grows too long, and branch summarization to preserve context when changing branches. - -## Mechanisms +Pi has two summarization mechanisms that share the same structured summary format and track file operations cumulatively. | Mechanism | Trigger | Purpose | |---|---|---| -| Compaction | context exceeds threshold or `/compact` | Summarize old messages to free context | +| Compaction | context exceeds threshold, or `/compact` | Summarize old messages to free context | | Branch summarization | `/tree` navigation | Preserve context when switching branches | +Both use fresh routing session IDs and, where the provider supports it, disable prompt-cache writes because these one-off prompts are unlikely to be reused. + ## Auto-Compaction -Triggers when: +Triggers when `contextTokens > contextWindow - reserveTokens`. Defaults: `reserveTokens` 16384, `keepRecentTokens` 20000, configured under `compaction` in global or project settings. `/compact [instructions]` works even with auto-compaction disabled. -```text -contextTokens > contextWindow - reserveTokens -``` +Steps: walk backwards from the newest message accumulating token estimates until `keepRecentTokens` is reached (the cut point) โ†’ collect messages from the previous kept boundary (or session start) to the cut point โ†’ summarize with the structured format, passing any previous summary as iterative context โ†’ append a `CompactionEntry` โ†’ rebuild the context for the next request as summary plus messages from `firstKeptEntryId`. -Defaults: `reserveTokens` 16384, `keepRecentTokens` 20000. Configure under `compaction` in settings. +On repeated compactions the summarized span starts at the previous compaction's kept boundary (`firstKeptEntryId`), not at the compaction entry, falling back to the entry after the previous compaction when that kept entry is not on the path. This re-includes messages that survived the earlier pass. `tokensBefore` is recalculated from the rebuilt context before writing the new entry. -Compaction finds a cut point, summarizes old messages, appends a `CompactionEntry`, then reloads session context as summary plus kept messages. - -Valid cut points: user messages, assistant messages, bash execution messages, and custom messages. Pi never cuts at tool results. +Valid cut points: user messages, assistant messages, bash execution messages, and custom messages (`custom_message`, `branch_summary`). Pi never cuts at tool results. ## Split Turns -If one turn exceeds `keepRecentTokens`, Pi may cut mid-turn at an assistant message, generate a history summary plus turn-prefix summary, and merge them. +A turn starts with a user message and includes all assistant responses and tool calls until the next user message. When a single turn exceeds `keepRecentTokens`, the cut lands mid-turn at an assistant message (`isSplitTurn`). Pi then generates two summaries โ€” a history summary for previous context and a turn-prefix summary for the early part of the split turn โ€” and merges them. + +## Branch Summarization + +On `/tree` navigation to a different branch: find the deepest common ancestor, walk from the old leaf back to it, include messages up to the token budget newest-first, summarize, and append a `BranchSummaryEntry` at the navigation point โ€” the summary lands on the destination branch's new leaf, not on the branch being left. + +Both mechanisms extract file operations from the tool calls being summarized **and** from previous compaction/branch-summary `details`, so read/modified file tracking accumulates across passes. ## Entry Shapes -`CompactionEntry` contains `type`, `id`, `parentId`, `timestamp`, `summary`, `firstKeptEntryId`, `tokensBefore`, optional `fromHook`, and optional `details`. +`CompactionEntry`: `type`, `id`, `parentId`, `timestamp`, `summary`, `firstKeptEntryId`, `tokensBefore`, optional `usage` (LLM usage that generated the summary; counted in session totals), optional `fromHook` (legacy name for "provided by extension"), optional `details`. -`BranchSummaryEntry` contains `type`, `id`, `parentId`, `timestamp`, `summary`, `fromId`, optional `fromHook`, and optional `details`. +`BranchSummaryEntry`: same plus `fromId` instead of `firstKeptEntryId`. -Default details track `readFiles` and `modifiedFiles`; extensions may store JSON-serializable custom details. +Default `details` is `{ readFiles: string[], modifiedFiles: string[] }`; extensions may store any JSON-serializable structure. Newer harness-generated compactions also embed `retainedTail` โ€” see `references/session-format.md`. + +## Summary Format + +Sections: `## Goal`, `## Constraints & Preferences`, `## Progress` (Done / In Progress / Blocked), `## Key Decisions`, `## Next Steps`, `## Critical Context`, then `` and `` blocks. + +## Message Serialization + +`serializeConversation()` renders messages as `[User]:`, `[Assistant thinking]:`, `[Assistant]:`, `[Assistant tool calls]:`, `[Tool result]:` lines so the model does not treat the input as a conversation to continue. Tool results are truncated to 2000 characters during serialization, with a marker showing how many characters were dropped โ€” `read` and `bash` results are usually the largest contributors to context. ## Extension Hooks -`session_before_compact` can cancel or provide custom compaction. Convert messages with `convertToLlm()` and `serializeConversation()` when using a custom summarizer. +`session_before_compact` receives `{ preparation, branchEntries, customInstructions, reason, willRetry, signal }`. `preparation` exposes `messagesToSummarize`, `turnPrefixMessages`, `previousSummary`, `fileOps`, `tokensBefore`, `firstKeptEntryId`, and `settings`; `reason` is `"manual"`, `"threshold"`, or `"overflow"`; `willRetry` indicates overflow recovery. Return `{ cancel: true }` or `{ compaction: { summary, firstKeptEntryId, tokensBefore, usage?, details? } }`. -`session_before_tree` can cancel navigation or provide a custom branch summary when the user chose to summarize. +To summarize with your own model, convert messages first: + +```ts +import { convertToLlm, serializeConversation } from "@earendil-works/pi-coding-agent"; + +const text = serializeConversation(convertToLlm(preparation.messagesToSummarize)); +``` + +`session_before_tree` receives `{ preparation, signal }` with `targetId`, `oldLeafId`, `commonAncestorId`, `entriesToSummarize`, and `userWantsSummary`. It always fires, whether or not the user chose to summarize. Return `{ cancel: true }` to cancel navigation, or `{ summary: { summary, usage?, details? } }` (used only when `userWantsSummary`). + +For direct programmatic summarization, `generateSummary()` returns the text and `generateSummaryWithUsage()` returns `{ text, usage }`. ## Settings diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/json.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/json.md index 713b43e0..be51d5ce 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/json.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/json.md @@ -2,9 +2,9 @@ title: "JSON Event Stream Mode" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/pi-agent/references/json.md -upstream_sha: 9c9bd2e9 -imported_at: 2026-06-27 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/references/json.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: unknown upstream_changes: accepted author: upstream @@ -23,17 +23,33 @@ pi --mode json "Your prompt" ## Event Types -`AgentSessionEvent` includes base agent events plus queue, compaction, and retry events: +Wire events use `JsonAgentSessionEvent`, which matches `AgentSessionEvent` except that streaming message updates omit cumulative snapshots: -- `agent_start`, `agent_end` -- `turn_start`, `turn_end` -- `message_start`, `message_update`, `message_end` -- `tool_execution_start`, `tool_execution_update`, `tool_execution_end` -- `queue_update` -- `compaction_start`, `compaction_end` -- `auto_retry_start`, `auto_retry_end` +```typescript +type WithoutPartial = T extends { partial: unknown } ? Omit : T; -`queue_update` emits full pending steering and follow-up queues. Compaction events cover manual and automatic compaction. +type JsonAgentSessionEvent = + | Exclude + | { type: "message_update"; usage: Usage; assistantMessageEvent: WithoutPartial }; +``` + +`AgentSessionEvent` is `AgentEvent` plus session-level events: + +- `queue_update` โ€” `{ steering: readonly string[], followUp: readonly string[] }`, emitted whenever either queue changes +- `compaction_start` โ€” `{ reason: "manual" | "threshold" | "overflow" }` +- `compaction_end` โ€” `{ reason, result: CompactionResult | undefined, aborted, willRetry, errorMessage? }` +- `auto_retry_start` โ€” `{ attempt, maxAttempts, delayMs, errorMessage }` +- `auto_retry_end` โ€” `{ success, attempt, finalError? }` +- `summarization_retry_scheduled` โ€” `{ attempt, maxAttempts, delayMs, errorMessage }` +- `summarization_retry_attempt_start` โ€” `{ source: "branchSummary" }` or `{ source: "compaction", reason }` +- `summarization_retry_finished` + +Base `AgentEvent` types: + +- `agent_start`, `agent_end` (`messages`) +- `turn_start`, `turn_end` (`message`, `toolResults`) +- `message_start` (`message`), `message_update` (`usage`, `assistantMessageEvent`), `message_end` (`message`) +- `tool_execution_start` (`toolCallId`, `toolName`, `args`), `tool_execution_update` (+ `partialResult`), `tool_execution_end` (`result`, `isError`) ## Output Format @@ -43,15 +59,20 @@ First line is the session header: {"type":"session","version":3,"id":"uuid","timestamp":"...","cwd":"/path"} ``` -Subsequent lines are events: +Then events as they occur: ```json {"type":"agent_start"} {"type":"turn_start"} -{"type":"message_update","message":{},"assistantMessageEvent":{"type":"text_delta","delta":"Hello"}} +{"type":"message_start","message":{"role":"assistant","content":[]}} +{"type":"message_update","usage":{},"assistantMessageEvent":{"type":"text_delta","contentIndex":0,"delta":"Hello"}} +{"type":"message_end","message":{}} +{"type":"turn_end","message":{},"toolResults":[]} {"type":"agent_end","messages":[]} ``` +`message_update` records are delta-only: they omit both the cumulative `message` field and `assistantMessageEvent.partial` to keep stream size linear. The top-level `usage` field carries the latest cumulative provider-reported usage and may stay zero when a provider only reports usage at completion. Assemble live text, thinking, or tool-call arguments from `contentIndex` and `delta`; `message_end` holds the final authoritative message. + ## Example ```bash diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/keybindings.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/keybindings.md index f2921457..8219409c 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/keybindings.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/keybindings.md @@ -2,9 +2,9 @@ title: "Keybindings" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/pi-agent/references/keybindings.md -upstream_sha: 9c9bd2e9 -imported_at: 2026-06-27 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/references/keybindings.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: unknown upstream_changes: accepted author: upstream @@ -15,34 +15,57 @@ validated: false Source: https://pi.dev/docs/latest/keybindings -All shortcuts can be customized in `~/.pi/agent/keybindings.json`. Run `/reload` after editing. +All shortcuts are customizable in `~/.pi/agent/keybindings.json`, which uses the same namespaced ids Pi uses internally and that extension authors pass to `keyHint()` and the injected `keybindings` manager. Pre-namespaced ids from older configs (`cursorUp`, `expandTools`, โ€ฆ) migrate automatically on startup. Run `/reload` after editing. ## Key Format -Use `modifier+key`; modifiers are `ctrl`, `shift`, and `alt`. Keys include letters, digits, special keys, function keys, and symbols. +`modifier+key` where modifiers are `ctrl`, `shift`, `alt`, `super` (combinable, e.g. `ctrl+shift+x`, `alt+ctrl+1`, `super+k`, `ctrl+super+k`). `super` bindings need a terminal that reports the modifier separately, typically via the Kitty keyboard protocol. Keys: -## Common Defaults +- Letters `a-z`, digits `0-9` +- Special: `escape`/`esc`, `enter`/`return`, `tab`, `space`, `backspace`, `delete`, `insert`, `clear`, `home`, `end`, `pageUp`, `pageDown`, `up`, `down`, `left`, `right` +- Function: `f1`โ€“`f12` +- Symbols: `` ` ``, `-`, `=`, `[`, `]`, `\`, `;`, `'`, `,`, `.`, `/`, `!`, `@`, `#`, `$`, `%`, `^`, `&`, `*`, `(`, `)`, `_`, `+`, `|`, `~`, `{`, `}`, `:`, `<`, `>`, `?` -Editor movement: arrows, Ctrl+B/F, Alt+Left/Right, Ctrl+A/E, PageUp/PageDown. +## Actions -Deletion: Backspace, Delete/Ctrl+D, Ctrl+W, Alt+Backspace, Alt+D, Ctrl+U, Ctrl+K. +**`tui.editor.*` cursor** โ€” `cursorUp` (up; browses older history at the top), `cursorDown` (down; browses newer history at the bottom), `historyPrevious` / `historyNext` (no defaults), `cursorLeft` (left, ctrl+b), `cursorRight` (right, ctrl+f), `cursorWordLeft` (alt+left, ctrl+left, alt+b), `cursorWordRight` (alt+right, ctrl+right, alt+f), `cursorLineStart` (home, ctrl+home, ctrl+a), `cursorLineEnd` (end, ctrl+end, ctrl+e), `jumpForward` (ctrl+]), `jumpBackward` (ctrl+alt+]), `pageUp` (pageUp, ctrl+pageUp), `pageDown` (pageDown, ctrl+pageDown). -Input: `tui.input.submit` is Enter, `tui.input.newLine` is Shift+Enter, `tui.input.tab` is Tab. +The dedicated `historyPrevious`/`historyNext` actions always change history entries regardless of cursor position in a multiline prompt, and explicit history bindings take precedence over application actions while the main editor is focused โ€” binding `tui.editor.historyPrevious` to `ctrl+p` overrides model cycling in that context without changing `Ctrl+P` in selectors. -Application: Escape interrupts, Ctrl+C clears editor/copies selection depending context, Ctrl+D exits when editor empty, Ctrl+G opens external editor, Ctrl+V/Alt+V pastes image. +**`tui.editor.*` deletion** โ€” `deleteCharBackward` (backspace), `deleteCharForward` (delete, ctrl+d), `deleteWordBackward` (ctrl+w, alt+backspace), `deleteWordForward` (alt+d, alt+delete), `deleteToLineStart` (ctrl+u), `deleteToLineEnd` (ctrl+k). -Models: Ctrl+L opens model selector, Ctrl+P cycles forward, Shift+Ctrl+P cycles backward, Shift+Tab cycles thinking level, Ctrl+T toggles thinking block display. +**`tui.editor.*` kill ring** โ€” `yank` (ctrl+y), `yankPop` (alt+y), `undo` (ctrl+-). -Messages: Ctrl+O expands tools, Alt+Enter queues follow-up, Alt+Up retrieves queued messages. +**`tui.input.*`** โ€” `newLine` (shift+enter, ctrl+j), `submit` (enter), `tab` (tab), `copy` (ctrl+c). + +**`tui.select.*`** โ€” `up`, `down`, `pageUp`, `pageDown`, `confirm` (enter), `cancel` (escape, ctrl+c). + +**`tui.altScreen.*` fullscreen viewport** (only in `--tui-mode fullscreen`) โ€” `pageUp` (pageUp), `pageDown` (pageDown), `halfPageUp` / `halfPageDown` / `lineUp` / `lineDown` (no defaults), `previousPrompt` (ctrl+shift+up), `nextPrompt` (ctrl+shift+down), `search` (ctrl+shift+f), `searchNext` (enter, ctrl+g), `searchPrevious` (shift+enter, ctrl+shift+g), `searchClose` (escape), `top` (home), `bottom` (end). + +These target the primary transcript scroll region and take precedence over editor bindings, so in fullscreen mode unmodified `home`/`end`/`pageUp`/`pageDown` drive the transcript while their `ctrl` variants still drive the editor; outside fullscreen both variants drive the editor. Rebind normally to change the routing (`"tui.altScreen.pageUp": "ctrl+pageUp"`), or set `[]` to disable a transcript shortcut. Mouse-wheel and two-finger input scroll the region under the pointer, OSC 8 hyperlinks open on click, and primary-button drag selects text and copies it (holding at an edge auto-scrolls). + +**`app.*` application** โ€” `interrupt` (escape), `clear` (ctrl+c; clears the editor first, exits on a second press), `exit` (ctrl+d when editor empty), `suspend` (ctrl+z; none on Windows), `editor.external` (ctrl+g), `clipboard.pasteImage` (ctrl+v; alt+v on Windows โ€” pastes images or text). + +**`app.session.*`** โ€” `new`, `tree`, `fork`, `resume` (no defaults), `togglePath` (ctrl+p), `toggleSort` (ctrl+s), `toggleNamedFilter` (ctrl+n), `rename` (ctrl+r), `delete` (ctrl+d), `deleteNoninvasive` (ctrl+backspace). + +**`app.model.*` / `app.thinking.*`** โ€” `model.select` (ctrl+l), `model.cycleForward` (ctrl+p), `model.cycleBackward` (shift+ctrl+p), `thinking.cycle` (shift+tab), `thinking.toggle` (ctrl+t). + +**`app.tools.*` / `app.message.*`** โ€” `tools.expand` (ctrl+o), `message.copy` (ctrl+x), `message.followUp` (alt+enter), `message.dequeue` (alt+up). + +**`app.tree.*`** โ€” `foldOrUp` (ctrl+left, alt+left), `unfoldOrDown` (ctrl+right, alt+right), `editLabel` (shift+l), `toggleLabelTimestamp` (shift+t), `filter.default` (ctrl+d), `filter.noTools` (ctrl+t), `filter.userOnly` (ctrl+u), `filter.labeledOnly` (ctrl+l), `filter.all` (ctrl+a), `filter.cycleForward` (ctrl+o), `filter.cycleBackward` (shift+ctrl+o). + +**`app.models.*`** (inside the `/scoped-models` selector) โ€” `save` (ctrl+s), `enableAll` (ctrl+a), `clearAll` (ctrl+x), `toggleProvider` (ctrl+p), `reorderUp` (alt+up), `reorderDown` (alt+down). ## Custom Config ```json { - "tui.editor.cursorUp": ["up", "ctrl+p"], - "tui.editor.cursorDown": ["down", "ctrl+n"], + "tui.editor.historyPrevious": "ctrl+p", + "tui.editor.historyNext": "ctrl+n", "tui.editor.deleteWordBackward": ["ctrl+w", "alt+backspace"] } ``` -User config overrides defaults. Native Windows has no default `app.suspend` binding; WSL uses normal Unix Ctrl+Z/fg behavior. +Each action takes a single key or an array; user config overrides defaults. The docs include full Emacs and Vim example configs. + +On native Windows, `app.suspend` has no default because Windows terminals lack Unix job control โ€” bound manually, Pi shows a status message instead of suspending. WSL keeps normal `ctrl+z`/`fg` behavior. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/overview.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/overview.md index a1fbf223..666fcd4d 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/overview.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/overview.md @@ -2,9 +2,9 @@ title: "Pi Documentation Overview" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/pi-agent/references/overview.md -upstream_sha: 9c9bd2e9 -imported_at: 2026-06-27 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/references/overview.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: unknown upstream_changes: accepted author: upstream @@ -15,14 +15,14 @@ validated: false Source: https://pi.dev/docs/latest -Pi is a minimal terminal coding harness. The core stays small and most workflow-specific behavior lives in TypeScript extensions, skills, prompt templates, themes, and Pi packages. +Pi is a minimal terminal coding harness. The core stays small and most workflow-specific behavior lives in TypeScript extensions, skills, prompt templates, themes, and Pi packages. Positioning on `https://pi.dev/`: "There are many agent harnesses but this one is yours" โ€” four run modes (interactive, print/JSON, RPC, SDK), 15+ providers with mid-session model switching, tree-structured shareable sessions, steering and follow-up during a run, and extensible primitives instead of baked-in features. ## Top-Level Areas -- Start here: quickstart, usage, providers, security, containerization, settings, keybindings, sessions, compaction. +- Start here: quickstart, usage, providers, llama.cpp, security, containerization, settings, keybindings, sessions, compaction. - Customization: extensions, skills, prompt templates, themes, Pi packages, custom models, custom providers. - Programmatic usage: SDK, RPC mode, JSON event stream mode, TUI components. -- Reference: session file format and SessionManager API. +- Reference: environment variables, session file format and SessionManager API. - Platform setup: Windows, Termux, tmux, terminal setup, shell aliases. - Development: local source setup, rebranding, debug logs, tests, package structure. @@ -33,10 +33,18 @@ npm install -g --ignore-scripts @earendil-works/pi-coding-agent pi ``` -On Linux/macOS, the installer is also available: +On Linux/macOS the installer is also available: ```bash curl -fsSL https://pi.dev/install.sh | sh ``` +pnpm and bun global installs work too (`pnpm add -g --ignore-scripts ...`, `bun add -g --ignore-scripts ...`). + Authenticate with `/login` for subscription providers or set API keys such as `ANTHROPIC_API_KEY` before startup. + +## Ecosystem + +Package gallery at `https://pi.dev/packages` lists community extensions tagged `pi-package`. Source: `https://github.com/earendil-works/pi` (formerly `pi-mono`; docs live under `packages/coding-agent/docs/`, and doc pages still link the old repo name, which redirects). + +Pi requires Node.js >= 22.19.0. The published version this skill was written against is `0.84.2` (`https://pi.dev/api/latest-version` reports the current one). diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/quickstart.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/quickstart.md index fea68908..9e3932e6 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/quickstart.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/quickstart.md @@ -2,9 +2,9 @@ title: "Quickstart" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/pi-agent/references/quickstart.md -upstream_sha: 9c9bd2e9 -imported_at: 2026-06-27 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/references/quickstart.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: unknown upstream_changes: accepted author: upstream @@ -23,42 +23,45 @@ npm install -g --ignore-scripts @earendil-works/pi-coding-agent `--ignore-scripts` disables dependency lifecycle scripts. Pi does not need install scripts for normal npm installs. -Uninstall with the matching package manager. Curl and npm installs are removed with: +Uninstall with the package manager that installed Pi. The curl installer uses global npm, so curl and npm installs both remove with npm: ```bash npm uninstall -g @earendil-works/pi-coding-agent +pnpm remove -g @earendil-works/pi-coding-agent +yarn global remove @earendil-works/pi-coding-agent +bun uninstall -g @earendil-works/pi-coding-agent ``` Uninstalling Pi leaves settings, credentials, sessions, and installed packages in `~/.pi/agent/`. ## Authenticate -Use `/login` in interactive mode for subscription providers: Claude Pro/Max, ChatGPT Plus/Pro (Codex), and GitHub Copilot. API-key providers can be configured by environment variable or stored through `/login` in `~/.pi/agent/auth.json`. +Use `/login` in interactive mode for subscription providers; built-in subscription logins include Claude Pro/Max, ChatGPT Plus/Pro (Codex), and GitHub Copilot. API-key providers can be configured by environment variable or stored through `/login` in `~/.pi/agent/auth.json`. ```bash export ANTHROPIC_API_KEY=sk-ant-... pi ``` -## First Session +See `references/providers.md` for the full provider list. -Run Pi in the project directory: +## First Session ```bash cd /path/to/project pi ``` -By default, the model gets `read`, `write`, `edit`, and `bash`. Additional read-only built-ins `grep`, `find`, and `ls` are available through tool options. +By default the model gets four tools: `read`, `write` (create/overwrite), `edit` (patch), and `bash`. Read-only built-ins `grep`, `find`, and `ls` are available through tool options. Pi works in the current directory and can modify files there โ€” use git or another checkpointing workflow for rollback. ## Project Instructions Pi loads context files at startup: - `~/.pi/agent/AGENTS.md` -- `AGENTS.md` or `CLAUDE.md` from parent directories and current directory +- `AGENTS.md` or `CLAUDE.md` from parent directories and the current directory -Run `/reload` or restart after changing context files. +A directory containing `AGENTS.override.md` contributes that file instead of its `AGENTS.md`/`CLAUDE.md`. Run `/reload` or restart after changing context files. ## Common First Tasks @@ -73,8 +76,11 @@ pi --name "my task" pi --session pi -p "Summarize this codebase" cat README.md | pi -p "Summarize this text" +pi -p @screenshot.png "What's in this image?" pi --mode json "List files" pi --mode rpc --no-session ``` -Use `!command` to run shell and send output to the model. Use `!!command` to run without adding output to model context. +Use `!command` to run shell and send output to the model; `!!command` runs without adding output to model context. Paste text or images with Ctrl+V (Alt+V on Windows), or drag images into supported terminals. + +Switch models with `/model` or Ctrl+L, cycle thinking level with Shift+Tab, and cycle scoped models with Ctrl+P / Shift+Ctrl+P. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/session-format.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/session-format.md index 599fccbf..bb81d095 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/session-format.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/session-format.md @@ -2,9 +2,9 @@ title: "Session File Format" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/pi-agent/references/session-format.md -upstream_sha: 9c9bd2e9 -imported_at: 2026-06-27 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/references/session-format.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: unknown upstream_changes: accepted author: upstream @@ -15,7 +15,7 @@ validated: false Source: https://pi.dev/docs/latest/session-format -Sessions are JSONL files. Each line is a JSON object with `type`. Entries form a tree through `id` and `parentId`. +Sessions are JSONL files. Each line is a JSON object with a `type`. Entries form a tree through `id` / `parentId`, enabling in-place branching without new files. ## Location @@ -23,41 +23,81 @@ Sessions are JSONL files. Each line is a JSON object with `type`. Entries form a ~/.pi/agent/sessions/----/_.jsonl ``` -Existing sessions auto-migrate to current version. Version 3 renamed `hookMessage` role to `custom`. +`` is the working directory with `/` replaced by `-`. Delete sessions by removing the `.jsonl` file, or from `/resume` with Ctrl+D (Pi uses the `trash` CLI when available). -## Message Content +## Versions -Messages use content blocks: `text`, `image`, `thinking`, and `toolCall`. Base roles include `user`, `assistant`, and `toolResult`. Extended roles include `bashExecution`, `custom`, `branchSummary`, and `compactionSummary`. +- v1: linear entry sequence (legacy, auto-migrated on load) +- v2: tree structure with `id`/`parentId` +- v3: renamed the `hookMessage` role to `custom` (extensions unification) -Assistant messages include `api`, `provider`, `model`, `usage`, `stopReason`, optional `errorMessage`, timestamp, and content blocks. +Existing sessions auto-migrate to v3 when loaded. + +## Content Blocks + +`TextContent { type: "text", text }`, `ImageContent { type: "image", data (base64), mimeType }`, `ThinkingContent { type: "thinking", thinking }`, `ToolCall { type: "toolCall", id, name, arguments }`. + +## Message Types + +Base (from `pi-ai`): + +- `UserMessage` โ€” `role: "user"`, `content` string or `(Text|Image)[]`, `timestamp` (Unix ms) +- `AssistantMessage` โ€” `content: (Text|Thinking|ToolCall)[]`, `api`, `provider`, `model`, `usage`, `stopReason` โˆˆ `stop`/`length`/`toolUse`/`error`/`aborted`, optional `errorMessage`, `timestamp` +- `ToolResultMessage` โ€” `toolCallId`, `toolName`, `content: (Text|Image)[]`, optional `details`, optional `usage` (nested LLM work performed by the tool), `isError`, `timestamp` +- `Usage` โ€” `input`, `output`, `cacheRead`, `cacheWrite`, `totalTokens`, and `cost` with the same four fields plus `total` + +The exported pi-ai `StopReason` type also includes `"pending"`, but that value is reserved for partial messages in streaming events. Terminal `done`/`error` messages replace it with a completion reason before Pi persists the assistant message, so `"pending"` should never appear in session JSONL. + +Extended (from `pi-coding-agent`): + +- `BashExecutionMessage` โ€” `command`, `output`, `exitCode`, `cancelled`, `truncated`, optional `fullOutputPath`, optional `excludeFromContext` (true for `!!`) +- `CustomMessage` โ€” `customType`, `content`, `display`, optional `details` +- `BranchSummaryMessage` โ€” `summary`, `fromId` +- `CompactionSummaryMessage` โ€” `summary`, `tokensBefore` + +`AgentMessage` is the union of all seven. ## Entry Types -- `session`: header, first line, metadata only. -- `message`: wraps an `AgentMessage`. -- `model_change`: model switches. -- `thinking_level_change`: thinking level changes. -- `compaction`: summary of earlier messages with `firstKeptEntryId` and `tokensBefore`. -- `branch_summary`: summary of an abandoned branch. -- `custom`: extension state, not sent to LLM. -- `custom_message`: extension-injected message, sent to LLM. -- `label`: user-defined bookmark on an entry. -- `session_info`: display name metadata. +All entries except the header extend `SessionEntryBase { type, id (8-char hex), parentId (null for the first entry), timestamp (ISO string) }`. + +- `session` โ€” header, first line, metadata only (no `id`/`parentId`): `version`, `id`, `timestamp`, `cwd`, plus `parentSession` for sessions created via `/fork`, `/clone`, or `newSession({ parentSession })` +- `message` โ€” wraps an `AgentMessage` in `message` +- `model_change` โ€” `provider`, `modelId` +- `thinking_level_change` โ€” `thinkingLevel` +- `compaction` โ€” `summary`, `tokensBefore`, plus optional `usage`, `details`, `fromHook`, `firstKeptEntryId` (old format), and `retainedTail` +- `branch_summary` โ€” `summary`, `fromId`, plus optional `usage`, `details`, `fromHook` +- `custom` โ€” `customType`, `data`; extension state, **not** in LLM context. Renderable in the transcript via `pi.registerEntryRenderer(customType, renderer)` +- `custom_message` โ€” `customType`, `content`, `display`, optional `details`; extension-injected and **in** LLM context +- `label` โ€” `targetId`, `label` (set `label` to `undefined` to clear) +- `session_info` โ€” `name`; set via `/name`, `--name`/`-n`, or `pi.setSessionName()`. Shown in `/resume` instead of the first message + +`retainedTail` is a materialized `AgentMessage[]` kept after compaction. Newer harness-generated compactions include it so context rebuilds from that checkpoint without walking entries before the compaction. It is optional only for backward compatibility with sessions that store only `firstKeptEntryId`. ## Context Building -`buildSessionContext()` walks from current leaf to root. If a `CompactionEntry` is on the path, Pi emits the summary first, then messages from `firstKeptEntryId`, then later messages. +`buildContextEntries()` walks from the current leaf to the root and produces the active entry list honoring compaction: + +1. Collect all entries on the path. +2. If a `CompactionEntry` is on the path: include the compaction entry first; if `retainedTail` is present it acts as a self-contained checkpoint and entries after the compaction are included; otherwise include entries from `firstKeptEntryId` to the compaction, then entries after it. +3. Preserve non-message entries in the selected range so interactive mode can render them. + +`buildSessionContext()` builds the LLM message list on top of that: it extracts the current model and thinking level from the full path, then converts entries โ€” `message` โ†’ stored `AgentMessage`, `compaction` โ†’ `compactionSummary` plus `retainedTail` when present, `branch_summary` โ†’ `branchSummary`, `custom_message` โ†’ `CustomMessage`, `custom` โ†’ no context message. ## SessionManager API -Static factories: `create`, `open`, `continueRecent`, `inMemory`, `forkFrom`. +Static creation: `create(cwd, sessionDir?)`, `open(path, sessionDir?)`, `continueRecent(cwd, sessionDir?)`, `inMemory(cwd?)`, `forkFrom(sourcePath, targetCwd, sessionDir?)`. -Listing: `list`, `listAll`. +Static listing: `list(cwd, sessionDir?, onProgress?)`, `listAll(onProgress?)`. -Session management: `newSession`, `setSessionFile`, `createBranchedSession`. +Session management: `newSession({ parentSession? })`, `setSessionFile(path)`, `createBranchedSession(leafId)`. -Append: `appendMessage`, `appendThinkingLevelChange`, `appendModelChange`, `appendCompaction`, `appendCustomEntry`, `appendSessionInfo`, `appendCustomMessageEntry`, `appendLabelChange`. +Appending (each returns an entry ID): `appendMessage`, `appendThinkingLevelChange`, `appendModelChange`, `appendCompaction(summary, firstKeptEntryId, tokensBefore, details?, fromHook?)`, `appendCustomEntry(customType, data?)`, `appendSessionInfo(name)`, `appendCustomMessageEntry(customType, content, display, details?)`, `appendLabelChange(targetId, label)`. -Tree: `getLeafId`, `getLeafEntry`, `getEntry`, `getBranch`, `getTree`, `getChildren`, `getLabel`, `branch`, `resetLeaf`, `branchWithSummary`. +Tree navigation: `getLeafId`, `getLeafEntry`, `getEntry`, `getBranch(fromId?)`, `getTree`, `getChildren`, `getLabel`, `branch(entryId)`, `resetLeaf()`, `branchWithSummary(entryId, summary, details?, fromHook?)`. -Info/context: `buildSessionContext`, `getEntries`, `getHeader`, `getSessionName`, `getCwd`, `getSessionDir`, `getSessionId`, `getSessionFile`, `isPersisted`. +Context and info: `buildContextEntries`, `buildSessionContext`, `getEntries`, `getHeader`, `getSessionName`, `getCwd`, `getSessionDir`, `getSessionId`, `getSessionFile` (undefined in memory), `isPersisted`. + +## Parsing + +Read the file line by line and switch on `entry.type`; treat `entry.version ?? 1` on the header, and ignore unknown types for forward compatibility. For TypeScript definitions inspect `node_modules/@earendil-works/pi-coding-agent/dist/` and `node_modules/@earendil-works/pi-ai/dist/`. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/settings.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/settings.md index 687e8071..60f2ed9b 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/settings.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/settings.md @@ -2,10 +2,10 @@ title: "Settings" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/pi-agent/references/settings.md -upstream_sha: 9c9bd2e9 -imported_at: 2026-06-27 -prompt_class: unknown +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/references/settings.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 +prompt_class: catalogue upstream_changes: accepted author: upstream validated: false @@ -15,58 +15,101 @@ validated: false Source: https://pi.dev/docs/latest/settings -Pi uses JSON settings files. Project settings override global settings; nested objects merge. +Pi uses JSON settings files. Project settings override global settings; nested objects merge key by key. Edit directly or use `/settings` for common options. | Location | Scope | |---|---| | `~/.pi/agent/settings.json` | Global | | `.pi/settings.json` | Project | +Resource paths in the global file resolve relative to `~/.pi/agent`; in the project file relative to `.pi`. Absolute paths and `~` work. + ## Project Trust -Project settings are trust-gated. Interactive startup asks according to `defaultProjectTrust` when project inputs exist and no saved trust decision applies. Non-interactive modes use `defaultProjectTrust` and do not prompt. `--approve` and `--no-approve` override for one run. +Interactive startup asks before trusting a project folder that has project-local settings, resources, or project `.agents/skills` and no saved decision for that folder or a parent in `~/.pi/agent/trust.json`. Non-interactive modes never prompt and use `defaultProjectTrust` (`"ask"` default, `"never"`, `"always"`); `--approve`/`-a` and `--no-approve`/`-na` override for one run. `/trust` saves a decision (including for the immediate parent folder) but does not reload the current session. `pi config` and package commands follow the same flow; `pi update` never prompts. -## Core Settings +## Model and Thinking -Model/thinking: `defaultProvider`, `defaultModel`, `defaultThinkingLevel`, `hideThinkingBlock`, `thinkingBudgets`. +| Setting | Default | Notes | +|---|---|---| +| `defaultProvider` | โ€“ | Provider id | +| `defaultModel` | โ€“ | Model id | +| `defaultThinkingLevel` | โ€“ | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max` | +| `hideThinkingBlock` | `false` | Hide thinking blocks in output | +| `showCacheMissNotices` | `false` | Transcript notices for significant prompt-cache misses | +| `thinkingBudgets` | โ€“ | Custom token budgets per thinking level | -UI/display: `theme`, `quietStartup`, `defaultProjectTrust`, `collapseChangelog`, `enableInstallTelemetry`, `doubleEscapeAction`, `treeFilterMode`, `editorPaddingX`, `autocompleteMaxVisible`, `showHardwareCursor`. +## UI and Display -Compaction: `compaction.enabled`, `compaction.reserveTokens`, `compaction.keepRecentTokens`. +`theme` (`"dark"`), `externalEditor` (Ctrl+G command; takes precedence over `$VISUAL`/`$EDITOR` โ€” use `"code --wait"` for VS Code), `quietStartup` (`false`), `defaultProjectTrust` (`"ask"`, global only), `collapseChangelog` (`false`), `enableInstallTelemetry` (`true`), `enableAnalytics` (`false`, only asked during experimental first-time setup with `PI_EXPERIMENTAL=1`), `trackingId`, `doubleEscapeAction` (`"tree"` | `"fork"` | `"none"`), `treeFilterMode` (`"default"` | `"no-tools"` | `"user-only"` | `"labeled-only"` | `"all"`), `editorPaddingX` (`0`, range 0โ€“3), `outputPad` (`1`, 0 or 1), `autocompleteMaxVisible` (`5`, range 3โ€“20), `showHardwareCursor` (`false`). -Branch summary: `branchSummary.reserveTokens`, `branchSummary.skipPrompt`. +Fullscreen TUI: `tuiMode` (`"regular"` default, or experimental `"fullscreen"`; `/settings` changes apply immediately and `--tui-mode` overrides at startup), `fullscreenExitOutput` (`"transcript"` prints the final transcript plus resume hint, `"resume-hint"` restores the previous screen and prints only the hint), `fullscreenScrollbar` (`"auto"` shows it while scrolling, `"always"` reserves the rightmost column, `"hidden"`). The last two have no effect in regular mode. -Retry: `retry.enabled`, `retry.maxRetries`, `retry.baseDelayMs`, `retry.provider.timeoutMs`, `retry.provider.maxRetries`, `retry.provider.maxRetryDelayMs`. +## Network, Warnings, Markdown -Message delivery: `steeringMode`, `followUpMode`, `transport`, `httpIdleTimeoutMs`, `websocketConnectTimeoutMs`. +`httpProxy` (applied as `HTTP_PROXY`/`HTTPS_PROXY`, global only). `warnings.anthropicExtraUsage` (`true`) warns when Anthropic subscription auth may use paid extra usage. `markdown.codeBlockIndent` (`" "`), `markdown.mermaid` (`"streaming"`, or `"final"` / `"off"`). -Terminal/images: `terminal.showImages`, `terminal.imageWidthCells`, `terminal.clearOnShrink`, `images.autoResize`, `images.blockImages`. +## Compaction and Branch Summary -Shell: `shellPath`, `shellCommandPrefix`, `npmCommand`. +`compaction.enabled` (`true`), `compaction.reserveTokens` (`16384`), `compaction.keepRecentTokens` (`20000`). `branchSummary.reserveTokens` (`16384`), `branchSummary.skipPrompt` (`false`, defaults to no summary). -Sessions: `sessionDir`; precedence is `--session-dir`, `PI_CODING_AGENT_SESSION_DIR`, then setting. +## Retry -Resources: `packages`, `extensions`, `skills`, `prompts`, `themes`, `enableSkillCommands`. +`retry.enabled` (`true`), `retry.maxRetries` (`3`), `retry.baseDelayMs` (`2000`, so 2s/4s/8s), `retry.provider.timeoutMs` (SDK default), `retry.provider.maxRetries` (`0`), `retry.provider.maxRetryDelayMs` (`60000`; `0` disables the limit). -## Network/Telemetry +Keep `retry.provider.maxRetries` at `0` unless provider-level retries are explicitly needed โ€” above `0`, SDK retries can swallow out-of-usage-limit errors before Pi sees them and block the agent until quota resets. When a provider requests a delay longer than `maxRetryDelayMs`, the request fails immediately with an informative error instead of waiting silently. -`enableInstallTelemetry` only controls anonymous install/update ping. Use `PI_SKIP_VERSION_CHECK=1` to disable version checks. Use `--offline` or `PI_OFFLINE=1` to disable startup network operations including update checks, package update checks, and install/update telemetry. +## Message Delivery + +`steeringMode` / `followUpMode` (`"one-at-a-time"` or `"all"`), `transport` (`"auto"`, `"sse"`, `"websocket"`, `"websocket-cached"`), `httpIdleTimeoutMs` (`300000`, `0` disables), `websocketConnectTimeoutMs` (`15000`, `0` disables). + +## Terminal and Images + +`terminal.showImages` (`true`), `terminal.imageWidthCells` (`60`), `terminal.clearOnShrink` (`false`), `images.autoResize` (`true`, 2000ร—2000 max; applies to `@file` attachments, `read`, and images returned by tools), `images.blockImages` (`false`). + +## Tools + +`defaultTools` is a string array of built-in tools enabled at startup; omitting it uses Pi's standard defaults. Extension and SDK custom tools stay enabled, and an empty array starts with no built-ins while preserving them. `--tools` replaces this with a strict allowlist for all tools, `--no-tools` disables everything, `--no-builtin-tools` disables the built-in defaults, and `--exclude-tools` filters the result. A project `defaultTools` array replaces the global array. + +## Shell + +`shellPath` (supports leading `~`; Windows paths in JSON need forward slashes or escaped backslashes, e.g. `"C:/Program Files/Git/bin/bash.exe"`), `shellCommandPrefix` (prefix for every bash command), `npmCommand` (argv array, e.g. `["mise", "exec", "node@20", "--", "npm"]`). `npmCommand` covers all npm package-manager operations; user npm packages install under `~/.pi/agent/npm/`, project ones under `.pi/npm/`. When `npmCommand` is configured, git package dependency installs use plain `install` for wrapper compatibility. + +## Sessions and Model Cycling + +`sessionDir` (absolute, relative, or `~`); precedence is `--session-dir`, then `PI_CODING_AGENT_SESSION_DIR`, then this setting. `enabledModels` is an array of patterns for Ctrl+P cycling, same format as `--models`. + +## Resources + +`packages` (`[]`), `extensions` (`[]`), `skills` (`[]`), `prompts` (`[]`), `themes` (`[]`), `enableSkillCommands` (`true`, registers `/skill:name`). + +Arrays support glob patterns, `!pattern` to exclude, `+path` to force-include an exact path, and `-path` to force-exclude. `packages` accepts strings or objects that filter which resource types load: + +```json +{ + "packages": [ + "pi-skills", + { "source": "npm:my-package", "skills": ["brave-search"], "extensions": [] } + ] +} +``` + +## Telemetry and Update Checks + +`enableInstallTelemetry` only controls the anonymous install/update ping to `https://pi.dev/api/report-install`. Opting out does not disable update checks, which fetch `https://pi.dev/api/latest-version`. `PI_SKIP_VERSION_CHECK=1` disables the version check; `--offline` or `PI_OFFLINE=1` disables all startup network operations including package update checks and telemetry. ## Example ```json { "defaultProvider": "anthropic", - "defaultModel": "claude-sonnet-4-5", + "defaultModel": "claude-sonnet-4-20250514", "defaultThinkingLevel": "medium", "theme": "dark", - "compaction": { - "enabled": true, - "reserveTokens": 16384, - "keepRecentTokens": 20000 - }, + "compaction": { "enabled": true, "reserveTokens": 16384, "keepRecentTokens": 20000 }, "retry": { "enabled": true, "maxRetries": 3 }, "enabledModels": ["claude-*", "gpt-4o"], + "warnings": { "anthropicExtraUsage": true }, "packages": ["pi-skills"] } ``` diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/terminal-setup.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/terminal-setup.md index 7c547364..5fd811a8 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/terminal-setup.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/terminal-setup.md @@ -2,9 +2,9 @@ title: "Terminal Setup" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/pi-agent/references/terminal-setup.md -upstream_sha: 9c9bd2e9 -imported_at: 2026-06-27 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/references/terminal-setup.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: unknown upstream_changes: accepted author: upstream @@ -15,26 +15,31 @@ validated: false Source: https://pi.dev/docs/latest/terminal-setup -Pi uses the Kitty keyboard protocol for reliable modifier detection. Most modern terminals support it; some need setup. +Pi uses the [Kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol/) for reliable modifier detection. Most modern terminals support it; some need configuration. -## Works Out of Box +## Works Out of the Box -Kitty and iTerm2 work out of the box. Apple Terminal uses enhanced key reporting when available and a local macOS fallback for Shift+Enter when running on the same Mac. +Kitty and iTerm2 (regular TUI mode). Apple Terminal enables enhanced key reporting when available; if it still sends plain Return for `Shift+Enter`, Pi uses a local macOS modifier fallback โ€” which only works when Pi runs on the same Mac, not over SSH. VS Code 1.109.5+ also works by default. + +### iTerm2 in fullscreen TUI mode + +Pi owns the viewport, so iTerm2 sends mouse-wheel reports instead of scrolling its native scrollback. With iTerm2's default fast-trackpad behavior those reports can lose most of an accelerated wheel delta. If fast gestures move only ~one line at a time, open **iTerm2 โ†’ Settings โ†’ Advanced**, find **Trackpad scrolls fast?** and set it to **No**. This is an iTerm2-wide workaround (tracked in iTerm2 issue 9619). Inline images also render as text placeholders in fullscreen mode because iTerm2's inline-image protocol cannot delete or crop placements during application-owned scrolling. ## Ghostty -Add: +Config at `~/Library/Application Support/com.mitchellh.ghostty/config` (macOS) or `~/.config/ghostty/config` (Linux): ```text -keybind = alt+backspace=text: +keybind = alt+backspace=text:\x1b\x7f ``` -Remove older `shift+enter=text: -` mappings unless needed for other tools. If keeping that mapping for tmux, add `ctrl+j` to Pi's newline keybinding. +Older Claude Code versions may have added `keybind = shift+enter=text:\n`. That sends a raw linefeed, which inside Pi is indistinguishable from `Ctrl+J`, so tmux and Pi no longer see a real `shift+enter` event. Remove it unless you still need it for Claude Code in tmux. Pi binds `Ctrl+J` as a default newline alias, so `Shift+Enter` keeps working through that remap without extra Pi configuration. + +In fullscreen TUI mode links stay clickable, but Ghostty hides its hover underline and lower-left URL preview while Pi captures mouse input. Hold `Shift+Command` (macOS) or `Shift+Ctrl` (Linux) for Ghostty's native link handling. ## WezTerm -Usually works. To force Kitty keyboard: +Usually works via xterm `modifyOtherKeys`. To force the Kitty protocol, in `~/.wezterm.lua`: ```lua local wezterm = require 'wezterm' @@ -43,20 +48,53 @@ config.enable_kitty_keyboard = true return config ``` -On macOS, remap Option+Enter to send `` if you want follow-up queueing. +On macOS `Option+Enter` is bound to fullscreen; to use it for follow-up queueing add to `config.keys`: + +```lua +{ key = 'Enter', mods = 'ALT', action = wezterm.action.SendString('\x1b[13;3u') } +``` + +On WSL, WezTerm may need a visible hardware cursor for IME candidate positioning โ€” set `PI_HARDWARE_CURSOR=1` or `showHardwareCursor: true`. ## Alacritty -On macOS, add Alt+Enter binding to send ``. +Usually works for `Shift+Enter`. On macOS `Option+Enter` may arrive as plain `Enter`; add to `~/.config/alacritty/alacritty.toml` and restart: + +```toml +[[keyboard.bindings]] +key = "Enter" +mods = "Alt" +chars = "\u001b[13;3u" +``` ## VS Code Integrated Terminal -VS Code 1.109.5+ enables Kitty keyboard by default. Older versions need a Shift+Enter `workbench.action.terminal.sendSequence` keybinding sending ``. +1.109.5+ enables the Kitty protocol by default. Older versions need an explicit `keybindings.json` entry (macOS `~/Library/Application Support/Code/User/keybindings.json`, Linux `~/.config/Code/User/keybindings.json`, Windows `%APPDATA%\Code\User\keybindings.json`): + +```json +{ + "key": "shift+enter", + "command": "workbench.action.terminal.sendSequence", + "args": { "text": "\u001b[13;2u" }, + "when": "terminalFocus" +} +``` ## Windows Terminal -Add actions for Shift+Enter (``) and Alt+Enter (``). Fully restart if old fullscreen behavior persists. +Add to `settings.json` (Ctrl+Shift+, then Open JSON file) so the modified Enter keys reach Pi: + +```json +{ + "actions": [ + { "command": { "action": "sendInput", "input": "\u001b[13;2u" }, "keys": "shift+enter" }, + { "command": { "action": "sendInput", "input": "\u001b[13;3u" }, "keys": "alt+enter" } + ] +} +``` + +Windows Terminal binds `Alt+Enter` to fullscreen by default, which blocks follow-up queueing; remapping it to `sendInput` forwards the real chord. Fully close and reopen Windows Terminal if the old fullscreen behavior persists. ## Limited Terminals -xfce4-terminal, terminator, and IntelliJ's integrated terminal cannot distinguish modified Enter keys reliably. Use a terminal with Kitty keyboard support for the best experience. +xfce4-terminal, terminator, and IntelliJ IDEA's integrated terminal cannot distinguish modified Enter keys from plain Enter, so bindings such as `submit: ["ctrl+enter"]` will not work. Prefer a terminal with Kitty keyboard support: Kitty, Ghostty, WezTerm, iTerm2, or Alacritty compiled with Kitty protocol support. In IntelliJ, set `PI_HARDWARE_CURSOR=1` if you want the hardware cursor visible (off by default for compatibility). diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/themes.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/themes.md index 0abb34b4..b87ffcb3 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/themes.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/themes.md @@ -2,9 +2,9 @@ title: "Themes" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/pi-agent/references/themes.md -upstream_sha: 9c9bd2e9 -imported_at: 2026-06-27 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/references/themes.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: unknown upstream_changes: accepted author: upstream @@ -21,27 +21,62 @@ Themes are JSON files defining TUI colors. - Built-in: `dark`, `light` - Global: `~/.pi/agent/themes/*.json` -- Project: `.pi/themes/*.json` after project trust -- Packages: `themes/` or `pi.themes` +- Project: `.pi/themes/*.json` (only after project trust) +- Packages: `themes/` directories or `pi.themes` entries in `package.json` - Settings: `themes` array -- CLI: `--theme`, repeatable +- CLI: `--theme ` (repeatable) loads a theme file; `--use-theme ` selects the initial theme for one run -Disable with `--no-themes`. Select through `/settings` or `{"theme": "my-theme"}`. Pi detects terminal background on first run. +Disable discovery with `--no-themes`. Select via `/settings` or `{"theme": "my-theme"}`. On first run Pi detects the terminal background and defaults to `dark` or `light`. Editing the active custom theme file hot-reloads it for immediate feedback. + +`--use-theme light` starts a run with that theme without changing the saved setting; `--use-theme light/dark` uses `lightTheme/darkTheme` syntax to follow terminal appearance. Picking another theme later in `/settings` applies immediately and saves normally. ## Format -A theme has `$schema`, required `name`, optional `vars`, required `colors`, and optional `export` colors for HTML exports. All 51 color tokens must be defined. +```json +{ + "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json", + "name": "my-theme", + "vars": { "primary": "#00aaff", "secondary": 242 }, + "colors": { "accent": "primary", "muted": "secondary", "text": "" } +} +``` -Color values can be 6-digit hex strings, xterm 256-color indices, variable names from `vars`, or `""` for terminal default. +- `name` is required, must be unique, and must not contain `/`. +- `vars` is optional โ€” reusable colors referenced by name from `colors`. +- `colors` must define all 51 required tokens. Optional tokens fall back: `thinkingMax` โ†’ `thinkingXhigh`, `scrollbarThumb` and `searchMatchBg` โ†’ `selectedBg`, `searchMatchText` โ†’ `text`. +- `$schema` enables editor auto-completion and validation. -## Token Groups +## Color Tokens (51 required + 4 optional) -- Core UI: accent, borders, success/error/warning, muted/dim/text/thinkingText. -- Background/content: selected/user/custom/tool states. -- Markdown: headings, links, code, quote, hr, list bullets. -- Tool diffs: added, removed, context. -- Syntax: comment, keyword, function, variable, string, number, type, operator, punctuation. -- Thinking levels: off, minimal, low, medium, high, xhigh. -- Bash mode. +- **Core UI (11)**: `accent`, `border`, `borderAccent`, `borderMuted`, `success`, `error`, `warning`, `muted`, `dim`, `text`, `thinkingText` +- **Backgrounds & content (11 required + 3 optional)**: `selectedBg`, `userMessageBg`, `userMessageText`, `customMessageBg`, `customMessageText`, `customMessageLabel`, `toolPendingBg`, `toolSuccessBg`, `toolErrorBg`, `toolTitle`, `toolOutput`, plus optional `scrollbarThumb` (fullscreen scrollbar thumb), `searchMatchBg`, and `searchMatchText` (transcript search). Non-current search matches render `searchMatchText` on `searchMatchBg` with an underline; the current match reverses that pair and uses bold. +- **Markdown (10)**: `mdHeading`, `mdLink`, `mdLinkUrl`, `mdCode`, `mdCodeBlock`, `mdCodeBlockBorder`, `mdQuote`, `mdQuoteBorder`, `mdHr`, `mdListBullet` +- **Tool diffs (3)**: `toolDiffAdded`, `toolDiffRemoved`, `toolDiffContext` +- **Syntax (9)**: `syntaxComment`, `syntaxKeyword`, `syntaxFunction`, `syntaxVariable`, `syntaxString`, `syntaxNumber`, `syntaxType`, `syntaxOperator`, `syntaxPunctuation` +- **Thinking level borders (6 required + 1 optional)**: `thinkingOff`, `thinkingMinimal`, `thinkingLow`, `thinkingMedium`, `thinkingHigh`, `thinkingXhigh`, and optional `thinkingMax` +- **Bash mode (1)**: `bashMode` -Hot reload applies edits to the active custom theme for immediate feedback. +## HTML Export (optional) + +```json +{ "export": { "pageBg": "#18181e", "cardBg": "#1e1e24", "infoBg": "#3c3728" } } +``` + +Controls `/export` HTML output. If omitted, colors are derived from `userMessageBg`. + +## Color Values + +| Format | Example | Notes | +|---|---|---| +| Hex | `"#ff0000"` | 6-digit RGB | +| 256-color | `39` | xterm palette index 0โ€“255 | +| Variable | `"primary"` | Reference to a `vars` entry | +| Default | `""` | Terminal default color | + +256-color ranges: `0-15` basic ANSI (terminal-dependent), `16-231` the 6ร—6ร—6 RGB cube (`16 + 36R + 6G + B`), `232-255` grayscale. + +Pi emits 24-bit RGB; older 256-color terminals get the nearest approximation. Check truecolor with `echo $COLORTERM`. In VS Code set `terminal.integrated.minimumContrastRatio` to `1` for accurate colors. + +## Tips + +Dark terminals want bright saturated colors with higher contrast; light terminals want darker muted colors. Start from a base palette (Nord, Gruvbox, Tokyo Night) in `vars` and reference it consistently. Test against different message types, tool states, markdown, and long wrapped text. Built-in themes to copy: `src/modes/interactive/theme/dark.json` and `light.json`. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/tui.md b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/tui.md index 85dc844e..a2b396e9 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/tui.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/catalogue/skills/pi-agent/references/tui.md @@ -2,9 +2,9 @@ title: "TUI Components" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/pi-agent/references/tui.md -upstream_sha: 9c9bd2e9 -imported_at: 2026-06-27 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/references/tui.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: unknown upstream_changes: accepted author: upstream @@ -15,7 +15,7 @@ validated: false Source: https://pi.dev/docs/latest/tui -Extensions and custom tools can render custom terminal UI components through `@earendil-works/pi-tui`. +Extensions and custom tools render custom terminal UI through `@earendil-works/pi-tui`. ## Component Interface @@ -23,47 +23,88 @@ Extensions and custom tools can render custom terminal UI components through `@e interface Component { render(width: number): string[]; handleInput?(data: string): void; - wantsKeyRelease?: boolean; - invalidate(): void; + wantsKeyRelease?: boolean; // receive key release events (Kitty protocol) + invalidate(): void; // clear cached render state; called on theme changes } ``` -`render(width)` returns one string per line; each line must not exceed `width`. The TUI appends style resets at line ends, so reapply styles per line or use helpers that preserve ANSI styles. +Each line from `render(width)` **must not exceed `width`**. The TUI appends a full SGR reset and OSC 8 reset at the end of every rendered line, so styles do not carry across lines โ€” reapply styles per line or use `wrapTextWithAnsi()`. + +Width helpers: `visibleWidth(str)` (ignores ANSI), `truncateToWidth(str, width, ellipsis?)`, `wrapTextWithAnsi(str, width)`. ## Focusable and IME -Text cursor components that need IME support implement `Focusable` and emit `CURSOR_MARKER` before their fake cursor. Containers with embedded inputs must propagate focus to the child input, or IME candidate windows appear in the wrong place. Hardware cursor visibility is controlled by `showHardwareCursor`, `setShowHardwareCursor(true)`, or `PI_HARDWARE_CURSOR=1`. +Components that draw a text cursor should implement `Focusable { focused: boolean }`. When focused, the TUI sets `focused = true`, scans output for `CURSOR_MARKER` (a zero-width APC sequence emitted right before the fake cursor), and positions the hardware cursor there. The cursor stays hidden by default; some terminals need it visible for IME candidate windows โ€” enable with `showHardwareCursor`, `setShowHardwareCursor(true)`, or `PI_HARDWARE_CURSOR=1`. The built-in `Editor` and `Input` already implement this. + +Container components (dialogs, selectors) that embed an `Input`/`Editor` must implement `Focusable` and propagate `focused` to the child, or IME candidate windows appear in the wrong place. ## Usage -In extensions: - ```ts -pi.on("session_start", async (_event, ctx) => { - const handle = ctx.ui.custom(myComponent); - handle.requestRender(); - handle.close(); -}); +const result = await ctx.ui.custom((tui, theme, keybindings, done) => + new MyComponent({ theme, keybindings, onChange: () => tui.requestRender(), onSelect: done, onCancel: () => done(null) }) +); ``` -In custom tools, use `pi.ui.custom()` inside `execute` and close handles when done. +The same call works inside a custom tool's `execute(toolCallId, params, signal, onUpdate, ctx)`. Returning a plain `{ render, invalidate, handleInput }` object is also fine. ## Overlays -Pass `{ overlay: true }` to render on top of existing content. `overlayOptions` controls width, height, anchor, offsets, row/col, margins, and responsive visibility. Overlay handles support focus/unfocus, hide, and visibility toggles. Do not reuse disposed overlay component instances; create fresh ones for each show. +Pass `{ overlay: true }` to render on top of existing content without clearing the screen. `overlayOptions` controls layout: -## Built-ins +- Size: `width`, `minWidth`, `maxHeight` (numbers or percentage strings) +- Position: `anchor` (9 positions, default `"center"`), `offsetX`/`offsetY`, or `row`/`col` +- Margins: `margin` (number or `{ top, right, bottom, left }`) +- Responsive: `visible(termWidth, termHeight)` -Import `Text`, `Box`, `Container`, `Spacer`, `Markdown`, `Input`, `Editor`, `SelectList`, `SettingsList`, `BorderedLoader`, and helpers from `@earendil-works/pi-tui`. Prefer existing components for selectors, settings/toggles, loaders, markdown, and layout before building custom primitives. +`onHandle: (handle) => โ€ฆ` gives `focus()` (focus and bring to front), `unfocus()` (release input to the normal fallback), `unfocus({ target })` (hand input to a specific component, or `null` for none), `setHidden(true|false)`, and `hide()` (permanent removal). -## Theming Rules +A focused visible overlay keeps input ownership across temporary non-overlay UI and can reclaim input when that UI closes. Overlay components are disposed when closed โ€” never reuse a reference; call the show function again to re-show. -Use the theme object passed into callbacks; do not import a global theme. Implement `invalidate()` so theme changes clear cached styled output. Use explicit color callback parameters such as `(s: string) => theme.fg("accent", s)`. +## Built-in Components + +Import from `@earendil-works/pi-tui`: `Text` (multi-line with word wrapping, `new Text(content, paddingX, paddingY, bgFn?)`, `setText()`), `Box` (padding + background, `addChild`, `setBgFn`), `Container` (vertical grouping, `addChild`/`removeChild`/`clear`), `Spacer(lines)`, `Markdown(text, paddingX, paddingY, theme)`, `Image(base64, mimeType, theme, { maxWidthCells, maxHeightCells })` (Kitty, iTerm2, Ghostty, WezTerm, Warp), `SelectList(items, visibleCount, theme)` with `SelectItem { value, label, description? }`, `SettingsList(items, visibleCount, theme, onChange, onClose, { enableSearch })` with `SettingItem { id, label, currentValue, values }`, plus `AutocompleteItem` types. + +From `@earendil-works/pi-coding-agent`: `DynamicBorder`, `BorderedLoader` (spinner + abort signal + `onAbort`), `CustomEditor`, `getMarkdownTheme()`, `getSettingsListTheme()`, `keyHint`/`keyText`/`rawKeyHint`, `highlightCode`, `getLanguageFromPath`. + +## Keyboard Input + +```ts +import { matchesKey, Key } from "@earendil-works/pi-tui"; + +if (matchesKey(data, Key.up)) { /* ... */ } +if (matchesKey(data, Key.ctrl("c"))) { /* ... */ } +``` + +`Key.enter`, `Key.escape`, `Key.tab`, `Key.space`, `Key.backspace`, `Key.delete`, `Key.home`, `Key.end`, arrows, and modifier helpers `Key.ctrl`, `Key.shift`, `Key.alt`, `Key.ctrlShift`. String literals (`"enter"`, `"ctrl+c"`, `"shift+tab"`) also work. + +## Theming + +Use the `theme` passed into the callback or renderer โ€” never import a global theme. `theme.fg(color, text)` covers general (`text`, `accent`, `muted`, `dim`, `searchMatchText`), status (`success`, `error`, `warning`), borders (`border`, `borderAccent`, `borderMuted`), messages (`userMessageText`, `customMessageText`, `customMessageLabel`), tools (`toolTitle`, `toolOutput`), diffs (`toolDiffAdded`/`Removed`/`Context`), markdown (`md*`), syntax (`syntax*`), thinking levels (`thinkingOff`โ€ฆ`thinkingMax`), and `bashMode`. `theme.bg(color, text)` covers `selectedBg`, `searchMatchBg`, `userMessageBg`, `customMessageBg`, `toolPendingBg`, `toolSuccessBg`, `toolErrorBg`. Text styles: `theme.bold`, `theme.italic`, `theme.strikethrough`. + +Components that pre-bake theme colors into cached strings must rebuild that content in `invalidate()` โ€” clearing a render cache is not enough. This applies to `theme.fg`/`theme.bg` strings stored in child components, `highlightCode()` output, and child trees that embed colors. It is unnecessary when you pass theme callbacks that run at render time, for simple containers, or for stateless render. + +## Common Patterns + +1. **Selection dialog** โ€” `SelectList` framed with `DynamicBorder`, `onSelect`/`onCancel` calling `done`. +2. **Async with cancel** โ€” `BorderedLoader(tui, theme, "Fetchingโ€ฆ")`, pass `loader.signal` to the async work, `loader.onAbort = () => done(null)`. +3. **Settings toggles** โ€” `SettingsList` with `getSettingsListTheme()`. +4. **Persistent status** โ€” `ctx.ui.setStatus("my-ext", text)`; clear with `undefined`. +5. **Working indicator** โ€” `ctx.ui.setWorkingIndicator({ frames, intervalMs })`; `{ frames: [] }` hides it, no argument restores the default spinner. Frames render verbatim, so add colors yourself. Compaction and retry loaders keep built-in styling. `setWorkingMessage()` and `setWorkingVisible()` control the loader row. +6. **Widgets** โ€” `ctx.ui.setWidget(key, lines | factory, { placement: "aboveEditor" | "belowEditor" })`; `undefined` clears. +7. **Custom footer** โ€” `ctx.ui.setFooter((tui, theme, footerData) => ({ render, invalidate, dispose }))`. `footerData.getGitBranch()`, `getExtensionStatuses()`, and `onBranchChange(cb)` expose data extensions cannot otherwise reach; token stats come from `ctx.sessionManager.getBranch()` and `ctx.model`. `setFooter(undefined)` restores the default. +8. **Custom editor** โ€” extend `CustomEditor` (not base `Editor`) so app keybindings still work, call `super.handleInput(data)` for keys you do not handle, and install with `ctx.ui.setEditorComponent((tui, theme, keybindings) => new MyEditor(...))`. Capture `ctx.ui.getEditorComponent()` first to wrap another extension's editor; `setEditorComponent(undefined)` restores the default. ## Key Rules -- Always respect `render(width)`. -- Implement `invalidate()` on custom components and child trees. -- Propagate focus for embedded inputs. -- Use overlays for temporary panels/dialogs. -- Guard TUI features when running in non-TUI modes. +1. Respect `render(width)` on every line. +2. Always use the `theme` from the callback; type `DynamicBorder` color params explicitly (`(s: string) => theme.fg("accent", s)`). +3. Call `tui.requestRender()` after state changes in `handleInput`. +4. Implement `invalidate()` on custom components and their child trees. +5. Propagate focus to embedded inputs. +6. Reuse `SelectList`, `SettingsList`, and `BorderedLoader` before building primitives. +7. Guard TUI features in non-TUI modes (`ctx.mode === "tui"`). + +## Debug Logging + +`PI_TUI_WRITE_LOG=/tmp/tui-ansi.log` captures the raw ANSI stream written to stdout. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/custom-provider.md b/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/custom-provider.md index 80afc2d6..3777da01 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/custom-provider.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/custom-provider.md @@ -2,9 +2,9 @@ title: "Custom Providers" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/pi-agent/references/custom-provider.md -upstream_sha: 9c9bd2e9 -imported_at: 2026-06-27 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/references/custom-provider.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: prompt upstream_changes: accepted author: upstream @@ -15,66 +15,130 @@ validated: false Source: https://pi.dev/docs/latest/custom-provider -Extensions can register providers with `pi.registerProvider()` for proxies, private deployments, OAuth/SSO, and non-standard streaming APIs. +Extensions register providers with `pi.registerProvider()` for proxies, private deployments, OAuth/SSO, and non-standard streaming APIs. Two forms exist: a complete pi-ai `Provider` (preferred when you need custom authentication, filtering, refresh, or streaming) and the legacy provider-config object. `models.json` overrides compose **above** registered native providers. -## Quick Reference +## Complete Provider Form ```ts +import { createProvider, openAICompletionsApi } from "@earendil-works/pi-ai"; import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; export default function (pi: ExtensionAPI) { - pi.registerProvider("anthropic", { baseUrl: "https://proxy.example.com" }); - - pi.registerProvider("my-provider", { - name: "My Provider", - baseUrl: "https://api.example.com", - apiKey: "$MY_API_KEY", - api: "openai-completions", - models: [{ - id: "my-model", - name: "My Model", - reasoning: false, - input: ["text", "image"], - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, - contextWindow: 128000, - maxTokens: 4096 - }] - }); + pi.registerProvider(createProvider({ + id: "native-local", + name: "Native Local", + baseUrl: "http://localhost:8080/v1", + auth: { + apiKey: { + name: "Local server API key", + async login(interaction) { + return { type: "api_key", key: await interaction.prompt({ type: "secret", message: "API key" }) }; + }, + async resolve({ credential }) { + return credential?.key ? { auth: { apiKey: credential.key }, source: "stored API key" } : undefined; + }, + }, + }, + models: [], + api: openAICompletionsApi(), + })); } ``` -Use an async extension factory for dynamic model discovery so models are available during startup and `pi --list-models`. +The object form accepts a complete `Provider`, including native `auth`, `getModels`, `refreshModels`, `filterModels`, `stream`, and `streamSimple`. -## Overrides and New Providers +## Legacy Config Form -When only `baseUrl` and/or `headers` are provided, existing models for a built-in provider are preserved. When `models` is provided, it replaces dynamic models for that provider. +```ts +// Override baseUrl and/or headers only โ€” existing models are preserved +pi.registerProvider("anthropic", { baseUrl: "https://proxy.example.com" }); +pi.registerProvider("openai", { headers: { "X-Custom-Header": "value" } }); -`pi.unregisterProvider(name)` removes dynamic models, API key fallback, OAuth registration, and custom stream handlers, restoring built-in behavior where relevant. +// New provider with models โ€” replaces all existing models for that provider +pi.registerProvider("my-provider", { + name: "My Provider", + baseUrl: "https://api.example.com", + apiKey: "$MY_API_KEY", + api: "openai-completions", + models: [{ + id: "my-model", + name: "My Model", + reasoning: false, + input: ["text", "image"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 128000, + maxTokens: 4096, + }], +}); +``` + +Use an **async extension factory** for dynamic model discovery so models are registered before startup finishes and are visible to `pi --list-models`. Dynamic providers can also implement `refreshModels({ signal, store, ... })`; Pi calls it during model refresh and publishes the result synchronously. Persist through `context.store` only when the catalog should survive โ€” live servers such as llama.cpp can ignore it. + +`pi.unregisterProvider(name)` removes that provider's dynamic models, API key fallback, OAuth registration, and custom stream handlers, restoring overridden built-in behavior. Calls made after the initial load phase take effect immediately โ€” no `/reload`. ## API Types -Common values: `anthropic-messages`, `openai-completions`, `openai-responses`, `azure-openai-responses`, `openai-codex-responses`, `mistral-conversations`, `google-generative-ai`, `google-vertex`, `bedrock-converse-stream`. +`anthropic-messages`, `openai-completions`, `openai-responses`, `azure-openai-responses`, `openai-codex-responses`, `mistral-conversations`, `google-generative-ai`, `google-vertex`, `bedrock-converse-stream`. -Most OpenAI-compatible providers work with `openai-completions`; use `compat` for quirks and `thinkingLevelMap` for model-specific thinking levels. +Most OpenAI-compatible providers work with `openai-completions`; use model-level `thinkingLevelMap` for thinking levels and `compat` for quirks (full flag list in `references/models.md`). `xhigh` and `max` are opt-in and require non-null map entries. Mistral moved from `openai-completions` to `mistral-conversations` (native Mistral Chat Completions streaming) โ€” use the latter for native Mistral models. ## Auth Header and Secrets -Set `authHeader: true` to add `Authorization: Bearer `. `apiKey` and custom header values use `!command`, `$ENV`, `${ENV}`, `$$`, and `$!` resolution like `models.json`. +`authHeader: true` adds `Authorization: Bearer `; the key is resolved per request and an explicit request `Authorization` header wins. `apiKey` and custom header values use the same syntax as `models.json`: leading `!command` executes for the whole value, `$ENV`/`${ENV}` interpolate, `$$` and `$!` escape literals. ## OAuth -Register `oauth` with `login(callbacks)`, `refreshToken(credentials)`, `getApiKey(credentials)`, and optional `modifyModels(models, credentials)`. Credentials persist in `~/.pi/agent/auth.json` with `refresh`, `access`, and `expires` fields. Users authenticate with `/login provider-name`. +```ts +oauth: { + name: "Corporate AI (SSO)", + async login(callbacks: OAuthLoginCallbacks): Promise, + async refreshToken(credentials: OAuthCredentials, signal: AbortSignal): Promise, + getApiKey(credentials: OAuthCredentials): string, +} +``` -Callbacks include `onAuth`, `onDeviceCode`, `onPrompt`, and `onSelect`. +`OAuthLoginCallbacks`: `onAuth({ url })` (open in browser), `onDeviceCode({ userCode, verificationUri, intervalSeconds?, expiresInSeconds? })`, `onProgress?(message)`, `onPrompt({ message }): Promise`, `onSelect({ message, options: { id, label }[] }): Promise`. + +`OAuthCredentials` is `{ refresh, access, expires }` (expiry in ms), persisted in `~/.pi/agent/auth.json`. Users authenticate with `/login `. `refreshToken` receives an `AbortSignal` โ€” pass it to blocking I/O and call `signal.throwIfAborted()` early. ## Custom Streaming -For non-standard APIs, implement `streamSimple(model, context, options)`. Create an `AssistantMessage`, push `{ type: "start", partial }`, push content events as data arrives, then push `{ type: "done", reason, message }` or `{ type: "error", reason, error }`, and end the stream. +Implement `streamSimple(model, context, options?)` returning an `AssistantMessageEventStream` from `createAssistantMessageEventStream()`. Initialize an `AssistantMessage` (`role`, `content: []`, `api`, `provider`, `model`, zeroed `usage`, `stopReason: "pending"`, `timestamp`), then: -Content events include `text_start`, `text_delta`, `text_end`, `thinking_start`, `thinking_delta`, `thinking_end`, `toolcall_start`, `toolcall_delta`, and `toolcall_end`. Keep `partial` updated with the current assistant message state. +1. `stream.push({ type: "start", partial: output })` +2. Content events, tracking `contentIndex` per block: `text_start`, `text_delta`, `text_end`, `thinking_start`, `thinking_delta`, `thinking_end`, `toolcall_start`, `toolcall_delta`, `toolcall_end` +3. `stream.push({ type: "done", reason, message })` or `{ type: "error", reason, error }`, then `stream.end()` -For tool calls, accumulate JSON deltas, parse into `{ id, name, arguments }`, and end with `toolcall_end`. +`stopReason: "pending"` marks the partial message; set a terminal reason before pushing `done` (throw for `"error"`/`"aborted"`). Every event carries `partial` with the current `AssistantMessage` state โ€” mutate `output.content` as data arrives and pass `output`. Tool calls accumulate JSON deltas, parse into `{ id, name, arguments }`, and finish with `toolcall_end` carrying the full `toolCall`. Update usage from the API response and call `calculateCost(model, output.usage)`. Register with `streamSimple` on the provider config. + +Reference implementations in `packages/ai/src/providers/`: `anthropic.ts`, `mistral.ts`, `openai-completions.ts`, `openai-responses.ts`, `google.ts`, `amazon-bedrock.ts`. + +## Context Overflow Errors + +Pi auto-recovers from context overflow by compacting and retrying once, but only when it recognizes the failure: `stopReason === "error"` and `errorMessage` matching a known pattern (see `packages/ai/src/utils/overflow.ts`). If your provider's message is unrecognized, normalize it from the same extension with a `message_end` handler so `errorMessage` starts with a recognized phrase โ€” `context_length_exceeded` is the safest: + +```ts +pi.on("message_end", (event, ctx) => { + const message = event.message; + if (message.role !== "assistant" || message.stopReason !== "error") return; + if (message.provider !== "my-provider" && ctx.model?.provider !== "my-provider") return; + const errorMessage = message.errorMessage ?? ""; + if (errorMessage.includes("context_length_exceeded")) return; + if (!MY_PROVIDER_OVERFLOW_PATTERN.test(errorMessage)) return; + return { message: { ...message, errorMessage: `context_length_exceeded: ${errorMessage}` } }; +}); +``` + +`message_end` runs before Pi tracks the message for auto-compaction, so the rewrite is what Pi checks. Scope it to your provider, match a provider-specific pattern (never Pi's generic ones โ€” rewriting rate-limit errors would trigger compaction instead of retry-with-backoff), and skip when the phrase is already present so the handler is idempotent. ## Testing -Test provider registration with `pi --list-models`, authenticate through `/login` or env vars, run a simple prompt, then exercise tools, thinking levels, image input, cache behavior, retry behavior, and context overflow errors. +Adapt the suites in `packages/ai/test/` for your provider/model pairs: `stream.test.ts`, `tokens.test.ts`, `abort.test.ts`, `empty.test.ts`, `context-overflow.test.ts`, `image-limits.test.ts`, `unicode-surrogate.test.ts`, `tool-call-without-result.test.ts`, `image-tool-result.test.ts`, `total-tokens.test.ts`, `cross-provider-handoff.test.ts`. Verify registration with `pi --list-models`, then exercise auth, tools, thinking levels, image input, cache behavior, retries, and context overflow. + +## Config Reference + +`ProviderConfig`: `name?`, `baseUrl?`, `apiKey?`, `api?`, `streamSimple?`, `headers?`, `authHeader?`, `models?`, `refreshModels?`, `oauth?`. + +`ProviderModelConfig`: `id`, `name`, `api?`, `baseUrl?` (per-model endpoint override), `reasoning`, `thinkingLevelMap?`, `input`, `cost` (`input`, `output`, `cacheRead`, `cacheWrite` per million tokens), `contextWindow`, `maxTokens`, `headers?`, `compat?`. + +Example extensions: `examples/extensions/custom-provider-anthropic/` and `examples/extensions/custom-provider-gitlab-duo/`. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/environment-variables.md b/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/environment-variables.md new file mode 100644 index 00000000..43be7a64 --- /dev/null +++ b/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/environment-variables.md @@ -0,0 +1,70 @@ +--- +title: "Environment Variables" +task: "" +lineage_type: import +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/references/environment-variables.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 +prompt_class: prompt +upstream_changes: accepted +author: upstream +validated: false +--- + +# Environment Variables + +Source: https://pi.dev/docs/latest/environment-variables + +Pi uses environment variables three ways: variables that configure the Pi process, markers Pi sets so child processes know they run inside Pi, and session metadata injected into commands run by the LLM-callable bash tool. Provider API-key variables live in `references/providers.md`. + +## Process Markers + +The CLI and RPC entry points set two markers: + +- `AI_AGENT=pi` โ€” generic marker letting tooling identify Pi as the launching agent. +- `PI_CODING_AGENT=true` โ€” Pi-specific marker for detecting that a process runs inside Pi. + +Child processes inherit both. Neither is session-specific, and neither is set automatically when Pi is embedded through the SDK. + +## Bash Tool Session Environment + +Commands run by the LLM-callable bash tool receive: + +| Variable | Description | +|---|---| +| `PI_SESSION_ID` | Current session ID | +| `PI_SESSION_FILE` | Absolute path to the session JSONL file; unset for ephemeral sessions | +| `PI_PROVIDER` | Currently selected model provider | +| `PI_MODEL` | Currently selected model ID | +| `PI_REASONING_LEVEL` | Effective reasoning level: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max` | + +Values resolve when each command starts, so switching models affects the next bash command without restarting. `PI_PROVIDER`/`PI_MODEL` identify the selected Pi model, not an upstream model a router picks internally. When asked which model is running, inspect these variables instead of inferring from the system prompt: + +```bash +printf '%s/%s\n' "$PI_PROVIDER" "$PI_MODEL" +``` + +These are injected into the LLM-callable bash tool only โ€” not into user-entered `!` or `!!` commands. + +Custom bash tools built with `createBashTool()` expose the same variables by default, injected **before** `spawnHook` so hooks see them in `ctx.env`. Disable with `exposeSessionEnvironment: false`; Pi then also clears inherited values so nested Pi processes do not leak stale parent-session metadata. + +## Pi Process Configuration + +| Variable | Description | +|---|---| +| `PI_CODING_AGENT_DIR` | Override the config directory; default `~/.pi/agent` | +| `PI_CODING_AGENT_SESSION_DIR` | Override session storage; overridden by `--session-dir` | +| `PI_PACKAGE_DIR` | Override the package directory (useful for Nix/Guix store paths) | +| `PI_OFFLINE` | Disable startup network operations: update checks, package updates, install/update telemetry | +| `PI_SKIP_VERSION_CHECK` | Disable the `pi.dev` latest-version request | +| `PI_TELEMETRY` | Override install/update telemetry and provider attribution headers: `1`/`true`/`yes` or `0`/`false`/`no` | +| `PI_CACHE_RETENTION` | Set to `long` for extended provider prompt caching where supported | +| `PI_SHARE_VIEWER_URL` | Override the base URL used by `/share` | +| `PI_HARDWARE_CURSOR` | Set to `1` to show the hardware cursor (IME positioning) | +| `PI_TUI_ESC_TIMEOUT` | Milliseconds to wait after a lone ESC before treating it as Escape; defaults to `100` over SSH and `10` otherwise. Increase when Alt-key input is misread as Escape | +| `VISUAL`, `EDITOR` | External editor fallback when the `externalEditor` setting is unset | +| `HTTP_PROXY`, `HTTPS_PROXY` | Proxy outbound HTTP requests | + +Names are derived from the rebrandable app name (`package.json` `piConfig.name`), so a fork uses a different prefix โ€” see `references/development.md`. + +Other variables documented elsewhere: `PI_EXPERIMENTAL` (experimental first-time setup, `references/settings.md`), `PI_TUI_WRITE_LOG` (raw ANSI capture, `references/tui.md`), `AWS_BEDROCK_FORCE_CACHE` and other cloud variables (`references/providers.md`), `LLAMA_BASE_URL`/`LLAMA_API_KEY` (`references/llama-cpp.md`). diff --git a/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/extensions.md b/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/extensions.md index 0cd59ef4..537a6b30 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/extensions.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/extensions.md @@ -2,9 +2,9 @@ title: "Extensions" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/pi-agent/references/extensions.md -upstream_sha: 9c9bd2e9 -imported_at: 2026-06-27 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/references/extensions.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: prompt upstream_changes: accepted author: upstream @@ -15,17 +15,15 @@ validated: false Source: https://pi.dev/docs/latest/extensions -Extensions are TypeScript modules that extend Pi. They can register tools, commands, shortcuts, flags, custom providers, UI, event handlers, and persistent session entries. +Extensions are TypeScript modules that extend Pi. They register tools, commands, shortcuts, CLI flags, providers, renderers, UI, event handlers, and persistent session entries. They run with the full permissions of the Pi process โ€” only install extensions you trust. ## Locations -- `~/.pi/agent/extensions/*.ts` -- `~/.pi/agent/extensions/*/index.ts` -- `.pi/extensions/*.ts` -- `.pi/extensions/*/index.ts` -- Paths from settings or packages +- `~/.pi/agent/extensions/*.ts` and `~/.pi/agent/extensions/*/index.ts` (global) +- `.pi/extensions/*.ts` and `.pi/extensions/*/index.ts` (project-local, loaded only after project trust) +- Paths from `settings.json` `extensions` / `packages` -Project-local extensions load only after project trust. Use `pi -e ./my-extension.ts` for quick tests. Auto-discovered extensions can be hot-reloaded with `/reload`. +Use `pi -e ./my-extension.ts` for quick tests only; auto-discovered extensions can be hot-reloaded with `/reload`. Extensions load via [jiti](https://github.com/unjs/jiti), so TypeScript needs no compilation. ## Quick Extension @@ -64,39 +62,137 @@ export default function (pi: ExtensionAPI) { ## Imports -- `@earendil-works/pi-coding-agent`: extension types and APIs. -- `typebox`: schemas for tool parameters. -- `@earendil-works/pi-ai`: AI utilities. -- `@earendil-works/pi-tui`: TUI components. +`@earendil-works/pi-coding-agent` (extension types and APIs), `typebox` (tool parameter schemas), `@earendil-works/pi-ai` (`StringEnum` for Google-compatible enums), `@earendil-works/pi-tui` (TUI components), plus Node built-ins. Add a `package.json` next to the extension and run `npm install` for npm dependencies. Distributed packages must put runtime deps in `dependencies` โ€” package installs use `npm install --omit=dev`. -Runtime dependencies for distributed packages belong in `dependencies`; package installs use production installs by default. +## Factory Semantics -## Event Flow +The default export receives `ExtensionAPI` and may be async; Pi awaits it before `session_start`, `resources_discover`, and flushing queued `pi.registerProvider()` calls. Use async factories for one-time startup work such as dynamic model discovery. Do **not** start background resources (processes, sockets, watchers, timers) in the factory โ€” factories can run in invocations that never start a session. Start them in `session_start` or on demand, and close them in an idempotent `session_shutdown` handler. -Startup: `project_trust`, `session_start`, `resources_discover`. +## Event Lifecycle -Prompt: extension commands, `input`, skill/template expansion, `before_agent_start`, `agent_start`, message events, turn events, provider request/response hooks, tool events, `agent_end`. +Startup: `project_trust` (user/global and CLI `-e` extensions only) โ†’ `session_start { reason: "startup" }` โ†’ `resources_discover { reason: "startup" }`. -Session changes: `session_before_switch`, `session_shutdown`, `session_start`, `resources_discover`. Fork/clone use `session_before_fork`. +Prompt: extension commands checked first (they bypass `input`) โ†’ `input` โ†’ skill/template expansion โ†’ `before_agent_start` โ†’ `agent_start` โ†’ message events โ†’ per turn: `turn_start`, `context`, `before_provider_headers`, `before_provider_request`, `after_provider_response`, then `tool_execution_start`, `tool_call`, `tool_execution_update`, `tool_result`, `tool_execution_end` โ†’ `turn_end` โ†’ `agent_end` โ†’ `agent_settled`. -Compaction/tree: `session_before_compact`, `session_compact`, `session_before_tree`, `session_tree`. - -Model changes: `model_select`, `thinking_level_select`. - -Shutdown: `session_shutdown`. +Session replacement (`/new`, `/resume`): `session_before_switch` (cancellable) โ†’ `session_shutdown` โ†’ `session_start { reason: "new" | "resume", previousSessionFile }` โ†’ `resources_discover`. `/fork` and `/clone` use `session_before_fork` (with `position: "before" | "at"`) then `reason: "fork"`. `/name` emits `session_info_changed`. Compaction: `session_before_compact` โ†’ `session_compact`. `/tree`: `session_before_tree` โ†’ `session_tree`. Model changes: `thinking_level_select` then `model_select`. Exit: `session_shutdown`. ## High-Value Hooks -- `project_trust`: user/global or CLI extensions can decide project trust. -- `resources_discover`: contribute skill, prompt, and theme paths. -- `before_agent_start`: inject custom messages or modify the system prompt. -- `context`: non-destructively modify messages before each LLM call. -- `before_provider_request`: inspect/replace provider payload for debugging or compatibility. -- `after_provider_response`: inspect status/headers before streaming body is consumed. -- `tool_call`: block or mutate tool inputs before execution. -- `tool_result`: modify tool results. -- `message_end`: replace finalized message while preserving role. +- `project_trust` โ€” must return `{ trusted: "yes" | "no" | "undecided", remember?: boolean }`. First yes/no decision wins and suppresses the built-in prompt. `ctx` is a limited trust context (cwd, mode, hasUI, select/confirm/input/notify). +- `resources_discover` โ€” return `{ skillPaths, promptPaths, themePaths }`. +- `before_agent_start` โ€” return `{ message }` to inject a persistent custom message and/or `{ systemPrompt }` to replace it for this turn (chained across handlers). `event.systemPromptOptions` exposes the structured inputs Pi used: `customPrompt`, `selectedTools`, `toolSnippets`, `promptGuidelines`, `appendSystemPrompt`, `cwd`, `contextFiles`, `skills`. +- `context` โ€” `event.messages` is a deep copy; return `{ messages }` to modify what the LLM sees. +- `before_provider_headers` โ€” mutate `event.headers` in place; a string adds/overrides, `null` deletes. Fires once per request; retries reuse the headers. +- `before_provider_request` โ€” inspect or replace `event.payload`; handlers run in load order and `undefined` keeps it unchanged. Payload-level system-instruction rewrites are not reflected by `ctx.getSystemPrompt()`. +- `after_provider_response` โ€” `event.status` and normalized `event.headers` before the stream body is consumed. +- `tool_call` โ€” `event.input` is mutable and mutations affect execution (no re-validation); return `{ block: true, reason?, terminate? }` to block. `terminate` applies only to a blocked call, and the agent stops early only when every finalized result in the batch is terminating. Narrow with `isToolCallEventType("bash", event)`, or `isToolCallEventType<"my_tool", MyToolInput>(...)` for custom tools. +- `tool_result` โ€” middleware-style chain; return partial patches (`content`, `details`, `isError`, `usage`). Use `isBashToolResult(event)` for typed bash details and `ctx.signal` for nested async work. +- `message_end` โ€” return `{ message }` to replace the finalized message; the replacement must keep the same `role`. +- `user_bash` โ€” intercept `!`/`!!`: return `{ operations }` (optionally wrapping `createLocalBashOperations()`) or `{ result }`. +- `input` โ€” sees raw text before skill/template expansion. Return `{ action: "continue" | "transform" | "handled" }`; `event.source` is `"interactive" | "rpc" | "extension"` and `event.streamingBehavior` is `"steer" | "followUp" | undefined`. -## Runtime Notes +In parallel tool mode, sibling tool calls are preflighted sequentially then executed concurrently, so `tool_call` is not guaranteed to see sibling tool results; `tool_result`/`tool_execution_end` may interleave in completion order while final `toolResult` message events stay in assistant source order. -Extension factories may be async; Pi awaits them before startup continues. Use async factories for startup-only work such as dynamic model discovery. In RPC or JSON/print mode, guard TUI-specific UI with `ctx.mode === "tui"` and check `ctx.hasUI` before prompting. +## ExtensionContext + +`ctx.ui` (see Custom UI), `ctx.mode` (`"tui" | "rpc" | "json" | "print"`), `ctx.hasUI` (true in TUI and RPC), `ctx.cwd`, `ctx.signal` (agent abort signal; usually `undefined` outside active turns), `ctx.isProjectTrusted()`, `ctx.sessionManager` (read-only: `getEntries`, `getBranch`, `buildContextEntries`, `getLeafId`, โ€ฆ), `ctx.modelRegistry` (`getProvider(id)`, `getProviderAuth(id)`, `find(...)`), `ctx.model`, `ctx.thinkingLevel`, `ctx.scopedModels`, `ctx.isIdle()`, `ctx.abort()`, `ctx.hasPendingMessages()`, `ctx.shutdown()`, `ctx.getContextUsage()`, `ctx.compact({ customInstructions, onComplete, onError })`, `ctx.getSystemPrompt()`. + +`ctx.scopedModels` is the read-only list of models scoped to the session โ€” the same set `/scoped-models` shows, resolved at session start from `--models` and the `enabledModels` setting (minimatch against `provider/modelId` or a bare `modelId`). It is empty when no scoping is configured, meaning every available model is usable. Entries are `{ model, thinkingLevel? }`, with `thinkingLevel` set only when a pattern pinned it (e.g. `anthropic/*:high`). Use it for a model picker that mirrors the built-in one instead of enumerating `ctx.modelRegistry.getAvailable()`. + +Use the exported `CONFIG_DIR_NAME` instead of hardcoding `.pi` โ€” rebranded distributions use a different name. + +## ExtensionCommandContext + +Command handlers additionally get session-control methods that would deadlock from event handlers: `getSystemPromptOptions()`, `waitForIdle()`, `newSession({ parentSession, setup, withSession })`, `fork(entryId, { position: "before" | "at", withSession })`, `navigateTree(targetId, { summarize, customInstructions, replaceInstructions, label })`, `switchSession(path, { withSession })`, `reload()`. + +`withSession` receives a fresh `ReplacedSessionContext` with async `sendMessage()`/`sendUserMessage()`. It runs only after the old session emitted `session_shutdown` and the new instance already received `session_start`, but still executes in the original closure โ€” so captured old `pi`/`ctx`/`sessionManager` objects are stale and throw. Capture only plain data (strings, ids) across the boundary. Treat `await ctx.reload()` as terminal for that handler (`await ctx.reload(); return;`); tools cannot call it, so expose a command and have the tool queue it with `pi.sendUserMessage("/my-reload", { deliverAs: "followUp" })`. + +## ExtensionAPI + +Tools: `registerTool(definition)` (works during load and at runtime โ€” new tools are callable without `/reload`), `getActiveTools()`, `getAllTools()` (returns `name`, `description`, `parameters`, `promptGuidelines`, `sourceInfo`), `setActiveTools(names)`. + +Messages and session: `sendMessage(message, { deliverAs: "steer" | "followUp" | "nextTurn", triggerTurn })`, `sendUserMessage(content, { deliverAs, expandPromptTemplates })` (`deliverAs` required while streaming; `expandPromptTemplates` defaults to `false` and opts into extension-command dispatch plus skill/prompt-template expansion), `appendEntry(customType, data)`, `setSessionName`, `getSessionName`, `setLabel(entryId, label)`. + +Commands and input: `registerCommand(name, { description, handler, getArgumentCompletions })` (duplicate names get `:1`/`:2` suffixes in load order), `getCommands()` (extension โ†’ prompt โ†’ skill order, each with `sourceInfo.scope`/`origin`), `registerShortcut(key, options)`, `registerFlag(name, options)` + `getFlag(name)`. + +Rendering: `registerMessageRenderer(customType, renderer)` (custom messages, in LLM context), `registerEntryRenderer(customType, renderer)` (custom entries, TUI only), `registerMarkdownTransformer(transformer)`. + +`registerMarkdownTransformer` transforms the Markdown of normal user text, assistant text, and thinking blocks before Pi's built-in renderer runs. Transformers run in extension load order, each receiving the previous transformer's output plus a context of `messageType` (`"user" | "assistant" | "assistant-thinking"`), `isStreaming` (true only for partial assistant updates), and `availableWidth` (exact terminal columns): + +```typescript +pi.registerMarkdownTransformer((markdown, { messageType, isStreaming }) => { + if (isStreaming || messageType === "assistant-thinking") return markdown; + return markdown.replaceAll("-->", "โ†’"); +}); +``` + +A throwing transformer keeps the Markdown produced so far and continues with the next one. The hook is display-only โ€” the session and model context keep the original message. It fires for new user messages, assistant streaming updates, restored session messages, and terminal width changes, so keep transformers synchronous and cheap. + +Model and provider: `setModel(model)` (returns `false` without an API key), `getThinkingLevel()`, `setThinkingLevel(level)`, `registerProvider(nameOrProvider, config?)`, `unregisterProvider(name)`. Calls after the load phase take effect immediately. Dynamic providers can implement `refreshModels`, and a complete pi-ai `Provider` from `createProvider(...)` can be registered as the composition base with `models.json` overrides layered above. + +`refreshModels` receives the canonical credential/stored-catalog/network/signal context: `context.stored` is the persisted provider snapshot, and persistence goes through generation-checked `context.publish({ persist: entry })` (`persist: null` deletes the snapshot). Live servers such as llama.cpp can return models without persisting. `context.signal` is always a concrete signal and provider callbacks must pass it to blocking I/O; public `ModelRuntime.refresh()` / `ModelRegistry.refresh()` accept an optional signal and are unbounded when it is omitted, so extensions choose their own deadlines. Cancellation stops the caller waiting even if a provider ignores the signal. OAuth `refreshToken(credentials, signal)` now takes the signal as a second argument. + +Other: `exec(command, args, { signal, timeout })` โ†’ `{ stdout, stderr, code, killed }`, `on(event, handler)`, `events` (inter-extension bus). + +## Custom Tools + +```ts +pi.registerTool({ + name: "my_tool", + label: "My Tool", + description: "What this tool does (shown to the LLM)", + promptSnippet: "One-line entry in the system prompt's Available tools section", + promptGuidelines: ["Use my_tool when the user asks to summarize generated text."], + parameters: Type.Object({ action: StringEnum(["list", "add"] as const), text: Type.Optional(Type.String()) }), + prepareArguments(args) { return args; }, + async execute(toolCallId, params, signal, onUpdate, ctx) { + onUpdate?.({ content: [{ type: "text", text: "Working..." }], details: { progress: 50 } }); + return { content: [{ type: "text", text: "Done" }], details: {}, terminate: true }; + }, + renderShell: "self", + renderCall(args, theme, context) { /* Component */ }, + renderResult(result, options, theme, context) { /* Component */ }, +}); +``` + +- Use `StringEnum` from `@earendil-works/pi-ai` for string enums; `Type.Union`/`Type.Literal` breaks Google's API. +- `promptGuidelines` bullets are appended flat with no tool-name prefix โ€” always name the tool ("Use my_tool whenโ€ฆ"), never "this tool". +- `prepareArguments` runs before schema validation; use it to fold legacy argument shapes from resumed sessions instead of loosening `parameters`. +- Signal errors by **throwing** โ€” returning a value never sets `isError`. +- `terminate: true` hints that the follow-up LLM call should be skipped, and only applies when every finalized result in the batch terminates. +- Return `usage` for nested LLM calls; Pi persists it and includes it in footer, `/session`, and RPC totals. +- Strip a leading `@` from path arguments (some models add it), and wrap read-modify-write windows in `withFileMutationQueue(absolutePath, fn)` so the tool shares the per-file queue with built-in `edit`/`write` โ€” tools run in parallel by default. Resolve to an absolute path first; the helper canonicalizes existing files through `realpath()`. +- Truncate output. The built-in limit is 50 KB / 2000 lines, whichever hits first. Use `truncateHead` (beginning matters), `truncateTail` (end matters), `truncateLine`, `formatSize`, `DEFAULT_MAX_BYTES`, `DEFAULT_MAX_LINES`, and tell the LLM where the full output was saved. + +Overriding built-ins (`read`, `bash`, `edit`, `write`, `grep`, `find`, `ls`) works by registering the same name; interactive mode warns. Renderer inheritance is per slot, so omitting `renderCall`/`renderResult` keeps the built-in UI. `promptSnippet`/`promptGuidelines` are **not** inherited. The result shape, including `details`, must match exactly. + +Remote execution: built-in tool factories accept pluggable `operations` (`ReadOperations`, `WriteOperations`, `EditOperations`, `BashOperations`, `LsOperations`, `GrepOperations`, `FindOperations`). `createBashTool(cwd, { spawnHook, exposeSessionEnvironment })` can rewrite command/cwd/env; session variables are injected before `spawnHook` (`references/environment-variables.md`). + +### Dynamic Tool Loading + +Register every tool, keep only loader tools active, then call `pi.setActiveTools([...current, ...matched])` during loader execution. The change must be purely additive. Pi records the added names on the loader's tool result and exposes the definitions before the next model request โ€” natively via `defer_loading`/`tool_reference` on Anthropic Sonnet/Opus/Fable 4.5+ and via `tool_search_call`/`tool_search_output` on OpenAI `gpt-5.4`+, otherwise by sending the normal active tool list. Verified custom endpoints can opt in with `compat.supportsToolReferences` (anthropic-messages) or `compat.supportsToolSearch` (openai-responses / openai-codex-responses). Non-additive changes fall back to the full list. Lazily loaded tools should rely on `description` and omit `promptSnippet`/`promptGuidelines`, which rebuild the system prompt and can invalidate the cached prefix. + +## State Management + +Store state in tool result `details` so branching works, and rebuild it in `session_start` by walking `ctx.sessionManager.getBranch()`. `pi.appendEntry(customType, data)` persists extension state that does not enter LLM context. + +## Custom UI + +Dialogs: `ctx.ui.select(title, options)`, `confirm(title, message)`, `input(prompt, placeholder)`, `editor(title, prefill)`, `notify(message, "info" | "warning" | "error")`. Dialogs accept `{ timeout }` (live countdown; `select`/`input` return `undefined`, `confirm` returns `false`) or `{ signal }` when you need to distinguish timeout from user cancel. + +Chrome: `setStatus(key, text)`, `setWidget(key, lines | factory, { placement: "aboveEditor" | "belowEditor" })`, `setFooter(factory)`, `setHeader(...)`, `setTitle(text)`, `setEditorText` / `getEditorText` / `pasteToEditor`, `setWorkingMessage`, `setWorkingVisible`, `setWorkingIndicator({ frames, intervalMs })`, `setToolsExpanded` / `getToolsExpanded`, `setEditorComponent(factory)` / `getEditorComponent()`, `addAutocompleteProvider(current => provider)`, `getAllThemes` / `getTheme` / `setTheme` / `theme`, and `custom(factory, { overlay, overlayOptions, onHandle })`. + +Custom-component details, overlays, built-in components, and copy-paste patterns are in `references/tui.md`. Syntax highlighting helpers: `highlightCode(code, lang, theme)`, `getLanguageFromPath(path)`. Keybinding hints: `keyHint(id, description)`, `keyText(id)`, `rawKeyHint(key, description)` with namespaced ids (`app.*` for the coding agent, `tui.*` for shared TUI). + +## Error Handling and Mode Behavior + +Extension errors are logged and the agent continues; `tool_call` errors block the tool (fail-safe); tool `execute` errors must be thrown and are reported to the LLM with `isError: true`. + +| Mode | `ctx.mode` | `ctx.hasUI` | Notes | +|---|---|---|---| +| Interactive | `"tui"` | `true` | Full TUI | +| RPC | `"rpc"` | `true` | Dialogs/notifications over the JSON protocol; `custom()` returns `undefined` | +| JSON | `"json"` | `false` | UI methods are no-ops | +| Print (`-p`) | `"print"` | `false` | Extensions run but cannot prompt | + +Guard TUI-only features with `ctx.mode === "tui"`; guard dialogs and notifications with `ctx.hasUI`. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/models.md b/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/models.md index e6a05345..8566e0fe 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/models.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/models.md @@ -2,9 +2,9 @@ title: "Custom Models" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/pi-agent/references/models.md -upstream_sha: 9c9bd2e9 -imported_at: 2026-06-27 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/references/models.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: prompt upstream_changes: accepted author: upstream @@ -39,41 +39,89 @@ Add custom providers and models through `~/.pi/agent/models.json` for Ollama, LM } ``` -The `apiKey` is required even when the server ignores it. Edit during a session; `/model` reloads the file. +Only `id` is required per model. The `apiKey` placeholder matters because Pi treats models as requiring auth before they appear in `/model`; keyless local servers should keep a dummy value, store a key via `/login`, or pass `--api-key`. Set `compat.supportsDeveloperRole: false` for OpenAI-compatible servers that reject the `developer` role, and `compat.supportsReasoningEffort: false` when `reasoning_effort` is unsupported. `compat` can be set at provider level (all models) or model level (override). The file reloads each time `/model` opens โ€” no restart needed. + +For `google-generative-ai` custom models, `baseUrl` is required (for example `https://generativelanguage.googleapis.com/v1beta`). ## Supported APIs -- `openai-completions`: OpenAI Chat Completions and compatibles. -- `openai-responses`: OpenAI Responses API. -- `anthropic-messages`: Anthropic Messages API. -- `google-generative-ai`: Google Generative AI. +`openai-completions` (most compatible), `openai-responses`, `anthropic-messages`, `google-generative-ai`. Set `api` at provider level as the default, or per model to override. ## Provider Fields -`baseUrl`, `api`, `apiKey`, `headers`, `authHeader`, `models`, `modelOverrides`. +`baseUrl`, `api`, `apiKey`, `oauth`, `headers`, `authHeader`, `models`, `modelOverrides`. -`apiKey` and `headers` support command execution (`!command`), env interpolation (`$ENV`/`${ENV}`), escapes (`$$`, `$!`), and literals. Shell commands in `models.json` resolve at request time and do not get built-in TTL/recovery; wrap slow or flaky secret commands yourself. +- `oauth`: dynamic OAuth provider type; currently `"radius"`, which requires the gateway `baseUrl`. +- `authHeader: true` adds `Authorization: Bearer `. +- `apiKey` is optional โ€” omit it when auth comes from `/login`/`auth.json`/`--api-key`. Non-built-in providers with `models` need `baseUrl` and an `api` value at provider or model level. Without any auth, models load but stay unavailable in `/model` and `--list-models`. + +`apiKey` and `headers` support command execution (`!command`, whole value), env interpolation (`$ENV` / `${ENV}`, works inside larger literals), escapes (`$$`, `$!`), and literals (plain uppercase strings are literals). Shell commands in `models.json` resolve at request time with no built-in TTL, stale reuse, or recovery โ€” wrap slow or flaky secret commands yourself. `/model` availability checks use configured auth presence and do not execute shell commands. ## Model Fields -Required: `id`. +Required: `id`. Optional: `name` (defaults to `id`), `api`, `reasoning` (`false`), `thinkingLevelMap`, `input` (`["text"]` or `["text","image"]`), `contextWindow` (`128000`), `maxTokens` (`16384`), `samplingParams`, `cost` (zeros), `compat` (merged with provider `compat`). -Optional: `name`, `api`, `reasoning`, `thinkingLevelMap`, `input`, `contextWindow`, `maxTokens`, `cost`, `compat`. +`/model`, `--list-models`, and the footer display entries by model `id`; `name` is used for `--model` pattern matching and secondary detail text. -`thinkingLevelMap` maps Pi levels (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`) to provider values; `null` hides unsupported levels. +`cost` is per million tokens and supports request-wide input pricing tiers. A tier supplies a complete alternate rate set and applies to the whole request when `input + cacheRead + cacheWrite` exceeds `inputTokensAbove`; the highest matching threshold wins. -## Built-in Overrides +```json +{ + "cost": { + "input": 5, "output": 30, "cacheRead": 0.5, "cacheWrite": 6.25, + "tiers": [{ "inputTokensAbove": 272000, "input": 10, "output": 45, "cacheRead": 1, "cacheWrite": 12.5 }] + } +} +``` -Override a provider base URL without redefining models: +## Sampling Parameters + +`samplingParams` is a free-form object merged verbatim into every request body for that model, after the fields Pi sets itself โ€” so its keys win, including over Pi's own `temperature`. Use it for sampling controls Pi does not model, such as llama.cpp `min_p` or vLLM `top_k`: + +```json +{ "id": "deepseek-v4-flash", + "samplingParams": { "temperature": 1.0, "top_p": 0.95, "top_k": 0, "min_p": 0.0 } } +``` + +Only OpenAI-compatible APIs apply it (`openai-completions`, `openai-responses`, `azure-openai-responses`); other APIs ignore it. Treat it as the single source of sampling truth for a model. In `modelOverrides`, `samplingParams` merges per key with the base model's value. + +## Thinking Level Map + +Keys are Pi levels: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`. Maps may have holes. Values are tristate: omitted means standard levels through `high` use the provider default mapping while `xhigh`/`max` are unsupported; a string is sent to the provider; `null` marks the level unsupported so it is hidden/skipped/clamped away. + +```json +{ "id": "deepseek-v4-pro", "reasoning": true, + "thinkingLevelMap": { "minimal": null, "low": null, "medium": null, "high": "high", "xhigh": null, "max": "max" } } +``` + +Use `{ "off": null }` for models where thinking cannot be disabled. Older `compat.reasoningEffortMap` configs should move to model-level `thinkingLevelMap`. + +## Overriding Built-in Providers + +Base-URL-only overrides keep all built-in models and existing auth: ```json { "providers": { "anthropic": { "baseUrl": "https://my-proxy.example.com/v1" } } } ``` -If `models` is included, custom models merge into built-ins by `id`; matching IDs replace built-ins. Use `modelOverrides` to modify specific built-in models without replacing the provider list. +If `models` is included, built-in models are kept and custom models are upserted by `id` โ€” a matching `id` replaces the built-in, a new `id` is added. -## Compatibility Flags +`modelOverrides` customizes built-in and matching extension-registered models without replacing the provider list. Supported per-model fields: `name`, `reasoning`, `thinkingLevelMap`, `input`, `cost` (partial), `contextWindow`, `maxTokens`, `samplingParams` (merged per key), `headers`, `compat`. Unknown model IDs are ignored; provider-level `baseUrl`/`headers` can be combined with it. If `models` is also defined, custom models merge after built-in overrides. -Anthropic flags include `supportsEagerToolInputStreaming`, `supportsLongCacheRetention`, `sendSessionAffinityHeaders`, `supportsCacheControlOnTools`, `forceAdaptiveThinking`, and `allowEmptySignature`. +Example โ€” opt a direct OpenAI GPT-5.6 model into the 1.05M context window (they default to `272000` to stay in the short-context pricing tier): -OpenAI compatibility flags include `supportsStore`, `supportsDeveloperRole`, `supportsReasoningEffort`, `supportsUsageInStreaming`, `maxTokensField`, `requiresToolResultName`, `requiresAssistantAfterToolResult`, `requiresThinkingAsText`, `requiresReasoningContentOnAssistantMessages`, `thinkingFormat`, `cacheControlFormat`, `supportsStrictMode`, `supportsLongCacheRetention`, `openRouterRouting`, and `vercelGatewayRouting`. +```json +{ "providers": { "openai": { "modelOverrides": { "gpt-5.6-sol": { "contextWindow": 1050000 } } } } } +``` + +## Anthropic Messages Compatibility Flags + +`supportsEagerToolInputStreaming` (default `true`; `false` omits `tools[].eager_input_streaming` and sends the legacy fine-grained-tool-streaming beta header), `supportsLongCacheRetention` (default `true`, `cache_control.ttl: "1h"`), `sendSessionAffinityHeaders` (auto-detected), `supportsCacheControlOnTools` (default `true`), `forceAdaptiveThinking` (`thinking.type: "adaptive"` plus `output_config.effort`; built-in adaptive models set it automatically), `allowEmptySignature` (replay empty thinking signatures as `signature: ""`), `supportsStrictTools` (default `false`; built-in Anthropic models enable it). + +## OpenAI Compatibility Flags + +`supportsStore`, `supportsDeveloperRole`, `supportsReasoningEffort`, `supportsUsageInStreaming` (default `true`), `supportsFinishReason` (default `true`; `false` makes Pi infer `stop`/`toolUse` when the stream ends without one), `maxTokensField` (`max_completion_tokens` | `max_tokens`), `requiresToolResultName`, `requiresAssistantAfterToolResult`, `requiresThinkingAsText`, `requiresReasoningContentOnAssistantMessages`, `thinkingFormat` (`reasoning_effort`, `openrouter`, `deepseek`, `together`, `baseten`, `zai`, `qwen`, `chat-template`, `qwen-chat-template`), `chatTemplateKwargs`, `chatTemplateArgs` (`chat_template_args` values for `thinkingFormat: "baseten"`), `cacheControlFormat` (`anthropic`), `sendSessionAffinityHeaders` (default `false`), `sessionAffinityFormat` (`openai`, `openai-nosession`, `openrouter`), `supportsStrictMode`, `supportsOpenAIGrammarTools` (default `false`; the built-in catalog enables it for GPT-5+ on OpenAI, OpenAI Codex, Azure OpenAI, GitHub Copilot, opencode, Cloudflare AI Gateway), `deferredToolsMode` (`"kimi"`), `supportsLongCacheRetention` (default `true`; `prompt_cache_retention: "24h"`), `openRouterRouting`, `vercelGatewayRouting`. + +Thinking-format notes: `openrouter` uses `reasoning: { effort }`; `together` uses `reasoning: { enabled }` plus `reasoning_effort` when `supportsReasoningEffort`; `qwen` uses top-level `enable_thinking`; `qwen-chat-template` targets local Qwen servers needing `chat_template_kwargs.enable_thinking` and `preserve_thinking`; `chat-template` plus `chatTemplateKwargs` targets vLLM/Hugging Face templates, e.g. `chatTemplateKwargs: { "thinking": { "$var": "thinking.enabled" } }` for DeepSeek V3.x. `baseten` plus `chatTemplateArgs` targets providers exposing toggle controls through `chat_template_args`, optionally with top-level `reasoning_effort`. `$var` accepts `thinking.enabled` or `thinking.effort`. + +`openRouterRouting` is sent as-is in the OpenRouter `provider` field (`only`, `order`, `ignore`, `sort`, `max_price`, `quantizations`, `zdr`, โ€ฆ). `vercelGatewayRouting` takes `only`/`order`. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/pi-interview.md b/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/pi-interview.md index 7c587653..70c4aca9 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/pi-interview.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/pi-interview.md @@ -2,9 +2,9 @@ title: "pi-interview Package" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/pi-agent/references/pi-interview.md -upstream_sha: 9c9bd2e9 -imported_at: 2026-06-27 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/references/pi-interview.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: prompt upstream_changes: accepted author: upstream @@ -15,25 +15,31 @@ validated: false Source: https://pi.dev/packages/pi-interview -Interactive interview form extension: the agent collects structured user responses through forms with single/multi-select, text input, image upload, and info panels, plus rich media (code, diffs, Markdown, images, Chart.js charts, Mermaid diagrams, tables, HTML). Requires pi-agent v0.35.0+. +Interactive interview forms: the agent collects structured user responses through a form with single/multi-select, text input, image upload, and info panels, plus rich media (code, diffs, Markdown, images, Chart.js charts, Mermaid diagrams, tables, HTML). ```bash pi install npm:pi-interview -pi install npm:glimpseui # optional: native macOS windows (browser fallback otherwise) +pi install npm:glimpseui # optional: native macOS window; browser fallback otherwise ``` -## Invocation +Requires Pi v0.82.1 or later. Restart Pi after installing. -Agents call the tool directly: +## Invocation ```javascript await interview({ questions: '/path/to/questions.json', - timeout: 600, // optional, seconds + timeout: 600, // optional, seconds (default 600) verbose: false // optional, debug logging }); ``` +Lifecycle: the tool starts a local server and opens a Glimpse window (macOS), an Orca tab, or a browser tab โ†’ the user answers at their own pace with auto-save and timeout reset on any activity โ†’ the session ends by Submit (`โŒ˜+Enter`), timeout (warning overlay with an option to stay), or Escape twice โ†’ the window closes and the agent receives responses, or `null` if cancelled. + +Remote and Moshi sessions: when the session looks remote (ssh/mosh env, or an active remote login on the host), the tool skips or supplements the local window and prints the form URL with access hints โ€” a Moshi tip when the moshi-hook gateway is running (tap the preview button in the terminal title bar and pick the interview server), and an exact `ssh -L` command for plain SSH (mosh cannot forward ports). The server binds low ports (8377+, scanning forward on collision) and answers tokenless loopback opens with a landing page that hops to the form, so Moshi's browser preview reaches it in one tap. Requests with a non-loopback `Host` header are rejected. + +With multiple concurrent interviews, only the first auto-opens; the rest are queued and surfaced as URLs in tool output, plus a top-right toast with a dropdown to open queued sessions. Submitting the active interview redirects the window to the next queued one. A status bar shows project path, git branch, and session ID. + ## Question Schema ```json @@ -41,10 +47,15 @@ await interview({ "title": "Project Setup", "description": "Review my suggestions and adjust as needed.", "questions": [ - { "id": "context", "type": "info", "question": "Architecture context", "context": "This project needs SSR and edge deployment support." }, + { "id": "context", "type": "info", "question": "Architecture context", + "context": "This project needs SSR and edge deployment support." }, { "id": "framework", "type": "single", "question": "Which framework?", "options": ["React", "Vue", "Svelte"], - "recommended": "React", "conviction": "strong", "weight": "critical" } + "recommended": "React", "conviction": "strong", "weight": "critical" }, + { "id": "features", "type": "multi", "question": "Which features?", + "options": ["Auth", "Database", "API"], "recommended": ["Auth", "Database"] }, + { "id": "notes", "type": "text", "question": "Additional requirements?" }, + { "id": "mockup", "type": "image", "question": "Upload a design mockup" } ] } ``` @@ -54,18 +65,24 @@ Question types: `single` (radio), `multi` (checkbox), `text`, `image` (upload), | Field | Purpose | |---|---| | `id`, `type`, `question` | Identifier, type, question text | -| `options` | Choices for single/multi; strings or `{ label, content }` objects | -| `recommended` | Pre-selected option(s) with badge | -| `conviction` | `"strong"` or `"slight"` โ€” controls pre-selection | -| `weight` | `"critical"` or `"minor"` โ€” visual prominence | -| `context` | Help text | -| `content` | Code/diff/Markdown block: `{ source, lang, file, lines, highlights, showSource }`; `lang: "diff"` renders diffs, `lang: "md"` renders Markdown preview | -| `media` | Object or array: types `image`, `table`, `chart`, `mermaid`, `html`; each supports `position`: `"above"`/`"below"`/`"side"` and `caption`; tables take `{ headers, rows, highlights }` | +| `options` | Choices for single/multi; strings or `{ label, content? }` objects | +| `recommended` | Pre-selected option(s) with a "Recommended" badge | +| `conviction` | `"strong"` or `"slight"` (slight opts out of pre-selection); requires `recommended` | +| `weight` | `"critical"` (prominent card) or `"minor"` (compact card) | +| `context` | Help text below the question | +| `content` | Code/diff/Markdown block: `{ source, lang, file, lines, highlights, showSource }`; `lang: "diff"` renders a diff, `lang: "md"`/`"markdown"` previews Markdown | +| `media` | Object or array of `image`, `table`, `chart`, `mermaid`, `html`; each supports `position` (`"above"`/`"below"`/`"side"`) and `caption`; tables take `{ headers, rows, highlights }` | + +Single/multi questions also support an "Other" custom-text option, per-question image attachments (button or drag & drop), "โœฆ Generate more" and "โ†ป Review options" LLM actions, an "Ask about an option" inline assistant panel with prompt chips and provider/model overrides, and an optional per-option clarification field. ## Response Format ```typescript -interface Response { id: string; value: string | string[]; attachments?: string[]; } +interface Response { + id: string; + value: string | string[]; + attachments?: string[]; // image paths attached to non-image questions +} ``` ## Settings @@ -80,19 +97,40 @@ interface Response { id: string; value: string | string[]; attachments?: string[ "snapshotDir": "~/.pi/interview-snapshots/", "autoSaveOnSubmit": true, "generateModel": "anthropic/claude-haiku-4-5", - "theme": { "mode": "auto", "name": "default", "lightPath": "/path/to/light.css", "darkPath": "/path/to/dark.css", "toggleHotkey": "mod+shift+l" } + "launcher": "browser", + "browser": "Firefox", + "glimpseFloating": false, + "theme": { + "mode": "auto", + "name": "default", + "lightPath": "/path/to/light.css", + "darkPath": "/path/to/dark.css", + "toggleHotkey": "mod+shift+l" + } } } ``` -Timeout precedence: function parameter > settings > default 600s. Built-in themes: `default` (monospace) and `tufte` (serif); modes `dark` (default), `light`, `auto`. Custom themes are CSS files overriding variables like `--bg-body`, `--bg-card`, `--accent`, `--error`. +Timeout precedence: function parameter > settings > default 600s. A fixed `port` keeps the URL stable across sessions. `generateModel` drives the generate/review option actions, defaulting to the agent's current model then a cheap available model; if an explicitly configured model fails and the session uses a different one, it retries once with the session model. `glimpseFloating` keeps the native macOS window above others (browser fallback unaffected). + +`launcher` chooses where the form opens; omit it for the default (Glimpse on a local macOS session with `glimpseui` installed, otherwise a browser tab): + +- `"glimpse"` โ€” native macOS Glimpse window; requires a local macOS session with `glimpseui`, and reports why the window could not open instead of falling back to a browser. +- `"browser"` โ€” browser tab even when Glimpse is installed. +- `"orca"` โ€” a browser tab in the current [Orca](https://github.com/stablyai/orca)-managed worktree, or Orca's focused worktree when the cwd is outside one; the tab is focused when that worktree is visible, otherwise staged in its tab bar. Needs `orca` on `PATH`. + +`browser` names the application used for browser tabs (`"Firefox"`, `"Brave Browser"`, โ€ฆ). It applies to `launcher: "browser"` and to an omitted `launcher` when Glimpse is unavailable; it has no effect under `"glimpse"` or `"orca"`. + +Themes: built-ins are `default` (monospace) and `tufte` (serif); modes are `dark` (default), `light`, and `auto` (follows the OS, user override persists in localStorage). Custom themes are CSS files overriding variables such as `--bg-body`, `--bg-card`, `--bg-elevated`, `--bg-selected`, `--fg`, `--fg-muted`, `--accent`, `--border`, `--success`, `--warning`, `--error`, `--focus-ring`. + +## Keyboard + +`โ†‘`/`โ†“` navigate options, `โŒ˜+โ†`/`โŒ˜+โ†’` navigate questions (Ctrl off macOS), `Tab` cycles, `Enter`/`Space` selects, `โŒ˜+V` pastes into the focused input, `โŒ˜+Enter` submits, `Esc` shows the exit overlay (twice to quit), `โŒ˜+Shift+L` toggles the theme when enabled. ## Recovery and Snapshots -Abandoned/timed-out interviews save to `~/.pi/interview-recovery/{date}_{time}_{project}_{branch}_{sessionId}.json` (auto-deleted after 7 days). Submissions can auto-save snapshots (`index.html` + `images/`) to `~/.pi/interview-snapshots/`. Resume either by passing the recovery JSON or snapshot `index.html` path as `questions`. +Abandoned or timed-out interviews save their questions to `~/.pi/interview-recovery/{date}_{time}_{project}_{branch}_{sessionId}.json`, auto-deleted after 7 days. Snapshots (manual Save button, or automatic on submit with `autoSaveOnSubmit`) land in `~/.pi/interview-snapshots/{title}-{project}-{branch}-{timestamp}[-submitted]/` as `index.html` plus an `images/` subfolder. Resume either by passing the recovery JSON or the snapshot `index.html` path as `questions` โ€” the form reopens with answers pre-populated. -## Keyboard and Limits +## Limits -`โ†‘`/`โ†“` navigate options, `โŒ˜+โ†`/`โŒ˜+โ†’` navigate questions (Ctrl on non-macOS), `Tab` cycles, `Enter`/`Space` selects, `โŒ˜+Enter` submits, `Esc` twice quits, `โŒ˜+Shift+L` toggles theme. Auto-saves via localStorage; detects multi-agent queues. - -Image limits: max 12 per submission, 5MB each, 4096ร—4096 px, PNG/JPG/GIF/WebP. +Max 12 images per submission, 5 MB per image, 4096ร—4096 pixels, types PNG/JPG/GIF/WebP. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/pi-mcp-adapter.md b/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/pi-mcp-adapter.md index 73cdd8e2..88814bef 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/pi-mcp-adapter.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/pi-mcp-adapter.md @@ -2,9 +2,9 @@ title: "pi-mcp-adapter Package" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/pi-agent/references/pi-mcp-adapter.md -upstream_sha: 9c9bd2e9 -imported_at: 2026-06-27 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/references/pi-mcp-adapter.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: prompt upstream_changes: accepted author: upstream @@ -15,83 +15,190 @@ validated: false Source: https://pi.dev/packages/pi-mcp-adapter -MCP adapter extension for Pi. Instead of loading hundreds of MCP tool definitions upfront (10,000+ tokens per server), it exposes one `mcp` proxy tool (~200 tokens) that discovers and calls tools on demand. Servers connect lazily and disconnect when idle. +MCP adapter extension for Pi. Instead of loading hundreds of tool definitions upfront, it exposes one `mcp` proxy tool (~200 tokens) that discovers and calls tools on demand. Servers connect lazily and disconnect when idle; tool metadata is cached to disk so search/describe work offline. ```bash pi install npm:pi-mcp-adapter ``` -Restart Pi after installation. +Restart Pi after installation. On first run the adapter reads standard MCP files automatically. If you only have host-specific configs (Cursor, Claude Code, Codex, โ€ฆ), run `/mcp setup` to adopt them, or `pi-mcp-adapter init` to scan and add compatibility imports to the Pi agent dir config. ## Configuration Files -Precedence (highest to lowest): +Precedence, lowest to highest: -1. `~/.config/mcp/mcp.json` โ€” user-global shared config -2. `/mcp.json` โ€” Pi global override -3. `.mcp.json` โ€” project-local shared config -4. `.pi/mcp.json` โ€” Pi project override +1. `~/.config/mcp/mcp.json` โ€” user-global shared +2. `~/.agents/mcp.json` โ€” user-global tool-agnostic +3. `~/.agents/mcp/mcp.json` โ€” user-global tool-agnostic +4. `/mcp.json` โ€” Pi global override (`~/.pi/agent/mcp.json`, or `$PI_CODING_AGENT_DIR/mcp.json`) +5. `.mcp.json` โ€” project-local shared (preferred for projects) +6. `.pi/mcp.json` โ€” Pi project override ```json { "mcpServers": { - "chrome-devtools": { "command": "npx", "args": ["-y", "chrome-devtools-mcp@latest"] } + "chrome-devtools": { "command": "npx", "args": ["-y", "chrome-devtools-mcp@1.6.0"] } } } ``` -Import existing configs with `"imports": ["cursor", "claude-code", "claude-desktop"]` (also `vscode`, `windsurf`, `codex`). +Host-specific configs are detected but **not** loaded automatically, and the normal `/mcp` panel does not scan them while `settings.hostConfigDiscovery` is `"off"` (the default). Opt in with `"on"` (or `pi-mcp-adapter init --discover-host-configs`); `"prompt"` detects without activating. Host configs sit below every shared and Pi-owned source. + +Import specific host formats explicitly with `"imports": ["cursor", "claude-code", "claude-desktop", "opencode", "vscode", "windsurf", "codex"]`. + +### Agent Plugins + +List [Agent Plugins](https://agent-plugins.org/) package directories in `settings.agentPluginPaths` to load their MCP servers: + +```json +{ "settings": { "agentPluginPaths": ["./plugins/acme-tools"] }, "mcpServers": {} } +``` + +Each directory needs a valid Agent Plugins 1.0 `plugin.json`; a root `mcp.json` there contributes `mcpServers` entries prefixed `__`. The loader uses the transport declared by each server `type` and skips invalid entries without blocking others. For stdio plugin servers, `${PLUGIN_ROOT}` and `${PLUGIN_DATA}` expand only in `args`, `env`, and `cwd`; both are set for the child process and plugin data is stored under the Pi agent directory. Native Pi MCP config remains `.mcp.json`, `~/.config/mcp/mcp.json`, and the Pi-owned overrides. + +`/mcp disable ` / `/mcp enable ` persist only the `disabled` field into `.pi/mcp.json` (never rewriting the source file or copying credentials); run `/reload` to apply. The manual equivalent is `{ "disabled": true }` in any MCP config. + +### SDK Configuration + +```ts +import { createMcpAdapter } from "pi-mcp-adapter"; +const extension = createMcpAdapter({ config: { mcpServers: { docs: { url: "https://mcp.example.com/mcp", lifecycle: "eager" } } } }); +``` + +A supplied `config` is a complete isolated snapshot โ€” never merged with files, imports, or `--mcp-config`, and cloned per adapter/session. Status, reconnect, explicit `/mcp-auth `, proxy calls, and direct tools still work; setup and no-argument auth/status panels report the limitation. With `configPath` and no `config`, normal file merging applies and that path outranks argv and `--mcp-config`. The package ships TypeScript source, so standalone Node processes need a TS-capable loader (e.g. `node --import tsx`). + +OAuth credentials are stored in the OS credential store keyed by configured server name, with URL binding so credentials cannot be reused for a different server URL. `settings.oauthDir` / `MCP_OAUTH_DIR` are legacy plaintext `tokens.json` import locations only. + +Cooperating Pi extensions can reuse URL-bound tokens through the public `pi-mcp-adapter/oauth` subpath (`getMcpOAuthTokensForUrl(server, url)`, `updateMcpOAuthTokensForUrl(server, url, tokens)`, plus a status helper). The async read path applies the adapter's refresh logic first. It exposes token read/update only โ€” never client registration secrets, PKCE verifiers, or OAuth state โ€” and keeps secure-store storage, URL binding, refresh persistence, chunk handling, legacy import, and fail-closed credential-store errors. ## Server Options | Field | Description | |---|---| -| `command`, `args`, `env`, `cwd` | stdio transport; `env`/`cwd` support `${VAR}`, `$env:VAR`, `~` | -| `url`, `headers` | HTTP endpoint (StreamableHTTP with SSE fallback); headers support interpolation | +| `command`, `args` | stdio transport; mutually exclusive with `url` and `socket` | +| `socket` | `rmcp-mux` Unix socket path; supports `${VAR}`, `$env:VAR`, `~` | +| `env`, `cwd` | Interpolation supported; an `env` value starting with `!` runs a command at connect (`!!` escapes) | +| `url`, `headers` | StreamableHTTP with SSE fallback; interpolation supported, missing URL vars fail before any request | | `auth` | `"bearer"` or `"oauth"` | -| `oauth` | `{ grantType, clientId, clientSecret, scope, redirectUri }` | -| `lifecycle` | `"lazy"` (default: connect on first call, idle disconnect), `"eager"` (connect at startup), `"keep-alive"` (startup + health checks + auto-reconnect) | -| `idleTimeout` | Minutes before idle disconnect (default 10) | -| `exposeResources` | Expose MCP resources as tools (default true) | -| `directTools` | `true`, `string[]`, or `false` โ€” register tools directly instead of via proxy | -| `excludeTools` | Tool names to hide | -| `debug` | Show server stderr (default false) | +| `oauth.*` | `grantType` (`authorization_code` default, or `client_credentials`), `clientId` (MCP 2026 prefers pre-registered clients or Client ID Metadata Documents; Dynamic Client Registration is the fallback when omitted), `clientSecret`, `scope`, `redirectUri` (exact localhost callback for pre-registered clients), `clientName`, `clientUri` (defaults to the host manifest's `piConfig.clientUri`, omitted under a rebranded host), `logoUri` (absolute `http(s)` URL, RFC 7591 `logo_uri`), `authorizationParams` (extra authorization-URL parameters; flow-owned ones such as `client_id`, `redirect_uri`, `scope`, `state`, `code_challenge`, `response_type`, `resource` cannot be overridden), `skipIssuerMetadataValidation` | +| `bearerToken` / `bearerTokenEnv` | Token or env var name; interpolation and `!command` supported | +| `lifecycle` | `"lazy"` (default), `"eager"`, `"keep-alive"`, `"lazy-keep-alive"` | +| `idleTimeout` | Minutes before idle disconnect (overrides global) | +| `requestTimeoutMs` | Per-server request timeout; omitted or `<= 0` uses the MCP SDK default | +| `protocolVersion` | `"legacy"` (default), `"auto"`, or `"2026-07-28"` โ€” modern negotiation is opt-in | +| `exposeResources` | Expose MCP resources as tools (default `true`) | +| `directTools` | `true`, `string[]`, or `false` | +| `toolPrefix` | Per-server override of the global `toolPrefix` | +| `includeTools` / `excludeTools` | Names or glob patterns; `excludeTools` applies after `includeTools` | +| `searchKeywords` | `{ "tool-or-glob": ["keyword", โ€ฆ] }` extra keywords boosting `mcp({ search })` ranking; never shown to the model | +| `approveTools` | `true` or glob array requiring approval before calls (overrides the global setting) | +| `debug` | Show server stderr (default `false`) | +| `trace` | Metadata-only JSONL protocol tracing for this server | +| `disabled` | Keep visible in config/status but block connections, auth, tools, resources (only literal `true`) | -Global `settings` block: `toolPrefix`, `idleTimeout`, `directTools`, `disableProxyTool`, `autoAuth`, `sampling`, `samplingAutoApprove`, `elicitation`, `elicitationAutoOpenUrls`. +Secret values in `headers`, `bearerToken`, `oauth.clientSecret`, and stdio `env` may use a leading `!command` resolved at connect/auth time: stdin and stderr suppressed, stdout capped at 1 MiB and trimmed, 10-second limit, non-empty output required. Commands never run during discovery, merging, previewing, hashing, or rendering config. -Direct tools cost 150โ€“300 tokens each; use for 5โ€“20 targeted tools, the proxy for everything else: +`oauth.skipIssuerMetadataValidation: true` disables the RFC 8414 issuer echo check for one server. It weakens OAuth mix-up protection โ€” use it only for a known-misconfigured internal server while its metadata is being fixed, never for public or untrusted servers. + +**Protocol version negotiation** โ€” the default `"legacy"` uses the classic MCP initialize sequence with no `server/discover` or 2026 headers, preserving compatibility with deployed 2025-era servers. `"auto"` probes for MCP 2026-07-28 and conservatively falls back to the classic handshake on legacy evidence; set it for Cloudflare Workers `createMcpHandler` and other MCP SDK v2 stateless servers. For stdio servers, `"auto"` probes with a short-lived sibling process before starting the session process, so each fresh connection adds one spawn and may wait out the request timeout; explicit Unix sockets probe in place. HTTP auto negotiation uses the real Streamable HTTP connection and falls back to legacy SSE only on definitive rejection (404/405/406/415) โ€” never on auth failures, cancellation, timeouts, or server errors. `"2026-07-28"` pins that revision with no legacy or SSE fallback. Strict OAuth issuer validation applies in every mode. Adapter-level roots support, standard MCP logging presentation, and protocol cache-hint config are not yet implemented. + +**Lifecycle modes** โ€” `lazy`: connect on first tool call, disconnect after idle, cached metadata keeps search working. `eager`: connect at startup, no auto-reconnect, no idle timeout unless set. `keep-alive`: connect at startup with health checks and auto-reconnect. `lazy-keep-alive`: connect on first use, then stay resident with auto-reconnect. Any enabled `eager`/`keep-alive` server also triggers initialization at extension load, supporting hosts that never emit `session_start`. + +**rmcp-mux** โ€” point `socket` at an [`rmcp-mux`](https://github.com/VetCoders/rmcp-mux) service socket to share one stdio server across Pi sessions. The adapter owns only its client socket; the mux owns the upstream process, routing, restart policy, and socket permissions. A socket is an explicit trusted local endpoint. + +## Global Settings + +```json +{ "settings": { "toolPrefix": "server", "idleTimeout": 10, "requestTimeoutMs": 30000, "trace": { "enabled": true } } } +``` + +`toolPrefix` (`"server"` default, `"short"` strips a `-mcp` suffix, `"none"`, `"mcp"` prefixes `mcp__`; per-server `toolPrefix` overrides it), `idleTimeout` (minutes, default 10, `0` disables), `requestTimeoutMs`, `showStatusIcon` (default `true`), `mcpFooterStatus` (`"full"` default, `"compact"`, `"off"`), `toolResultRendering` (`"compact"` default self-rendered rows, or `"boxed"` for the legacy Pi tool row), `collapsedResultLines` (`1`โ€“`3`; defaults `1` compact / `3` boxed), `notifyOnStartupConnect` (default `true`; `false` suppresses routine connect notices but keeps errors and auth warnings), `hostConfigDiscovery`, `agentPluginPaths`, `approveTools`, `oauthDir`, `directTools` (global default, default `false`), `freezeDirectTools` (default `false`), `scriptMode` (default `true`; registers the MCP-only `mcpScript` plain-JavaScript tool), `disableProxyTool`, `autoAuth` (default `false`), `sampling` (default `true` when UI approval is available; honors `modelPreferences.hints`), `samplingAutoApprove` (required for sampling in non-UI sessions), `elicitation` (default `true` with UI), `outputGuard`, `trace` (`{ enabled, file, maxBytes: 262144, maxEvents: 10000 }`; the per-session JSONL defaults to `.pi/mcp-traces/` and never records payloads, prompts, arguments/results, auth data, or URLs). + +Per-server `idleTimeout`, `requestTimeoutMs`, and `approveTools` override the global values. + +### Tool Approval + +`approveTools` keeps a tool visible but gates the call โ€” useful for destructive or high-cost actions where hiding the tool would hurt planning: + +```json +{ "settings": { "approveTools": ["github_delete_*", "notion_update_*"] }, + "mcpServers": { "github": { "approveTools": ["delete_*", "merge_pull_request"] }, "docs": { "approveTools": false } } } +``` + +A matching call from the proxy tool, a direct MCP tool, `mcpScript`, a resource call, or an MCP UI iframe prompts **Allow once** / **Allow for session** / **Deny**; session approvals live in memory only, and headless sessions fail closed with an `approval_required` result. `excludeTools` still removes tools entirely โ€” `approveTools` only gates visible ones. + +Permission extensions can broker decisions by listening on `MCP_TOOL_APPROVAL_REQUEST_EVENT` (`pi-mcp-adapter:tool-approval-request`) and claiming the request synchronously with `request.claim(async () => "allow_once" | "allow_for_session" | "deny" | "abstain")`. The request carries `serverName`, `originalToolName`, `prefixedToolName`, `args`, `origin`, and an optional `signal`; the first synchronous claim wins, `allow_for_session` updates the same in-memory cache as the dialog, and `abstain`/no claim keeps the fallback behavior. Brokered approval runs for every uncached MCP call regardless of `approveTools` config. + +### Search Keywords + +Search matches literally, so per-server `searchKeywords` adds vocabulary for tools whose names and descriptions use different words: + +```json +{ "mcpServers": { "github": { "searchKeywords": { "search_code": ["grep"], "*": ["gh"] } } } } +``` + +Keys match a tool's original name, prefixed name, or a glob (`*` covers every tool on the server), and all matching entries combine. Keywords are weighted like description text with an extra boost on exact phrase matches. They affect ranked and regex search only (including `tools.search` in `mcpScript`) and never appear in tool schemas, `describe` output, direct-tool registration, or the metadata cache โ€” so keyword search works offline from cached metadata. + +## Output Guard + +On by default: inline text is capped at **50 KiB / 2000 lines** (matching Pi's `bash` guard), with the full text spilled to a temp file whose path is included so the agent can `read`/`grep` it. Image blocks pass through unchanged. Binary resource blobs up to **10 MiB** are decoded to private temp files and replaced with file references, bounded to 100 MiB and 10,000 files per session and removed at session teardown. In proxy mode `details.mcpResult` stays raw when its JSON is โ‰ค 16 KiB; larger results become a compact summary with the raw JSON spilled to a temp file (direct tools never carry `mcpResult`). Tune with `{ maxBytes, maxLines, detailsMaxBytes }`; disable with `"outputGuard": false` or `MCP_OUTPUT_GUARD=0`. Temp files are mode `0600` under the system temp dir and are not cleaned up automatically. + +## Direct Tools ```json { "mcpServers": { "github": { "directTools": ["search_repositories", "get_file_contents"] } } } ``` +`true` registers all of a server's tools individually, an array registers only those (original MCP names), omitted/`false` is proxy-only. Per-server overrides the global default. `includeTools`/`excludeTools` filter direct tools, proxy search/list/describe, and the `/mcp` panel. Each direct tool costs ~150โ€“300 tokens, so use targeted sets of 5โ€“20; for 75+ tool servers stay on the proxy. + +Direct tools register from the metadata cache (`~/.pi/agent/mcp-cache.json`, or `$PI_CODING_AGENT_DIR/mcp-cache.json`), so no startup connections are needed. The first session after adding `directTools` falls back to proxy-only while the cache populates, then hot-loads. Servers advertising list-change notifications refresh the current session. Force a refresh with `/mcp reconnect `. + +Set `settings.freezeDirectTools: true` when prompt-cache stability matters more than hot-loading: the initial sync still runs, but later automatic reconnects, lazy-connects, and list-change notifications leave the registered tool surface unchanged. Deliberate refreshes via `mcp({ connect: "server" })` or `/mcp reconnect ` still update it. + ## Proxy Tool API ```javascript -mcp({ }) // list servers -mcp({ server: "name" }) // server details -mcp({ search: "screenshot navigate" }) // search tools -mcp({ describe: "tool_name" }) // tool description -mcp({ tool: "chrome_devtools_take_screenshot", args: '{"format": "png"}' }) // call; args is a JSON string -mcp({ connect: "server-name" }) -mcp({ action: "ui-messages" }) // retrieve MCP UI messages +mcp({ }) // status / list servers +mcp({ server: "name" }) // server details (+ instructions preview) +mcp({ search: "screenshot navigate", limit: 12, offset: 0 }) // ranked tool search +mcp({ describe: "tool_name" }) +mcp({ instructions: "name" }) // full server instructions +mcp({ tool: "chrome_devtools_take_screenshot", args: { format: "png" } }) +mcp({ connect: "server-name" }) // connect or refresh +mcp({ action: "ui-messages" }) +mcp({ action: "auth-start", server: "name" }) +mcp({ action: "auth-complete", server: "name", args: { redirectUrl: "http://localhost:19876/callback?code=...&state=..." } }) ``` -## CLI Commands +`args` accepts a JSON object or a JSON string. Search covers MCP tools **and** Pi extension tools (prefixed `[pi tool]`, listed first). Space-separated words are ranked by weighted matches across name, server, description, and any configured `searchKeywords`, then paginated (`limit` defaults to 12; follow `details.nextOffset`). `regex: true` still works but paginates without ranking. Names fuzzy-match on hyphens and underscores, and an unresolvable `describe`/`tool` name returns top suggestions so the agent can fix a typo in the same turn. With `includeSchemas`, search and describe render common JSON Schema parameters as compact TypeScript shapes like `{ query: string; limit?: number; }`. For HTTP servers, a failed connect runs a one-request shape probe that turns opaque transport errors into hints such as `endpoint returned HTML (200) โ€” this URL does not appear to speak MCP`. Server `instructions` surface at three levels: a truncated head in the proxy tool description, a longer preview in `mcp({ server })`, and the full text via `mcp({ instructions })` โ€” captured at connect time and cached. -``` -/mcp # interactive panel and first-run setup -/mcp setup # guided imports and config -/mcp tools # list all available tools -/mcp reconnect [server] -/mcp logout # clear OAuth credentials -/mcp-auth [server] # OAuth setup picker +Remote/headless OAuth: `/mcp-auth ` first shows a clickable authorization URL. Open it in your local browser, approve, then select **Yes** in Pi to open the callback input โ€” the browser's localhost callback page will usually fail to load (localhost is your workstation), so copy the full URL from its address bar and paste it into Pi. When the browser can reach Pi's callback directly, the authorization screen closes on its own instead. The same flow is available through the proxy tool (`auth-start` then `auth-complete` with `redirectUrl` or `args: { code }`) for non-interactive clients. Persistent OAuth requires an available OS credential store โ€” on headless Linux, an unlocked Secret Service/libsecret keyring; the adapter fails closed rather than storing plaintext. On Linux, when credential access fails because Pi inherited a revoked session keyring, the adapter attempts recovery through `keyctl session - node ` (requires `keyctl` and `node` on `PATH`) so re-authentication can write fresh credentials without killing a long-lived tmux server. + +## Commands + +`/mcp` (interactive panel: status, tools, direct/proxy toggles, reconnect, `ctrl+a` or Enter for OAuth, Save on `ctrl+s` โ€” remappable via the `mcp.panel.save` keybinding), `/mcp setup` (imports, a minimal `.mcp.json`, curated known servers โ€” DeepWiki, Context7, Notion, GitHub, Chrome DevTools โ€” RepoPrompt quick-add, config-path inspection), `/mcp tools`, `/mcp prompts`, `/mcp reconnect [server]`, `/mcp disable `, `/mcp enable `, `/mcp logout `, `/mcp-auth [server]`. + +## Prompts, Elicitation, UI + +MCP prompt templates register as slash commands `/mcp____`, refreshed on connect. Arguments support positional and `key=value` forms with quoting; required arguments are validated before `prompts/get`. Results flatten into one user message preserving `[user]`/`[assistant]` markers. + +Elicitation forms use Pi's `select()`/`input()` dialogs with validation and a review step; explicit refusal maps to MCP `decline`, dismissal to `cancel`. URL mode is TUI-only, always shows requesting server/host/URL, and requires consent; `-32042` URL-required tool errors are handled โ€” retry the original call after completing the browser step. + +MCP UI resources open in a native macOS window via Glimpse (`pi install npm:glimpseui`) or fall back to the browser. `MCP_UI_VIEWER=browser|glimpse|none` forces or suppresses the viewer (`none` still runs the tool and returns inline results). UIs talk back โ€” message types `prompt`, `intent`, `notify`, `message`, plus custom types forwarded as intents โ€” retrieved with `mcp({ action: "ui-messages" })` (each with `type`, `sessionId`, `serverName`, `toolName`, `timestamp`). Calling the same tool again pushes a new result into the open window instead of replacing it. Tool consent gates whether UIs may call MCP tools (never / once-per-server / always), and `_meta.ui.visibility` controls audience โ€” app-only tools stay out of the model tool list, model-only tools cannot be called from the UI iframe. Browser controls: Cmd/Ctrl+Enter completes, Escape cancels. + +## Status Snapshots + +```ts +import { MCP_STATUS_EVENT, type McpStatusSnapshot } from "pi-mcp-adapter"; +pi.events.on(MCP_STATUS_EVENT, (snapshot) => { /* read-only */ }); ``` -## Behavior Notes +Includes `totalTools`, `totalResources`, `connectedCount`, `disabledCount`, and per-server `name`, `status` (connected, cached, failed, needs-auth, not-connected, disabled), `toolCount`, `disabled`, plus `resourceCount` when known and `failedAgoSeconds` on active failure. Reading status never connects a lazy server, starts auth, or exposes clients, transports, credentials, or server definitions. An initial snapshot follows initialization; an empty snapshot is emitted on shutdown. -Tool metadata is cached to disk so search/describe work offline. npx-based servers resolve to direct binaries to skip npm overhead. MCP UIโ€“capable tools open in a native macOS window via Glimpse (if installed) or a browser fallback; UI message types `prompt`, `intent`, `notify`, `message` are retrievable via `mcp({ action: "ui-messages" })`. +## Behavior Notes and Limitations -Limitations: no cross-session server sharing; MCP sampling is text-only (context, tools, audio, images rejected). +npx-based servers resolve to direct binaries, skipping the ~143 MB npm parent process. Advertised `outputSchema` supports JSON Schema draft-07 and 2020-12 (unstamped schemas use the SDK's 2020-12 default), and returned `structuredContent` is validated for both proxy and direct calls. Results use compact self-rendered rows by default โ€” collapsed success output shows the call title and the first result line plus a `Ctrl+O to expand` hint โ€” while the model still receives the full result. Set `toolResultRendering: "boxed"` for the legacy row, or `collapsedResultLines` to `2`/`3` for more collapsed text. -Subagents (`pi-subagents`) only receive direct MCP tools when listed in their `tools:` frontmatter with an `mcp:` prefix โ€” see `references/pi-subagents.md`. +Limitations: no cross-session server sharing (each Pi session runs its own server processes, unless using rmcp-mux); MCP sampling is text-only (context inclusion, tools, stop sequences, audio, and images are rejected); inline images follow Pi's image display settings; Pi still owns one separator row before self-rendered tool output, so compact mode reduces but cannot eliminate the gap. + +Subagents (`pi-subagents`) receive direct MCP tools only when listed with an `mcp:` prefix in their `tools:` frontmatter โ€” a global `directTools: true` is not enough. See `references/pi-subagents.md`. diff --git a/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/pi-subagents.md b/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/pi-subagents.md index bb1a18c5..ea86ee0d 100644 --- a/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/pi-subagents.md +++ b/upstream/K-Dense-AI-scientific-agent-skills/skills/pi-agent/references/pi-subagents.md @@ -2,9 +2,9 @@ title: "pi-subagents Package" task: "" lineage_type: import -upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/pi-agent/references/pi-subagents.md -upstream_sha: 9c9bd2e9 -imported_at: 2026-06-27 +upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/b2a92ba0/skills/pi-agent/references/pi-subagents.md +upstream_sha: b2a92ba0 +imported_at: 2026-08-14 prompt_class: prompt upstream_changes: accepted author: upstream @@ -13,135 +13,372 @@ validated: false # pi-subagents Package -Source: https://pi.dev/packages/pi-subagents +Source: https://pi.dev/packages/pi-subagents (docs: `https://github.com/nicobailon/pi-subagents/tree/main/docs`) -Extension for delegating tasks to focused child agents with sequential chains, parallel execution, dynamic fanout, worktree isolation, and acceptance gates. +Delegate work to focused child Pi sessions: code review, scouting, implementation, parallel audits, saved workflows, background jobs. Installing the extension does not start anything automatically โ€” it gives Pi a `subagent` delegation tool. ```bash pi install npm:pi-subagents ``` +Users normally ask in plain language ("Use reviewer to review this diff", "Run parallel reviewers for correctness, tests, and complexity"). Pi decides whether to call the tool, which agent to use, and how to compose the work. + ## Built-in Agents | Agent | Purpose | |---|---| -| `scout` | Fast local codebase recon: files, entry points, data flow, risks | -| `researcher` | Web/docs research with sources and a concise brief | -| `planner` | Concrete implementation plan from existing context | -| `worker` | Implementation: file editing and validation | -| `reviewer` | Code review and small fixes against task/plan | -| `context-builder` | Setup pass gathering code context and handoff material | -| `oracle` | Second opinion challenging assumptions; no edits | -| `delegate` | Lightweight general delegate close to parent behavior | +| `scout` | Fast local codebase recon: relevant files, entry points, data flow, risks, where to start | +| `researcher` | Web/docs research with sources; needs `pi-web-access` for `web_search`/`fetch_content`/`get_search_content` | +| `worker` | Implementation, including approved oracle handoffs; escalates unapproved decisions | +| `reviewer` | Code review and small fixes against task/plan, tests, edge cases, simplicity | +| `oracle` (alias `advisor`) | Second opinion before acting; challenges assumptions, no edits | +| `delegate` | Lightweight general delegate close to parent behavior (append prompt mode) | -Packaged `planner`, `worker`, `oracle` default to `context: "fork"` (branch from parent session state); others default to `fresh`. +Builtins load at the lowest priority and inherit the current Pi default model unless `subagents.defaultModel` or an override says otherwise. Packaged `worker`, `oracle`, and `advisor` default to `context: "fork"`; others default to `fresh`. Builtins opt into project-instruction inheritance so they follow repo rules. + +Recommended implementation loop: **clarify โ†’ scout โ†’ worker โ†’ fresh reviewers โ†’ worker**. + +## Execution: workflowScript + +All model-facing execution goes through `workflowScript` โ€” an ordinary JavaScript statement body with an explicit `return`. The legacy `/chain`, `/parallel`, `/run-chain`, and `/chain-prompts` commands are no longer registered, and `.chain.md`/`.chain.json` files exist only as durable legacy chains. + +```javascript +// One child +subagent({ workflowScript: `return runs.run("main", { agent: "scout", task: "Analyze the auth flow" })` }) + +// Sequential +subagent({ workflowScript: ` + const scan = await runs.run("scan", { agent: "scout", task: "Analyze auth" }); + return (await runs.run("implement", { agent: "worker", task: "Implement from: " + scan.output })).output; +` }) + +// Parallel +subagent({ workflowScript: ` + const reviews = await runs.all([ + { key: "correctness", agent: "reviewer", task: "Review correctness" }, + { key: "tests", agent: "reviewer", task: "Review tests" } + ]); + return reviews.map(r => r.output); +` }) +``` + +Globals inside the script: `runs.run(key, opts)`, `runs.all([items])`, `runs.ref`, `state.get/set` (durable mission JSON state), and `prompts.render(ref, vars?)`. For long task text containing Markdown fences or shell blocks, build the string from quoted lines joined with `\n` rather than a raw template literal. + +Workflows default to background execution; pass `async: false` for a watched foreground run with a live in-chat card (`chatProgress` forces `auto`/`off`/`live-card`). Foreground workflows default to a 30-minute timeout; async workflows have no default top-level timeout. + +`prompts.render` needs an explicit scope: `package:`, `user:`, or `project:`, each naming a top-level `.md`. Frontmatter is stripped, scalar `{{name}}` placeholders are substituted, and unknown placeholders stay unchanged. Rendering returns text only โ€” pass it explicitly as `task`. + +### Key Tool Parameters + +`agent`, `action`, `topic`, `chainName`, `config`, `context` (`fresh`/`fork` โ€” an explicit value overrides every workflow child; otherwise each child uses its own `defaultContext`), `missionId`, `mission` (object or `false`), `handoffPath`, `view` (`fleet`/`transcript`), `lines` (default 80, max 500), `agentScope`, `async`, `chatProgress`, `timeoutMs`/`maxRuntimeMs`, `toolTimeoutMs`, `turnBudget`, `toolBudget`, `usageBudget`, `cwd`, `maxOutput` (200 KB / 5000 lines), `artifacts`, `includeProgress`, `share`, `sessionDir`, `acceptance`, `gate`, plus per-item `output`, `outputMode`, `skill`, `model`, `worktree`, `resume`. + +Budgets: `turnBudget` is `{ maxTurns, graceTurns }` (warn at `maxTurns`, terminate at the next assistant boundary after the grace window); `toolBudget` is `{ soft?, hard, block? }` (block defaults to `read`/`grep`/`find`/`ls`; `"*"` blocks everything, final assistant text never blocked); `usageBudget` is root-only `{ tokens?: { soft?, hard }, costUsd?: { soft?, hard } }` where soft limits are status-only and hard limits prevent later child launches without stopping running ones. **Do not** set turn, hard tool, or tight usage budgets on mutation-capable children (implementation workers, fix workers, reviewers with edit authority) โ€” none of those measure whether a delivery slice is buildable, and a default tool budget blocks read/search tools rather than mutations. Bound writers with a narrow task and an outer `timeoutMs` instead, and request a checkpoint via `steer` before the deadline. + +`context: "fork"` fails fast when the parent session is not persisted, the leaf is missing, or the branched session cannot be created โ€” it never silently downgrades to `fresh`. Forking strips signed Anthropic `thinking`/`redacted_thinking` blocks from the child session and forces thinking `off` when the child's effective primary or fallback model resolves to the Anthropic provider or `anthropic-messages` API (unresolved models are treated conservatively). Use `fresh` when an Anthropic child needs thinking. + +`outputMode: "file-only"` returns a compact pointer (`Output saved to: /abs/report.md (48.2 KB, 2847 lines)โ€ฆ`) instead of inline text; failed runs and save errors still return inline output for debugging. A read-only child does not need filesystem access for `output` โ€” it returns the artifact in its final response and the runtime persists it. + +### Retained Children + +Completed workflow children from the current parent session stay addressable. `{ action: "children.list" }` lists up to the last 10 with run ids; a later workflow continues one by passing `resume` instead of `agent`: + +```javascript +subagent({ workflowScript: ` + let writer = await runs.run("implement", { agent: "worker", task: "Implement the accepted contract" }); + for (const pass of [1, 2]) { + const task = await prompts.render("project:writer-followup", { pass, previous: writer.output }); + writer = await runs.run("followup-" + pass, { resume: writer.runId, task }); + } + return writer; +` }) +``` + +Each resume can return a new retained run id, so loops must continue from the latest `runId`. `resume` and `agent` are mutually exclusive, the revived child keeps its stored agent/model/tool contract, and `gate` is rejected on resume items. Top-level `{ action: "resume" }` stays detached and returns a background receipt โ€” use it for a simple challenge outside a script; use `runs.run({ resume })` only when the script must await the revived output. `steer` with `mode: "follow_up"` only queues text for the next `resume`; it does not revive a completed child. ## Commands ```bash -/run [task] # single agent; --bg detached, --fork branch session -/run reviewer[model=anthropic/claude-sonnet-4] summarize this code -/chain scout "scan the codebase" -> planner "create an implementation plan" -/parallel scanner "find security issues" -> reviewer "check code style" -/run-chain -- # saved workflow -/subagents-doctor # setup diagnostics +/run [task] [--bg] [--fork] # one child +/subagents-fleet # live fleet inspector +/subagents-stop [run-id] # stop a top-level async run +/subagents-detach [run-id] # leave a foreground run running +/subagents-doctor # read-only setup diagnostics +/subagents-guide [topic] # packaged docs for the installed version +/subagents-refine # project-local refinement overlay +/subagents-models [agent] # live runtime model mapping +/subagents-watchdog [status|on|off|recommend-model|model ...|session model ...|check] +/subagents-refresh-provider-models [--force] +/subagents-generate-profiles | /subagents-load-profile | /subagents-check-profile +/prompt-workflow