Skip to content

feat: Add find-skills bundled skill and 'skillet find' command (v0.2.0)#5

Open
warhod wants to merge 17 commits intomainfrom
kazu/0.2.0
Open

feat: Add find-skills bundled skill and 'skillet find' command (v0.2.0)#5
warhod wants to merge 17 commits intomainfrom
kazu/0.2.0

Conversation

@warhod
Copy link
Copy Markdown
Collaborator

@warhod warhod commented May 5, 2026

Features

  • New bundled skill: find-skills - helps users discover skills in the ecosystem
  • New command: skillet find <query> (primary), skillet search <query> (alias)
  • New flag: skillet init --skip-bundled to skip bundled skills installation

Infrastructure

  • Single source of truth: skills/ at repo root contains actual skill files
  • CI build copies skills to src/skillet/bundled_skills/ during publish
  • Updated get_skills_dir() to properly locate bundled skills
  • Fixed _seed_default_sources() to use absolute paths

Documentation

  • Added docs/DEVELOPMENT.md - development workflow guide
  • Updated README.md with simplified Quick Start (1-2-3 steps)
  • Updated README.md Common Commands to use find as primary
  • Updated docs/AUTHORING.md to reference find/search consistently
  • Added src/skillet/bundled_skills/README.md explaining build destination

Testing

  • All 84 tests pass
  • Updated tests to accommodate new sources.json format with path field

Breaking Changes

  • Version bump to 0.2.0 due to new find command (primary) and search (alias)
  • sources.json now includes path field for bundled skills (absolute paths)

Testing

# Initialize a new project with bundled skills (default)
skillet init

# Skip bundled skills
skillet init --skip-bundled

# Find skills
skillet find git
skillet search git  # alias for find

# List installed skills
skillet list

Checklist

  • All tests pass (84/84)
  • Version bumped to 0.2.0
  • Documentation updated
  • CHANGELOG updated (if applicable)
  • Commits are logical and atomic

Summary by CodeRabbit

  • New Features

    • Added skillet find command for discovering skills from the skills.sh registry
    • Added --skip-bundled flag to init for customized project setup
    • Bundled core skills now distributed and copied during initialization
  • Documentation

    • Streamlined quick start guide with clearer initialization workflow
    • Added comprehensive development setup guide
    • Enhanced skill authoring documentation with bundled skills guidance
    • New skill documentation for skill discovery and sprint automation
  • Chores

    • Version bumped to 0.2.0
    • Improved build workflow to package bundled skills

warhod added 5 commits May 4, 2026 17:33
Add a new bundled skill that teaches users how to discover and install
skills using 'skillet find' command. This skill serves as the
entry point for users to explore the agent skills ecosystem.
- Add 'find' as the primary command for searching skills
- Keep 'search' as an alias for backward compatibility
- Bump version to 0.2.0 in pyproject.toml
- Update uv.lock with new version
- Create src/skillet/bundled_skills/README.md to document build destination
- Update .github/workflows/publish.yml to copy skills during CI build
- Fix get_skills_dir() to find skills in package or repo root
- Fix _seed_default_sources() to use absolute paths for bundled skills
- Update tests to accommodate new sources.json format
- Add docs/DEVELOPMENT.md with development workflow
- Update README.md Quick Start with 1-2-3 steps
- Update README.md Common Commands to use 'find' as primary
- Update docs/AUTHORING.md to reference find/search consistently
- Link to development guide from README
@warhod warhod requested a review from a team as a code owner May 5, 2026 00:46
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 2026

Warning

Rate limit exceeded

@warhod has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 25 minutes and 41 seconds before requesting another review.

To continue reviewing without waiting, purchase usage credits in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2a24aba4-8f36-48e2-802d-221e19137e7f

📥 Commits

Reviewing files that changed from the base of the PR and between f41645c and 2bb73f4.

📒 Files selected for processing (2)
  • src/skillet/cli.py
  • src/skillet/utils.py
📝 Walkthrough

Walkthrough

The PR introduces the find command for querying skills.sh, implements bundled skills infrastructure with a new skip_bundled flag during initialization, refactors CLI commands into a delegated architecture, and adds comprehensive development and skill documentation.

Changes

Find Command & Bundled Skills Infrastructure

