Skip to content

Add CrewAI TinyFish integration#22

Open
pranavjana wants to merge 4 commits into
mainfrom
integration/crew-ai
Open

Add CrewAI TinyFish integration#22
pranavjana wants to merge 4 commits into
mainfrom
integration/crew-ai

Conversation

@pranavjana

Copy link
Copy Markdown
Collaborator

Summary

Adds crewai-tinyfish — official TinyFish tools for CrewAI agents, wrapping all four TinyFish API surfaces (Search, Fetch, Agent, Browser) as CrewAI BaseTools. Mirrors the structure and house style of the existing langchain and google-adk integrations.

What's included

  • crewai/src/crewai_tinyfish/ — four tools:
    • TinyFishSearchTool (Search), TinyFishFetchTool (Fetch), TinyFishAgentTool (Agent), TinyFishBrowserSessionTool (Browser)
    • Lazy SDK import (optional tinyfish extra), version-skew-tolerant kwargs, graceful error strings, _arun async delegation
    • TF_API_INTEGRATION="crewai" request attribution, matching the sibling packages
  • crewai/tests/ — fully offline (TinyFish client faked); 11 passing
  • crewai/examples/quickstart.py (direct tool smoke test) and research_crew.py (Search + Fetch research crew)
  • crewai/README.md — written in the house style of the langchain/adk READMEs
  • CI/CD: crewai-ci.yml (lint + test + build on PR) and crewai-publish.yml (version-gated PyPI publish on merge to main, mirroring the sibling publish workflows)

Verification

Replicated the exact CI sequence locally in a clean env (no tinyfish SDK installed, as on the runner):

  • pip install -e . -r requirements-dev.txt
  • make lint (ruff check + format check) ✓
  • make test → 11 passed ✓
  • python -m build → wheel + sdist, twine check PASSED ✓

All four tools were also exercised live against the TinyFish API (Search, Fetch, Agent, Browser-session + Playwright CDP connect), and a full CrewAI crew ran end-to-end using the Search + Fetch tools.

Publishing notes

  • crewai-tinyfish does not yet exist on PyPI; the publish workflow is version-gated, so it publishes only when pyproject.toml version is new.
  • Reuses the existing PYPI_API_TOKEN secret, which has already created new projects (tinyfish-adk, langchain-tinyfish), so it has new-project scope.

Add `crewai-tinyfish`: official TinyFish tools for CrewAI agents, wrapping
the Search, Fetch, Agent, and Browser API surfaces as CrewAI BaseTools.

- src/crewai_tinyfish: four tools with lazy SDK import, version-skew-tolerant
  kwargs, and TF_API_INTEGRATION="crewai" request attribution
- tests: fully offline (SDK faked), 11 passing
- examples: direct-tool quickstart and a Search+Fetch research crew
- CI + CD workflows mirroring the langchain/google-adk packages
  (lint/test/build on PR, version-gated PyPI publish on merge to main)
- README in the house style of the sibling integrations
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d79f8c1a-4f12-46df-85ad-8fbf3efae854

📥 Commits

Reviewing files that changed from the base of the PR and between 1e0c659 and 42508bb.

📒 Files selected for processing (7)
  • crewai/UPSTREAM.md
  • crewai/pyproject.toml
  • crewai/src/crewai_tinyfish/_serde.py
  • crewai/src/crewai_tinyfish/tinyfish_agent_tool.py
  • crewai/src/crewai_tinyfish/tinyfish_browser_tool.py
  • crewai/src/crewai_tinyfish/tinyfish_fetch_tool.py
  • crewai/src/crewai_tinyfish/tinyfish_search_tool.py
✅ Files skipped from review due to trivial changes (1)
  • crewai/UPSTREAM.md
🚧 Files skipped from review as they are similar to previous changes (6)
  • crewai/pyproject.toml
  • crewai/src/crewai_tinyfish/tinyfish_browser_tool.py
  • crewai/src/crewai_tinyfish/_serde.py
  • crewai/src/crewai_tinyfish/tinyfish_search_tool.py
  • crewai/src/crewai_tinyfish/tinyfish_fetch_tool.py
  • crewai/src/crewai_tinyfish/tinyfish_agent_tool.py

📝 Walkthrough

Walkthrough

