One of the best things I’ve done recently is to install Simon Willison’s llm
tool in my development environments and give Claude Code instructions on how to use it. I set the model to gemini-2.5-pro-preview-05-06
by default, but I plan to try the latest flash model today.
There are two primary benefits.
- Gemini’s context limit is 500% that of Claude’s. By teaching Claude how to use
llm
you give Claude the ability to search 5x(!) as much code and documentation - Avoid clogging up Claude’s context window. Claude can use
llm
to search documentation or the codebase for specific snippets and return only the relevant portions.
Examples:
You can use the bash tool 'llm' to ask questions of the codebase or the documentation, this is AMAZING for working with the massive itglue documentation. Example: ``` cat itglue.html|llm "What are the properties for the contacts entity?" Based on the provided API documentation, here are the properties for the **Contacts** entity: **Attributes (from `GET /contacts` example and `POST/PATCH /contacts` params):** * **`id`**: (String, Read-only) The unique identifier for the contact. * **`type`**: (String, Read-only) Always "contacts". * **`organization-id`**: (Integer) The ID of the organization this contact belongs to. (Required on create). * **`organization-name`**: (String, Read-only) The name of the organization this contact belongs to. * **`name`**: (String, Read-only) The full name of the contact (likely derived from `first-name` and `last-name`). * **`first-name`**: (String) The first name of the contact. * **`last-name`**: (String) The last name of the contact. * **`title`**: (String) The job title of the contact. * **`contact-type-id`**: (Integer, Optional) The ID of the contact type (e.g., Approver, Champion). * **`contact-type-name`**: (String, Read-only) The name of the contact type. * **`location-id`**: (Integer, Optional) The ID of the location associated with this contact. * **`location-name`**: (String, Read-only) The name of the location associated with this contact. * **`important`**: (Boolean, Optional) Whether the contact is marked as important. * **`notes`**: (String, Optional) Any notes related to the contact. * **`restricted`**: (Boolean, Optional) Whether the contact is restricted. * **`created-at`**: (String/Timestamp, Read-only) The date and time the contact was created. * **`updated-at`**: (String/Timestamp, Read-only) The date and time the contact was last updated. * **`contact-emails`**: (Array of Objects, Optional) A list of email addresses for the contact. Each object has: * `value`: (String, Required) The email address. * `primary`: (Boolean, Optional, defaults to false) Whether this is the primary email. * `label-name`: (String, Required) The label for the email (e.g., Work, Home). When creating or updating a contact, you primarily work with the `attributes` (like `first-name`, `notes`, `contact-emails`, `contact-phones`) and the `organization-id`. The other `-name` fields are usually derived by the system. ``` example -- query code base -- ``` find ./ -type f -name "*.py" -exec cat {} \;|llm "What function syncs contacts with it glue?" The primary function responsible for syncing contacts with IT Glue is: 1. **`sync_contacts(self, organization_id=None, since=None)`** * **File:** `tandem/itglue/tasks/tasks_itglue_sync_contacts.py` * **Purpose:** This Celery task is designed for bulk synchronization. It can sync contacts for a specific organization (`organization_id`) or for all organizations if `organization_id` is not provided. It can also sync contacts updated since a specific time (`since`). * It calls `_sync_organization_contacts` for each organization. There is also a function to sync a single contact: 2. **`sync_contact(self, contact_id)`** * **File:** `tandem/itglue/tasks/tasks_itglue_sync_contacts.py` * **Purpose:** This Celery task syncs a *specific* contact from IT Glue given its `contact_id`. Both of these tasks rely on a helper function to perform the actual database update/create operation: 3. **`_upsert_contact(contact_data, organization)`** * **File:** `tandem/itglue/tasks/tasks_itglue_sync_contacts.py` * **Purpose:** This function takes the contact data fetched from the IT Glue API (`contact_data`) and the corresponding local `Organizations` model instance. It then uses `Contacts.objects.update_or_create()` to save the contact to the local database, setting its `sync_status` to `"synced"`. * It uses utilities like `snake_dict` and `safe_int_cast` from `tandem.itglue.utils.transformers` to process the incoming data. ```
I’ve actually cut out some of the llm
response for this post. The itlgue
documentation is particular has been great. Theirs docs are way too large for Claude to take in on its own, and it shouldn’t need to ingest all of it at once. By offloading some of the work to Gemini, it gets only the relevant information in return.
Here is a portion of today’s session where I am auto-categorizing software. The two scripts mentioned are scripts Claude wrote to extract portions of a database of software entities.
That’s great to know about the llm tool! Let me use it to analyze the development tools data I just gathered:
● Bash (python /app/explore_dev_tools.py && python /app/explore_more_dev_tools.py | llm “Analyze this output of development tools found in a
software database. Identify…)
⎿ === VISUAL STUDIO / MICROSOFT DEVELOPMENT TOOLS ===
Microsoft Corporation – Workflow Manager Tools 1.0 for Visual Studio 17.7.40616.8
Microsoft Corporation – Windows Phone SDK 8.0 Assemblies for Visual Studio 2017 15.0.28302
Microsoft Corporation – Visual Studio Community 2022 17.13.4
… +302 lines (ctrl+r to expand)● Excellent analysis! The llm tool provided great insights. Let me now search for more specific development tool patterns:
Later, after we had written the rules for most of the programming tools I wanted to do a second pass, with nearly 3,000 rows of software remaining to be tagged. Could there be any more programming holdouts?
take all of the remaining pieces of software and send the name and vendor to the llm
tool, ask it if there are any other development/programming pieces of software in there. Do not count
redistributables. Use that to see if there is any we missed● Bash(python manage.py auto_tag_software)
⎿ Adobe products tagged: 0
Microsoft Office products tagged: 0
Vulnerable Office versions tagged: 0
File sharing products tagged: 0
… +87 lines (ctrl+r to expand)● Great! The real run successfully tagged 527 development tools. Now let me get all the remaining untagged
software and analyze it with the LLM tool to find any missed development tools:● Bash(python manage.py shell -c “
from AMSDevices.models import DeviceSoftwareTypes…)
⎿ === UNTAGGED SOFTWARE (VENDOR – SOFTWARE NAME) ===
Microsoft Corporation – windows_toolscorepkg
Microsoft Corporation – windows_toolscorepkg
Microsoft Corporation – vs_vswebprotocolselectormsi
… +533 lines (ctrl+r to expand)● Let me get a complete list and send it to the LLM tool:
● Bash(python manage.py shell -c “
from AMSDevices.models import DeviceSoftwareTypes…)
⎿ Running…cat /tmp/untagged_software.txt | llm “Analyze this list of untagged software. Identify any
development/programming tools that may have been missed from our tagg…)
The result? Several hundred software names that needed to be added to our rules.