Layer / File(s) Summary
Data Shape & Configuration
pyproject.toml, src/skillet/config/settings.py, docs/AUTHORING.md
Version bumped to 0.2.0; package data includes bundled_skills/*/SKILL.md; description now required in skill frontmatter; bundled vs. local source distinction documented with kind: "bundled" and path resolution fallback logic.
Core Utilities & Skill Sourcing
src/skillet/utils.py, src/skillet/sources/apply.py
New utilities: search_skills_api for skills.sh queries, format_installs for install counts, get_skills_dir with multi-fallback discovery, _seed_default_sources to bootstrap bundled skills, _emit_native_mirrors, and _materialize_summary_lines for output. Apply layer adds bundled source handling and enhanced origin tracking for local/GitHub sources.
Command Implementation
src/skillet/commands/init.py, src/skillet/commands/find.py, src/skillet/commands/search.py, src/skillet/commands/__init__.py
New _init_command orchestrates initialization with skip_bundled flag to optionally skip seeding default sources; new _find_command queries skills.sh and renders results; _search_command refactored for local search; commands exported from __init__.py.
CLI Refactoring & Wiring
src/skillet/cli.py
Thin entry points delegate to command implementations; init_cmd extended with --skip-bundled flag and skip_bundled parameter; new find_cmd command added; search_cmd delegates to _search_command.
Build & Packaging
.github/workflows/build_and_publish.yml, src/skillet/bundled_skills/README.md
CI workflow renamed to "Build and Publish" with new step to copy skills/* into src/skillet/bundled_skills/ for packaging; README explains the build destination role and editing practices.
Tests & Integration
tests/test_cli.py, tests/test_integration.py, tests/test_skills.py, tests/test_sources.py, tests/test_emitters.py, tests/test_copier.py, tests/test_lock.py, tests/test_settings_config.py
Updated monkeypatch targets to skillet.commands.init.ensure_project_agents; import sources switched to skillet.utils; assertions updated for bundled source kind; new test test_init_skip_bundled_does_not_call_seed_default_sources; expected skill count updated to 4; formatting adjustments across test signatures and literals.
Documentation & Skills
AGENTS.md, README.md, docs/AUTHORING.md, docs/DEVELOPMENT.md, skills/find-skills/SKILL.md, .skillet/skills/find-skills/SKILL.md, .skillet/skills/sprint/SKILL.md
New AGENTS.md as AI navigation index; README updated with step-by-step quick start and GitHub-based skill examples; AUTHORING.md clarified bundled skills location and source.json resolution; new DEVELOPMENT.md guide covers setup, testing, and project structure; skill documentation for find-skills and sprint functionality.
Code Style & Formatting
src/skillet/config/wizard.py, src/skillet/installer/emitters.py, src/skillet/operations/add_sources.py, src/skillet/skills/parser.py, src/skillet/skills/search.py, src/skillet/sources/__init__.py, src/skillet/sources/github.py
Function signatures reformatted to multi-line style; string literals unified to double quotes; error messages and dict literals reformatted for readability; no behavioral changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • 508-dev/agent-skillet#3: Overlapping changes to CLI command routing, source handling and origin logic, bundled-skills packaging, installer/copier/lock mechanics, and corresponding test updates.

Poem

🐰 Hops through the PR with glee,
Find Skills now queries with ease,
Bundled, seeded, and cached—
The skillet grows with grace,
CLI delegates its weight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.97% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and specifically describes the main changes: adding a find-skills bundled skill, introducing a 'skillet find' command, and bumping version to 0.2.0. It accurately summarizes the primary work across documentation, CLI refactoring, and versioning.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kazu/0.2.0

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
tests/test_cli.py (1)

51-54: ⚡ Quick win

Strengthen the new path contract assertion.

Right now this only checks key presence. Since path is part of the new behavior, assert it is absolute and points to a real skill directory.

Proposed test tightening
     assert sources["git-os"]["kind"] == "local"
     assert sources["git-os"]["source"] == "git-os"
     assert "path" in sources["git-os"]
+    git_os_path = Path(sources["git-os"]["path"])
+    assert git_os_path.is_absolute()
+    assert (git_os_path / "SKILL.md").is_file()
🤖 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 `@tests/test_cli.py` around lines 51 - 54, The test currently only checks that
"path" exists on sources["git-os"]; update the assertion to validate that
sources["git-os"]["path"] is an absolute path and points to an existing skill
directory by asserting os.path.isabs(sources["git-os"]["path"]) and that the
path is a directory (e.g., os.path.isdir or Path(...).is_dir()), and, if
applicable, assert the directory contains an expected marker file/name for a
skill (for example a known file or subfolder) to ensure it's a real skill
directory.
src/skillet/cli.py (1)

217-237: ⚡ Quick win

Add a focused regression test for init --skip-bundled.

This introduces new init behavior, but there’s no explicit test in the provided changes validating that bundled sources are not seeded when the flag is used.

As per coding guidelines, tests/**/*.py: Use pytest for testing with configuration in pyproject.toml and test paths of tests/.

🤖 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 `@src/skillet/cli.py` around lines 217 - 237, Add a pytest regression test
under tests/ that invokes the CLI init flow with the --skip-bundled flag and
asserts that _seed_default_sources is not called (or that no bundled sources are
written to the project config). Use Click's CliRunner to invoke the command
entrypoint that calls init_cmd (or call init_cmd through its Click wrapper), set
up a temporary project_dir (tmp_path) and verify after invocation that
get_project_config_dir(project_dir) exists with project config but that
_seed_default_sources returned/created nothing (or that the
.skillet/config/sources.json file is absent or unchanged). Mock
_seed_default_sources where appropriate to assert it was not invoked; place the
test in tests/ following pytest conventions.
🤖 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 `@docs/DEVELOPMENT.md`:
- Around line 124-136: The fenced code block containing the project tree
(starting with the line "```" before "agent-skillet/") lacks a language
specifier and triggers markdownlint MD040; update the opening fence from ``` to
```text (or ```plain) so it becomes ```text to silence the warning and preserve
the plain text tree layout in the block where the snippet showing
"agent-skillet/" and its children appears.

In `@pyproject.toml`:
- Around line 49-51: The package-data entry for "skillet" uses the incorrect
relative pattern "../skills/*/SKILL.md" which resolves outside the package and
won't be packaged; update the pattern under [tool.setuptools.package-data] for
the "skillet" package to "bundled_skills/*/SKILL.md" (paths are relative to the
package dir, e.g., src/skillet) so the SKILL.md files in
src/skillet/bundled_skills/* are included in the wheel.

In `@skills/find-skills/SKILL.md`:
- Around line 148-149: Replace the relative reference "docs/AUTHORING.md" in
SKILL.md with an absolute URL so links still work when the skill is mirrored
(e.g., change the string 'docs/AUTHORING.md' to
'https://github.com/508-dev/agent-skillet/blob/main/docs/AUTHORING.md'); update
the SKILL.md line that currently contains the relative path to use the absolute
GitHub URL.
- Around line 1-3: Add the required YAML frontmatter to the top of the SKILL.md
file so the skill is discoverable: insert a YAML block delimited by --- lines
containing at minimum the keys name (set to "Find Skills" or the canonical skill
name) and description (a short sentence describing the skill's purpose); ensure
the frontmatter appears before any markdown content so the skillet CLI (skillet
find/list) can index and display this skill.

In `@src/skillet/cli.py`:
- Line 1: Remove the unused import "importlib.resources" from the top of
src/skillet/cli.py (the import statement referencing importlib.resources) so
Ruff no longer raises F401; simply delete that import line or replace it with
only the actually used imports in the module.

---

Nitpick comments:
In `@src/skillet/cli.py`:
- Around line 217-237: Add a pytest regression test under tests/ that invokes
the CLI init flow with the --skip-bundled flag and asserts that
_seed_default_sources is not called (or that no bundled sources are written to
the project config). Use Click's CliRunner to invoke the command entrypoint that
calls init_cmd (or call init_cmd through its Click wrapper), set up a temporary
project_dir (tmp_path) and verify after invocation that
get_project_config_dir(project_dir) exists with project config but that
_seed_default_sources returned/created nothing (or that the
.skillet/config/sources.json file is absent or unchanged). Mock
_seed_default_sources where appropriate to assert it was not invoked; place the
test in tests/ following pytest conventions.

In `@tests/test_cli.py`:
- Around line 51-54: The test currently only checks that "path" exists on
sources["git-os"]; update the assertion to validate that
sources["git-os"]["path"] is an absolute path and points to an existing skill
directory by asserting os.path.isabs(sources["git-os"]["path"]) and that the
path is a directory (e.g., os.path.isdir or Path(...).is_dir()), and, if
applicable, assert the directory contains an expected marker file/name for a
skill (for example a known file or subfolder) to ensure it's a real skill
directory.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 303286ab-c9e5-4ef2-af91-51c7dee6fb0b

📥 Commits

Reviewing files that changed from the base of the PR and between 3fad1d5 and 408971c.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • .github/workflows/publish.yml
  • AGENTS.md
  • README.md
  • docs/AUTHORING.md
  • docs/DEVELOPMENT.md
  • pyproject.toml
  • skills/find-skills/SKILL.md
  • src/skillet/bundled_skills/README.md
  • src/skillet/cli.py
  • tests/test_cli.py
  • tests/test_integration.py

Comment thread docs/DEVELOPMENT.md
Comment thread pyproject.toml
Comment thread skills/find-skills/SKILL.md
Comment thread skills/find-skills/SKILL.md Outdated
Comment thread src/skillet/cli.py Outdated
warhod and others added 8 commits May 4, 2026 19:41
- Add search_skills_api() function to query skills.sh API
- Add SEARCH_API_BASE with env var override (SKILLS_API_URL)
- Modify 'skillet find' to search skills.sh instead of local skills
- Keep 'skillet search' for local skill search (legacy behavior)
- Add format_installs() helper to format install counts (K/M)
- Update documentation to reflect new behavior
- API endpoint: https://skills.sh/api/search?q={query}&limit=10
- Add find-skills/SKILL.md
- Add sprint/SKILL.md
- These were created earlier but not committed
- All tests pass (84/84)
- Clean state with all 84 tests passing
- Ready for surgical refactoring following design patterns
Move shared utility functions from cli.py to utils.py:
- get_skills_dir, get_project_skills_dir
- _seed_default_sources, _emit_native_mirrors
- _github_token, _print_sync_errors, _sync_footer
- _ensure_project_skills_dir, _print_mirror_lines
- _print_tracked_sources_count, _origin_from_source_entry
- _record_applied_skills, _materialize_summary_lines
- search_skills_api, format_installs
Create commands/ package with separate files for each command:
- commands/__init__.py: package exports
- commands/find.py: find command implementation (search skills.sh API)
- commands/search.py: search command implementation (local search)
- commands/init.py: init command implementation (initialize project)
Update cli.py to only have Click command definitions (entry points):
- Import utility functions from utils.py
- Import command implementations from commands/ package
- Remove all implementation logic (now in utils.py and commands/)
- cli.py reduced from 530 lines to 233 lines
Update tests to work with refactored cli.py:
- Patch skillet.commands.init.ensure_project_agents instead of skillet.cli.ensure_project_agents
- Tests now work with utils.py and commands/ package structure
Route imports through canonical modules, trim utils re-exports, and
patch tests to use skillet.commands.init for ensure_project_agents.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🧹 Nitpick comments (1)
src/skillet/utils.py (1)

96-106: 💤 Low value

Dev-tree fallback can match an unrelated skills/ directory in installed environments.

Path(skillet.__file__).parent.parent.parent lands inside site-packages/ for a typical pip install. If any sibling distribution there happens to ship a top-level skills/ directory containing a SKILL.md subdir (unlikely, but not impossible — and entirely possible in editable installs from monorepos), it will be returned as the bundled skills dir.

Gate this branch on a dev-marker (e.g., presence of pyproject.toml or a .git directory at pkg_path.parent.parent.parent) so it only triggers in development checkouts.

🤖 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 `@src/skillet/utils.py` around lines 96 - 106, The dev-tree fallback that
computes pkg_path = Path(skillet.__file__).parent.parent.parent and returns
skills_dir can accidentally match an unrelated installed `skills/` directory;
restrict this branch to real development checkouts by verifying a dev-marker
before using pkg_path: check for presence of a project marker (e.g.,
pkg_path.joinpath("pyproject.toml").exists() or
pkg_path.joinpath(".git").is_dir()) and only iterate skills_dir if that marker
is present; keep the existing import skillet and skills_dir logic but wrap it
with the dev-marker guard so the code only returns the repo-root `skills/`
during development.
🤖 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 @.skillet/skills/sprint/SKILL.md:
- Around line 30-32: The markdown code fences in SKILL.md that show the example
branch pattern ("<type>/<ticket-id>-<short-description>"), the example commit
line ("feat(PROJ-123): Add user login"), and the git snippet currently have no
language identifiers; add explicit fence languages to silence MD040 by changing
the first fence to ```text for the pattern block, the single-line example fence
to ```text, and the multi-line git commands block to ```bash so the blocks read
e.g. ```text <type>/..., ```text feat(PROJ-123):..., and ```bash git checkout -b
... respectively.

In `@src/skillet/cli.py`:
- Around line 218-223: Update the CLI help string for the search command in
function search_cmd so it no longer claims to be an "alias for find"; change the
docstring from "Search local skills by name or description (alias for find)" to
a description that accurately reflects local behavior (e.g., "Search local
skills by name or description" or add "(local)"/ "(legacy)" if desired) so users
won't expect API-backed behavior; locate the docstring on the
`@main.command`("search") function that calls _search_command and modify it
accordingly.
- Around line 7-38: The file imports many symbols from skillet.utils that are no
longer used (get_skills_dir, _seed_default_sources, search_skills,
load_project_config, save_project_config, ensure_project_agents,
get_project_config_dir, PROJECT_CONFIG_VERSION) causing F401; remove those
unused names from the import list in src/skillet/cli.py and instead import only
the actually used symbols (e.g., _github_token, _print_sync_errors,
_materialize_summary_lines, _print_mirror_lines, _print_tracked_sources_count,
_record_applied_skills, _emit_native_mirrors, _sync_footer, apply_all_sources,
load_sources, sources_json_path, add_sources, apply_sources_and_emit,
is_managed, remove_source_entry, unrecord_skill, remove_skill,
get_skills_from_directory, run_config_wizard) and if any remaining symbols now
live in other modules (per the refactor) re-point those specific imports to
their new modules such as skillet.config.*, skillet.sources, or
skillet.installer.* rather than skillet.utils; keep the existing command imports
(_find_command, _search_command, _init_command) as-is.

In `@src/skillet/commands/__init__.py`:
- Around line 3-7: The __all__ list in this module exports names that don't
exist and causes AttributeError on wildcard imports; update __all__ to only
include the actual bindings or create explicit aliases. Either remove "find",
"search", "init" from __all__ so it contains only "_find_command",
"_search_command", "_init_command", or add aliases (e.g., assign find =
_find_command; search = _search_command; init = _init_command) and then include
those names in __all__ to expose them as intended.

In `@src/skillet/commands/find.py`:
- Around line 9-13: The CLI accepts a "directory" argument but _find_command
never uses it; update the command to remove the unused parameter by deleting the
`@click.argument`("directory", default=".") from find_cmd, change _find_command
signature to accept only term (def _find_command(term: str) -> None), and update
any call sites or tests that pass directory to call the single-argument
function; alternatively, if you prefer to keep the arg, explicitly document it
in the help text and add a TODO/reserved comment in _find_command referencing
"directory" so users know it's intentionally unused.
- Line 4: The file imports Path but never uses it causing an F401 lint failure;
remove the unused import by deleting the "Path" symbol from the import statement
in src/skillet/commands/find.py (the line "from pathlib import Path") so only
needed modules remain and CI lint (Ruff) passes.

In `@src/skillet/commands/init.py`:
- Around line 7-23: Replace the indirect imports from skillet.utils with direct
imports from the canonical module skillet.config.project for the config/project
helpers; specifically import ensure_project_agents, load_project_config,
save_project_config, get_project_config_dir, and PROJECT_CONFIG_VERSION from
skillet.config.project instead of skillet.utils, updating the import statement
in src/skillet/commands/init.py to reference those symbols from
skillet.config.project so you no longer depend on re-exports in skillet.utils.
- Around line 57-64: The save_project_config call is incorrectly outside the "if
not skip_config" block causing an unnecessary save when skip_config is true;
move the save_project_config(project_dir, proj_cfg) line so it is executed only
inside the same "if not skip_config:" block that calls
ensure_project_agents(project_dir) and proj_cfg =
load_project_config(project_dir), preserving the subsequent calls to
_emit_native_mirrors(project_dir) and _print_mirror_lines(written) behavior
unchanged.

In `@src/skillet/utils.py`:
- Around line 3-34: The file imports many symbols that are not used here
(importlib.resources, __version__, PROJECT_CONFIG_VERSION,
ensure_project_agents, get_project_config_dir, load_project_config,
save_project_config, run_config_wizard, remove_skill, is_managed,
unrecord_skill, get_skills_from_directory, search_skills, add_sources,
apply_sources_and_emit, apply_all_sources, remove_source_entry,
sources_json_path); remove these unused imports from src/skillet/utils.py and
update the consumers (e.g., cli.py and commands/init.py) to import the needed
symbols directly from their original modules (skillet.config.*,
skillet.installer.*, skillet.sources.*, skillet.operations.*) instead of from
skillet.utils; if you intentionally want to re-export them, add an explicit
__all__ and per-line “# noqa: F401” instead of leaving unused imports; re-run
Ruff and pytest to confirm the F401 violations are resolved.
- Around line 41-67: The function search_skills_api currently swallows all
errors; update it to catch and handle specific exceptions instead: replace the
broad "except Exception: pass" with handlers for httpx.RequestError and
httpx.TimeoutException (log the exception with context using the module logger
or print), ValueError/JSONDecodeError for response.json() errors, and a fallback
Exception that re-raises unexpected bugs; additionally, when
response.status_code != 200 log a warning including response.status_code and
response.text before returning [] so callers can see non-200 failures; refer to
the function name search_skills_api and the variables response, data, and
results when making these changes.
- Around line 120-139: The _seed_default_sources function currently writes
absolute paths (via entry.resolve()) into sources.json which breaks portability;
change _seed_default_sources to avoid storing the absolute "path" for bundled
skills and instead store only the bundled name in the "source" field (or set
kind="bundled") when calling upsert_source so entries can be resolved later via
get_skills_dir; additionally ensure the local resolver (_apply_local_source)
continues to fall back to get_skills_dir for entries whose kind is "bundled" or
that lack a "path" so apply_all_sources can locate bundled skills at runtime.

---

Nitpick comments:
In `@src/skillet/utils.py`:
- Around line 96-106: The dev-tree fallback that computes pkg_path =
Path(skillet.__file__).parent.parent.parent and returns skills_dir can
accidentally match an unrelated installed `skills/` directory; restrict this
branch to real development checkouts by verifying a dev-marker before using
pkg_path: check for presence of a project marker (e.g.,
pkg_path.joinpath("pyproject.toml").exists() or
pkg_path.joinpath(".git").is_dir()) and only iterate skills_dir if that marker
is present; keep the existing import skillet and skills_dir logic but wrap it
with the dev-marker guard so the code only returns the repo-root `skills/`
during development.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: a70e461c-5ff9-452e-aed3-9a08d5fd2d11

📥 Commits

Reviewing files that changed from the base of the PR and between 408971c and 0fc4c22.

📒 Files selected for processing (12)
  • .skillet/skills/find-skills/SKILL.md
  • .skillet/skills/sprint/SKILL.md
  • README.md
  • docs/AUTHORING.md
  • docs/DEVELOPMENT.md
  • src/skillet/cli.py
  • src/skillet/commands/__init__.py
  • src/skillet/commands/find.py
  • src/skillet/commands/init.py
  • src/skillet/commands/search.py
  • src/skillet/utils.py
  • tests/test_cli.py
✅ Files skipped from review due to trivial changes (3)
  • .skillet/skills/find-skills/SKILL.md
  • tests/test_cli.py
  • docs/AUTHORING.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • README.md

Comment thread .skillet/skills/sprint/SKILL.md Outdated
Comment thread src/skillet/cli.py
Comment thread src/skillet/commands/__init__.py Outdated
Comment thread src/skillet/commands/find.py Outdated
Comment thread src/skillet/commands/find.py Outdated
Comment thread src/skillet/commands/init.py Outdated
Comment thread src/skillet/commands/init.py
Comment thread src/skillet/utils.py Outdated
Comment thread src/skillet/utils.py Outdated
Comment thread src/skillet/utils.py
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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 (5)
src/skillet/commands/find.py (1)

40-40: 💤 Low value

Hardcoded example skill could go stale.

wshobson/agents/python-design-patterns is a specific third-party path; if that skill is ever renamed or removed from skills.sh, the example becomes misleading. Consider using a placeholder like <owner>/<repo>/<skill-path> (already shown on line 39) or pulling the top result's slug from skills to print a live, always-valid example.

🤖 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 `@src/skillet/commands/find.py` at line 40, Replace the hardcoded example
string in the output (the click.echo call in the function in
src/skillet/commands/find.py) with a non‑stale placeholder or a dynamic value:
either use the existing generic placeholder "<owner>/<repo>/<skill-path>" (to
match line 39) or fetch the top result from the skills list and print its slug
so the example is always valid; update the click.echo invocation to output that
placeholder or the computed top-slug instead of
"wshobson/agents/python-design-patterns".
src/skillet/utils.py (3)

30-33: 💤 Low value

Unify warning log formatting.

Lines 30 and 33 use f-strings while lines 38–43, 49, and 71–73 use %-style deferred formatting. Pick one style for consistency; deferred formatting (logger.warning("...", arg)) is preferred so the message isn't built when the warning level is suppressed.

♻️ Proposed change
-    except httpx.TimeoutException as e:
-        logger.warning(f"{SEARCH_API_BASE} search timeout: {e} q={query}")
-        return []
-    except httpx.RequestError as e:
-        logger.warning(f"{SEARCH_API_BASE} search request failed: {e} q={query}")
-        return []
+    except httpx.TimeoutException as e:
+        logger.warning("skills.sh search timeout: %s q=%r", e, query)
+        return []
+    except httpx.RequestError as e:
+        logger.warning("skills.sh search request failed: %s q=%r", e, query)
+        return []
🤖 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 `@src/skillet/utils.py` around lines 30 - 33, The two logger.warning calls in
src/skillet/utils.py that currently use f-strings (the ones logging
SEARCH_API_BASE search timeout and httpx.RequestError with variables e and
query) should be converted to use deferred %-style logging so the message
formatting is skipped when the level is suppressed; update the calls that
reference SEARCH_API_BASE, e, and query to use logger.warning with a format
string and separate arguments (matching the existing style used elsewhere in
this file) rather than f-strings.

88-130: 💤 Low value

Three-tier fallback in get_skills_dir is hard to follow; consider extracting helpers.

The function repeats the same "iter bundled, find a skill dir with SKILL.md, return it" pattern three times across packaged install, dev checkout, and CWD fallback. Inlining import skillet twice and using raise RuntimeError inside a try purely as a control-flow jump (lines 111–112) makes the intent harder to read. A small helper like _first_valid_skills_dir(candidate: Path) -> Path | None would flatten the logic and remove the inner-raise trick. No behavior change required.

🤖 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 `@src/skillet/utils.py` around lines 88 - 130, The get_skills_dir function
repeats the same "iterate directory and find a subdir containing SKILL.md" logic
and re-imports skillet twice while using a RuntimeError for control-flow;
extract a helper function (e.g. _first_valid_skills_dir(candidate: Path) -> Path
| None) that returns the candidate if it contains at least one subdirectory with
SKILL.md or None otherwise, then simplify get_skills_dir to: (1) import skillet
once and check pkg_path / "bundled_skills" using the helper, (2) compute dev
repo root without raising for control flow and check pkg_path / "skills" using
the helper, and (3) check project_dir.resolve() / "skills" using the helper;
keep behavior identical and raise RuntimeError("Cannot find bundled skills
directory") only at the end if no helper call returns a Path.

145-148: 💤 Low value

parse_skill_file parsed but only the name field is consumed.

If the goal is just to read the front-matter name, parsing the whole file is fine; but note that when the front-matter is missing or malformed, meta.get("name") falls back to entry.name already, so the or {} guard against parse_skill_file returning None is the only safety net. Consider asserting/documenting parse_skill_file's return type, or simply use entry.name if you don't need the SKILL.md name override here. (No bug — just dead-ish parsing work per skill at init.)

🤖 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 `@src/skillet/utils.py` around lines 145 - 148, The code parses the entire
SKILL.md via parse_skill_file but only uses the name field, which makes the full
parse unnecessary or fragile; either (A) ensure parse_skill_file always returns
a dict (never None) so callers can safely do meta.get("name") — update
parse_skill_file's return type/contract and remove the "or {}" sentinel in the
caller, or (B) avoid the full parse here and only read a lightweight name
override (or default to entry.name) by replacing the parse call with a simple
safe-read that returns the front-matter name if present; locate the usage around
the meta variable and the name assignment in the initialization loop and apply
one of these fixes (referencing parse_skill_file and the meta/name assignment).
tests/test_integration.py (1)

50-52: ⚡ Quick win

Avoid hard-coding the bundled skill count.

This will fail every time another bundled skill is added, even if the copy/remove behavior still works. Derive the expected value from get_skills_dir() or assert on the specific copied skills this test cares about.

Proposed change
-    assert (
-        count == 4
-    )  # Now we have 4 bundled skills (git-os, sprint, deploy-checklist, find-skills)
+    expected = sum(
+        1
+        for entry in get_skills_dir().iterdir()
+        if entry.is_dir() and (entry / "SKILL.md").is_file()
+    )
+    assert count == expected
🤖 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 `@tests/test_integration.py` around lines 50 - 52, The test currently
hard-codes the bundled skill count (assert count == 4); replace that brittle
assertion by deriving the expected number from the actual bundled skills
directory: call get_skills_dir() (or use the existing helper that returns the
bundled source dir), list its contents to compute expected_count, and assert
count == expected_count; alternatively assert presence of the specific skill
names the test relies on (e.g., "git-os", "sprint", "deploy-checklist",
"find-skills") by checking they are in the copied list rather than asserting a
fixed total. Ensure you reference the existing get_skills_dir() helper and the
variable count from the test when making this change.
🤖 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 `@docs/DEVELOPMENT.md`:
- Around line 38-40: Update the documentation to show that "uv run skillet
search <query>" is an alias of "uv run skillet find <query>" (they are
equivalent) instead of labeling search as "local skills (legacy)"; change the
wording around the examples using the commands (the occurrences around the
current block and the later block referenced) to state that both commands
perform the same Skill.sh API search and that "search" is an alias of "find".

In `@src/skillet/cli.py`:
- Around line 209-214: The search command currently implements a local search
path by accepting a directory argument and calling _search_command; change it to
behave as an alias of the find command by removing the extra "directory"
argument and delegating to the same handler used by find (call
_find_command(term) or the public find handler) so both verbs use the identical
code path—update the search_cmd signature to only take term: str and replace the
call to _search_command with a call to _find_command(term) (or the find handler
function name used in this module) to ensure identical behavior and results.

In `@src/skillet/utils.py`:
- Around line 70-74: The final except block in the skills search routine (the
one that calls logger.exception("skills.sh search unexpected error processing
response q=%r", query)) violates the function's documented "return [] on error"
contract by re-raising the exception; change the behavior to return an empty
list instead of raising so callers like _find_command receive [] on failures,
i.e., replace the re-raise with returning [] (or alternatively remove the outer
try entirely if you prefer letting unexpected bugs propagate) and ensure the
logger.exception call remains to capture the error context.

---

Nitpick comments:
In `@src/skillet/commands/find.py`:
- Line 40: Replace the hardcoded example string in the output (the click.echo
call in the function in src/skillet/commands/find.py) with a non‑stale
placeholder or a dynamic value: either use the existing generic placeholder
"<owner>/<repo>/<skill-path>" (to match line 39) or fetch the top result from
the skills list and print its slug so the example is always valid; update the
click.echo invocation to output that placeholder or the computed top-slug
instead of "wshobson/agents/python-design-patterns".

In `@src/skillet/utils.py`:
- Around line 30-33: The two logger.warning calls in src/skillet/utils.py that
currently use f-strings (the ones logging SEARCH_API_BASE search timeout and
httpx.RequestError with variables e and query) should be converted to use
deferred %-style logging so the message formatting is skipped when the level is
suppressed; update the calls that reference SEARCH_API_BASE, e, and query to use
logger.warning with a format string and separate arguments (matching the
existing style used elsewhere in this file) rather than f-strings.
- Around line 88-130: The get_skills_dir function repeats the same "iterate
directory and find a subdir containing SKILL.md" logic and re-imports skillet
twice while using a RuntimeError for control-flow; extract a helper function
(e.g. _first_valid_skills_dir(candidate: Path) -> Path | None) that returns the
candidate if it contains at least one subdirectory with SKILL.md or None
otherwise, then simplify get_skills_dir to: (1) import skillet once and check
pkg_path / "bundled_skills" using the helper, (2) compute dev repo root without
raising for control flow and check pkg_path / "skills" using the helper, and (3)
check project_dir.resolve() / "skills" using the helper; keep behavior identical
and raise RuntimeError("Cannot find bundled skills directory") only at the end
if no helper call returns a Path.
- Around line 145-148: The code parses the entire SKILL.md via parse_skill_file
but only uses the name field, which makes the full parse unnecessary or fragile;
either (A) ensure parse_skill_file always returns a dict (never None) so callers
can safely do meta.get("name") — update parse_skill_file's return type/contract
and remove the "or {}" sentinel in the caller, or (B) avoid the full parse here
and only read a lightweight name override (or default to entry.name) by
replacing the parse call with a simple safe-read that returns the front-matter
name if present; locate the usage around the meta variable and the name
assignment in the initialization loop and apply one of these fixes (referencing
parse_skill_file and the meta/name assignment).

In `@tests/test_integration.py`:
- Around line 50-52: The test currently hard-codes the bundled skill count
(assert count == 4); replace that brittle assertion by deriving the expected
number from the actual bundled skills directory: call get_skills_dir() (or use
the existing helper that returns the bundled source dir), list its contents to
compute expected_count, and assert count == expected_count; alternatively assert
presence of the specific skill names the test relies on (e.g., "git-os",
"sprint", "deploy-checklist", "find-skills") by checking they are in the copied
list rather than asserting a fixed total. Ensure you reference the existing
get_skills_dir() helper and the variable count from the test when making this
change.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3c6c3976-f05b-4274-bf32-0e71c8668acd

📥 Commits

Reviewing files that changed from the base of the PR and between 0fc4c22 and f41645c.

📒 Files selected for processing (27)
  • .github/workflows/build_and_publish.yml
  • docs/DEVELOPMENT.md
  • pyproject.toml
  • skills/find-skills/SKILL.md
  • src/skillet/cli.py
  • src/skillet/commands/__init__.py
  • src/skillet/commands/find.py
  • src/skillet/commands/init.py
  • src/skillet/commands/search.py
  • src/skillet/config/settings.py
  • src/skillet/config/wizard.py
  • src/skillet/installer/emitters.py
  • src/skillet/operations/add_sources.py
  • src/skillet/skills/parser.py
  • src/skillet/skills/search.py
  • src/skillet/sources/__init__.py
  • src/skillet/sources/apply.py
  • src/skillet/sources/github.py
  • src/skillet/utils.py
  • tests/test_cli.py
  • tests/test_copier.py
  • tests/test_emitters.py
  • tests/test_integration.py
  • tests/test_lock.py
  • tests/test_settings_config.py
  • tests/test_skills.py
  • tests/test_sources.py
✅ Files skipped from review due to trivial changes (3)
  • src/skillet/sources/init.py
  • src/skillet/sources/github.py
  • tests/test_lock.py

Comment thread docs/DEVELOPMENT.md
Comment on lines +38 to +40
uv run skillet find <query> # Search skills.sh API (primary)
uv run skillet search <query> # Search local skills (legacy)
```
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 6, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

skillet search behavior is documented inconsistently with the new alias model.

These lines describe search as “local skills (legacy)”, but this PR defines search as an alias of find. Please align wording so both commands are documented as equivalent.

Suggested doc fix
-uv run skillet find <query>  # Search skills.sh API (primary)
-uv run skillet search <query>  # Search local skills (legacy)
+uv run skillet find <query>    # Search skills.sh API (primary)
+uv run skillet search <query>  # Alias of `find`
...
-# Test search command (searches local skills)
+# Test search command (alias of `find`)
 uv run --project /path/to/agent-skillet skillet search git

Also applies to: 97-99

🤖 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 `@docs/DEVELOPMENT.md` around lines 38 - 40, Update the documentation to show
that "uv run skillet search <query>" is an alias of "uv run skillet find
<query>" (they are equivalent) instead of labeling search as "local skills
(legacy)"; change the wording around the examples using the commands (the
occurrences around the current block and the later block referenced) to state
that both commands perform the same Skill.sh API search and that "search" is an
alias of "find".

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's legacy because it searches local skills only?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@michaelmwu — You're right, and my original comment was incorrect. skillet search searches local/installed skills only, while skillet find queries the remote skills.sh API. They are distinct commands with different behaviors, not aliases, so labeling search as "Search local skills (legacy)" in the docs is accurate. Sorry for the noise!

🐰 ears drooping in sheepish realization
ʕ •ᴥ• ʔ ~ my bad!


✏️ Learnings added
Learnt from: michaelmwu
Repo: 508-dev/agent-skillet PR: 5
File: docs/DEVELOPMENT.md:38-40
Timestamp: 2026-05-06T15:35:26.248Z
Learning: In the `agent-skillet` project, `skillet find` queries the remote skills.sh API, while `skillet search` searches only locally installed skills. They are NOT aliases — they are separate commands with different implementations (`src/skillet/commands/find.py` vs `src/skillet/commands/search.py`). The `search` command is considered "legacy" because it only operates on locally installed skills.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment thread src/skillet/cli.py
Comment on lines 209 to +214
@main.command("search")
@click.argument("term")
@click.argument("directory", default=".")
def search_cmd(term: str, directory: str) -> None:
"""Search all skills by name or description."""
project_dir = Path(directory).resolve()
project_skills = get_project_skills_dir(project_dir)

if not project_skills.exists():
click.echo("No skills installed. Run 'skillet init' first.")
return

skills = get_skills_from_directory(project_skills)
results = search_skills(skills, term)

if not results:
click.echo(f"No skills found matching '{term}'")
return

click.echo(f"\nSearch results for '{term}':\n")
for skill in results:
click.echo(f" {skill['name']} (score: {skill['score']})")
if skill["description"]:
click.echo(f" {skill['description']}")
"""Search local skills by name or description."""
_search_command(term, directory)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

search still does local search instead of behaving like the documented alias.

The PR objective says skillet search <query> should alias skillet find <query>, but this command still accepts a directory argument and calls _search_command, so users get a different code path and different results depending on which verb they type.

Proposed change
 `@main.command`("search")
 `@click.argument`("term")
-@click.argument("directory", default=".")
-def search_cmd(term: str, directory: str) -> None:
-    """Search local skills by name or description."""
-    _search_command(term, directory)
+def search_cmd(term: str) -> None:
+    """Alias for `find`."""
+    _find_command(term)
📝 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
@main.command("search")
@click.argument("term")
@click.argument("directory", default=".")
def search_cmd(term: str, directory: str) -> None:
"""Search all skills by name or description."""
project_dir = Path(directory).resolve()
project_skills = get_project_skills_dir(project_dir)
if not project_skills.exists():
click.echo("No skills installed. Run 'skillet init' first.")
return
skills = get_skills_from_directory(project_skills)
results = search_skills(skills, term)
if not results:
click.echo(f"No skills found matching '{term}'")
return
click.echo(f"\nSearch results for '{term}':\n")
for skill in results:
click.echo(f" {skill['name']} (score: {skill['score']})")
if skill["description"]:
click.echo(f" {skill['description']}")
"""Search local skills by name or description."""
_search_command(term, directory)
`@main.command`("search")
`@click.argument`("term")
def search_cmd(term: str) -> None:
"""Alias for `find`."""
_find_command(term)
🤖 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 `@src/skillet/cli.py` around lines 209 - 214, The search command currently
implements a local search path by accepting a directory argument and calling
_search_command; change it to behave as an alias of the find command by removing
the extra "directory" argument and delegating to the same handler used by find
(call _find_command(term) or the public find handler) so both verbs use the
identical code path—update the search_cmd signature to only take term: str and
replace the call to _search_command with a call to _find_command(term) (or the
find handler function name used in this module) to ensure identical behavior and
results.

Comment thread src/skillet/utils.py Outdated
warhod added 2 commits May 5, 2026 21:42
Add a UTF-8 block-letter banner to the CLI entry point that prints
before each subcommand invocation. Suppress the banner when `-h` or
`--help` is requested to keep help output clean. Behavior when no
subcommand is provided remains unchanged.
Return an empty list instead of re-raising exceptions when processing
skills.sh search API responses fails unexpectedly. This prevents
crashes due to malformed or unexpected responses.
@michaelmwu
Copy link
Copy Markdown
Member

This is a Codex generated review. Please validate.

Findings:

  • [P2] README quick start documents commands that fail. Please validate by running uv run skillet find and uv run skillet add git-os /tmp/some-project: find requires a TERM, and add git-os is parsed as an invalid GitHub spec rather than a bundled skill name. This affects README.md lines 37-47, where the first-run path says skillet find and skillet add git-os. Suggested fix: make the quick start use skillet find <query> and an actual installable source such as owner/repo/path, or say that bundled skills are installed by skillet init and do not need skillet add git-os.

  • [P2] AUTHORING.md describes the old source shape for bundled skills. Please validate against _seed_default_sources and _apply_bundled_source: this PR now writes bundled entries as {"kind": "bundled", "source": "<skill-dir>"} and does not write an absolute path. docs/AUTHORING.md lines 32 and 45-47 still say bundled skills use an absolute path under kind: "local" and omit kind: "bundled", which will mislead anyone hand-editing .skillet/config/sources.json. Suggested fix: document kind: "bundled" separately, then keep kind: "local" for repo-owned paths.

Validation:

  • Please validate these assertions on the PR branch. I ran uv run pytest -q (85 passed) and uv run ruff check . (All checks passed).

Copy link
Copy Markdown
Member

@michaelmwu michaelmwu left a comment

Choose a reason for hiding this comment

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

Visual inspection is okay but see AI code review

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.

2 participants