Adds a new crewai-tinyfish package with four CrewAI tools, shared helpers, tests, examples, docs, and CI/publish workflows. It also rewrites the google-adk README and bumps that package version to 0.1.3.

Sequence Diagram(s)

sequenceDiagram
  participant research_crew_py as research_crew.py
  participant build_crew_fn as build_crew(topic)
  participant Crew
  participant WebResearcher as Web Researcher
  participant TinyFishSearchTool
  participant TinyFishFetchTool
  participant BriefingWriter as Briefing Writer

  research_crew_py->>build_crew_fn: build crew
  build_crew_fn-->>research_crew_py: Crew
  research_crew_py->>Crew: kickoff()
  Crew->>WebResearcher: run research task
  WebResearcher->>TinyFishSearchTool: search(topic)
  WebResearcher->>TinyFishFetchTool: fetch source URLs
  Crew->>BriefingWriter: run writing task with research context
  BriefingWriter-->>research_crew_py: 5-bullet cited briefing
Loading

Suggested reviewers

  • uttambharadwaj
  • simantak-dabhade
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a CrewAI TinyFish integration.
Description check ✅ Passed The description matches the changeset and correctly outlines the new CrewAI tools, tests, examples, and workflows.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch integration/crew-ai

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 markdownlint-cli2 (0.22.1)
crewai/UPSTREAM.md

markdownlint-cli2 wrapper config was not available before execution


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (8)
crewai/src/crewai_tinyfish/tinyfish_browser_tool.py (1)

101-102: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

_arun blocks the event loop. Session creation is documented as taking 10-30s (Line 45), and _run calls it synchronously. In async crews this freezes the loop for the duration. Mirror the agent tool fix by offloading to a thread.

♻️ Offload blocking work to a worker thread
-    async def _arun(self, *args: Any, **kwargs: Any) -> str:
-        return self._run(*args, **kwargs)
+    async def _arun(self, *args: Any, **kwargs: Any) -> str:
+        import asyncio
+
+        return await asyncio.to_thread(self._run, *args, **kwargs)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crewai/src/crewai_tinyfish/tinyfish_browser_tool.py` around lines 101 - 102,
The _arun method in TinyfishBrowserTool is still calling the synchronous _run
directly, which blocks the event loop during slow session creation. Update _arun
to offload the blocking _run work to a worker thread, matching the agent tool
approach, so async crews can continue running without freezing.
crewai/UPSTREAM.md (1)

24-35: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add a language to the fenced block (markdownlint MD040). Use text for the directory tree listing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crewai/UPSTREAM.md` around lines 24 - 35, The fenced directory-tree block in
UPSTREAM.md is missing a language tag, triggering markdownlint MD040; update the
fence around the tree listing to use text so the block is explicitly marked as
plain text. Keep the change localized to the existing fenced snippet that shows
the crewai_tools/tools/* directories.

Source: Linters/SAST tools

crewai/src/crewai_tinyfish/tinyfish_agent_tool.py (1)

187-188: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Use AsyncTinyFish in _arun

tinyfish 0.2.5’s agent.run is synchronous; calling it from _arun blocks the event loop. Switch this path to AsyncTinyFish and await client.agent.run(...) instead of delegating to the sync _run (thread offload only as a fallback).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crewai/src/crewai_tinyfish/tinyfish_agent_tool.py` around lines 187 - 188,
The _arun path in TinyFishAgentTool is still delegating to the synchronous _run,
which blocks the event loop. Update _arun to use AsyncTinyFish and await
client.agent.run(...) directly, keeping the sync _run only as a fallback or
separate path. Use the existing TinyFishAgentTool and _arun/_run symbols to
locate the change.
crewai/pyproject.toml (1)

38-42: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Dev dependencies are duplicated and diverging across two sources.

The dev extra here lists tinyfish/pytest/pytest-asyncio but omits ruff and build, while requirements-dev.txt lists build/pytest/pytest-asyncio/ruff but omits tinyfish. Maintaining both invites drift. Consider making one the single source of truth (e.g. requirements-dev.txt installing .[dev]).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crewai/pyproject.toml` around lines 38 - 42, The dev dependency lists in
pyproject.toml and requirements-dev.txt are drifting apart, so make one source
of truth for the dev setup. Update the dev extra in pyproject.toml and the
requirements-dev workflow to stay aligned, including tinyfish, pytest,
pytest-asyncio, ruff, and build as needed, or switch requirements-dev.txt to
install the dev extra directly so both paths use the same dependency set.
crewai/src/crewai_tinyfish/_serde.py (1)

38-47: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Minor: as_list silently mangles scalars that are iterable.

A str or dict value won't raise TypeError, so list(value) yields characters or dict keys rather than [value]. Response results/errors are normally already lists so this is unlikely to bite, but if you want strict scalar-wrapping semantics, special-case str/bytes/dict.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crewai/src/crewai_tinyfish/_serde.py` around lines 38 - 47, The as_list
helper in _serde.py is incorrectly treating some scalar-like iterables as
sequences, so strings, bytes, and dicts get split into elements or keys instead
of being wrapped as a single item. Update as_list to special-case these types
before the generic list(value) conversion, and keep the existing behavior for
None and real lists while preserving scalar-wrapping semantics for any non-list
input.
.github/workflows/crewai-publish.yml (2)

21-103: 🔒 Security & Privacy | 🔵 Trivial | ⚖️ Poor tradeoff

Pin all third-party actions to commit SHAs.

actions/checkout@v6, actions/setup-python@v6, astral-sh/setup-uv@v8.1.0, actions/upload-artifact@v7, actions/download-artifact@v8, and pypa/gh-action-pypi-publish@release/v1 are referenced by mutable tags/branches. For a publishing workflow handling release artifacts, pin to immutable commit SHAs to mitigate supply-chain risk. @release/v1 is especially risky as a moving branch ref.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/crewai-publish.yml around lines 21 - 103, Pin every
third-party GitHub Action in this workflow to an immutable commit SHA instead of
mutable tags or branches. Update the action references in the publish workflow
for actions/checkout, actions/setup-python, astral-sh/setup-uv,
actions/upload-artifact, actions/download-artifact, and
pypa/gh-action-pypi-publish so the jobs use fixed commit hashes while keeping
the existing build and publish steps unchanged.

Source: Linters/SAST tools


90-106: 🔒 Security & Privacy | 🔵 Trivial | 🏗️ Heavy lift

Consider PyPI Trusted Publishing instead of a long-lived API token.

The publish-pypi job authenticates with a static PYPI_API_TOKEN secret. Trusted Publishing (OIDC) removes the need to store/rotate a token; it requires adding permissions: id-token: write to the job and configuring a PyPI publisher. The job also has no explicit permissions block, so it should be scoped down regardless.

🔒 Trusted publishing sketch
   publish-pypi:
     name: Publish to PyPI
     needs: build
     if: needs.build.outputs.version-exists == 'false'
     runs-on: ubuntu-latest
+    permissions:
+      id-token: write
     steps:
       - name: Download distributions
         uses: actions/download-artifact@v8
         with:
           name: python-package-distributions
           path: dist/
 
       - name: Publish to PyPI
         uses: pypa/gh-action-pypi-publish@release/v1
         with:
           packages-dir: dist/
-          password: ${{ secrets.PYPI_API_TOKEN }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/crewai-publish.yml around lines 90 - 106, The publish-pypi
job is using a long-lived PYPI_API_TOKEN secret instead of PyPI Trusted
Publishing and has no explicit permissions scoping. Update the publish-pypi job
in crewai-publish.yml to use OIDC by adding the required id-token: write
permission (and only the minimal permissions needed) and remove the
password-based secret from the pypa/gh-action-pypi-publish@release/v1 step. Keep
the job aligned with Trusted Publishing setup for the existing publish workflow.

Source: Linters/SAST tools

.github/workflows/crewai-ci.yml (1)

27-29: 🔒 Security & Privacy | 🔵 Trivial | ⚖️ Poor tradeoff

Harden action references: pin to commit SHAs and disable credential persistence.

The actions/checkout@v6 and actions/setup-python@v6 references are pinned only to mutable tags, which leaves the workflow exposed to supply-chain tampering if a tag is moved. Additionally, actions/checkout persists the GITHUB_TOKEN into the local git config by default; since this job doesn't push, set persist-credentials: false.

🔒 Proposed hardening
-      - uses: actions/checkout@v6
+      - uses: actions/checkout@<pinned-sha>  # v6
+        with:
+          persist-credentials: false

-      - uses: actions/setup-python@v6
+      - uses: actions/setup-python@<pinned-sha>  # v6
         with:
           python-version: "3.12"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/crewai-ci.yml around lines 27 - 29, The workflow uses
mutable action tags for actions/checkout and actions/setup-python, so update
those references in the CI job to immutable commit SHAs instead of version tags.
In the same checkout step, set persist-credentials to false because the job does
not need git credentials. Use the existing actions/checkout and
actions/setup-python entries in the workflow as the places to harden.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/crewai-publish.yml:
- Around line 1-16: Add a top-level permissions block to the CrewAI CD - Publish
to PyPI workflow so the default GITHUB_TOKEN scope is minimized; keep the
existing job-level permissions in build and only widen permissions in specific
jobs if needed. Update the workflow near the on/job declarations, using the
workflow name and build job as anchors when editing.

In `@crewai/examples/research_crew.py`:
- Line 20: The example in research_crew.py uses an invalid model identifier in
the LLM setup, which will break kickoff() for users running it as-is. Update the
LLM(...) initialization to use a real, supported model id such as gpt-4o, and
keep the rest of the quickstart flow unchanged. Use the llm assignment in the
example as the place to make the replacement so the snippet remains runnable
without extra edits.

In `@crewai/UPSTREAM.md`:
- Around line 47-49: The Tinyfish minimum version is inconsistent between the
upstream notes and the package extra, so align them to one floor. Update the
version reference in UPSTREAM.md to match the dependency declaration used by the
extra (or vice versa if the package spec is intended to change), and ensure the
documented symbol tinyfish stays consistent across both places so contributors
don’t copy the wrong minimum.

---

Nitpick comments:
In @.github/workflows/crewai-ci.yml:
- Around line 27-29: The workflow uses mutable action tags for actions/checkout
and actions/setup-python, so update those references in the CI job to immutable
commit SHAs instead of version tags. In the same checkout step, set
persist-credentials to false because the job does not need git credentials. Use
the existing actions/checkout and actions/setup-python entries in the workflow
as the places to harden.

In @.github/workflows/crewai-publish.yml:
- Around line 21-103: Pin every third-party GitHub Action in this workflow to an
immutable commit SHA instead of mutable tags or branches. Update the action
references in the publish workflow for actions/checkout, actions/setup-python,
astral-sh/setup-uv, actions/upload-artifact, actions/download-artifact, and
pypa/gh-action-pypi-publish so the jobs use fixed commit hashes while keeping
the existing build and publish steps unchanged.
- Around line 90-106: The publish-pypi job is using a long-lived PYPI_API_TOKEN
secret instead of PyPI Trusted Publishing and has no explicit permissions
scoping. Update the publish-pypi job in crewai-publish.yml to use OIDC by adding
the required id-token: write permission (and only the minimal permissions
needed) and remove the password-based secret from the
pypa/gh-action-pypi-publish@release/v1 step. Keep the job aligned with Trusted
Publishing setup for the existing publish workflow.

In `@crewai/pyproject.toml`:
- Around line 38-42: The dev dependency lists in pyproject.toml and
requirements-dev.txt are drifting apart, so make one source of truth for the dev
setup. Update the dev extra in pyproject.toml and the requirements-dev workflow
to stay aligned, including tinyfish, pytest, pytest-asyncio, ruff, and build as
needed, or switch requirements-dev.txt to install the dev extra directly so both
paths use the same dependency set.

In `@crewai/src/crewai_tinyfish/_serde.py`:
- Around line 38-47: The as_list helper in _serde.py is incorrectly treating
some scalar-like iterables as sequences, so strings, bytes, and dicts get split
into elements or keys instead of being wrapped as a single item. Update as_list
to special-case these types before the generic list(value) conversion, and keep
the existing behavior for None and real lists while preserving scalar-wrapping
semantics for any non-list input.

In `@crewai/src/crewai_tinyfish/tinyfish_agent_tool.py`:
- Around line 187-188: The _arun path in TinyFishAgentTool is still delegating
to the synchronous _run, which blocks the event loop. Update _arun to use
AsyncTinyFish and await client.agent.run(...) directly, keeping the sync _run
only as a fallback or separate path. Use the existing TinyFishAgentTool and
_arun/_run symbols to locate the change.

In `@crewai/src/crewai_tinyfish/tinyfish_browser_tool.py`:
- Around line 101-102: The _arun method in TinyfishBrowserTool is still calling
the synchronous _run directly, which blocks the event loop during slow session
creation. Update _arun to offload the blocking _run work to a worker thread,
matching the agent tool approach, so async crews can continue running without
freezing.

In `@crewai/UPSTREAM.md`:
- Around line 24-35: The fenced directory-tree block in UPSTREAM.md is missing a
language tag, triggering markdownlint MD040; update the fence around the tree
listing to use text so the block is explicitly marked as plain text. Keep the
change localized to the existing fenced snippet that shows the
crewai_tools/tools/* directories.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a4f827b2-66ff-488b-9739-52e160a89ca3

📥 Commits

Reviewing files that changed from the base of the PR and between ca23a08 and 1e0c659.

📒 Files selected for processing (26)
  • .github/workflows/crewai-ci.yml
  • .github/workflows/crewai-publish.yml
  • crewai/.env.example
  • crewai/.gitignore
  • crewai/LICENSE
  • crewai/Makefile
  • crewai/README.md
  • crewai/UPSTREAM.md
  • crewai/examples/quickstart.py
  • crewai/examples/research_crew.py
  • crewai/pyproject.toml
  • crewai/requirements-dev.txt
  • crewai/src/crewai_tinyfish/__init__.py
  • crewai/src/crewai_tinyfish/_client.py
  • crewai/src/crewai_tinyfish/_serde.py
  • crewai/src/crewai_tinyfish/tinyfish_agent_tool.py
  • crewai/src/crewai_tinyfish/tinyfish_browser_tool.py
  • crewai/src/crewai_tinyfish/tinyfish_fetch_tool.py
  • crewai/src/crewai_tinyfish/tinyfish_search_tool.py
  • crewai/tests/conftest.py
  • crewai/tests/test_agent_tool.py
  • crewai/tests/test_browser_tool.py
  • crewai/tests/test_fetch_tool.py
  • crewai/tests/test_search_tool.py
  • google-adk/README.md
  • google-adk/pyproject.toml

Comment on lines +1 to +16
name: CrewAI CD - Publish to PyPI

on:
push:
branches: [main]
paths:
- ".github/workflows/crewai-publish.yml"
- "crewai/**"

jobs:
build:
name: Build distribution
runs-on: ubuntu-latest
permissions:
contents: read
outputs:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Add a minimal top-level permissions block.

No permissions block is declared at the workflow level, so jobs inherit the default (often broad) GITHUB_TOKEN scopes. Restrict to least privilege at the top and let jobs widen only as needed.

🔒 Proposed fix
 on:
   push:
     branches: [main]
     paths:
       - ".github/workflows/crewai-publish.yml"
       - "crewai/**"
 
+permissions:
+  contents: read
+
 jobs:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
name: CrewAI CD - Publish to PyPI
on:
push:
branches: [main]
paths:
- ".github/workflows/crewai-publish.yml"
- "crewai/**"
jobs:
build:
name: Build distribution
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
name: CrewAI CD - Publish to PyPI
on:
push:
branches: [main]
paths:
- ".github/workflows/crewai-publish.yml"
- "crewai/**"
permissions:
contents: read
jobs:
build:
name: Build distribution
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
🧰 Tools
🪛 zizmor (1.26.1)

[warning] 1-107: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block

(excessive-permissions)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/crewai-publish.yml around lines 1 - 16, Add a top-level
permissions block to the CrewAI CD - Publish to PyPI workflow so the default
GITHUB_TOKEN scope is minimized; keep the existing job-level permissions in
build and only widen permissions in specific jobs if needed. Update the workflow
near the on/job declarations, using the workflow name and build job as anchors
when editing.

Source: Linters/SAST tools

Comment thread crewai/examples/research_crew.py
Comment thread crewai/UPSTREAM.md
- Offload sync SDK calls to a worker thread in every tool's _arun
  (asyncio.to_thread) so async crews don't block the event loop during
  slow Agent/Browser calls
- Harden _serde.as_list to wrap str/bytes/dict as single items instead of
  splitting them into characters/keys
- Align dev tooling: add ruff + build to the [dev] extra so `.[dev]` is a
  complete toolchain
- UPSTREAM.md: tag the directory-tree fence as text (markdownlint MD040)
  and align the documented tinyfish floor to >=0.2.5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant