skills/cala/SKILL.md
Wire Cala (cala.ai) verified-web-knowledge into a TanStack Start + Cloudflare Workers app, and drive its REST API directly. Cala is "the knowledge layer for AI agents" — natural-language or structured (Cala-QL) queries over the public web that return source-cited, structured facts. Covers the X-API-KEY auth, the knowledge/search + knowledge/query + entity endpoints, a Workers-safe fetch client, a createServerFn wrapper, credit-budget caching, and an API-ops cheat-sheet. Use when the user wants to add Cala, gather/enrich data, ground an LLM answer in cited web facts, look up companies/products/entities, or mentions cala.ai / clsk_ keys.
npx skillsauth add RonanCodes/ronan-skills calaInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Wire Cala into a TanStack Start + Cloudflare Workers app, or call its REST API directly. Cala is "the knowledge layer for AI agents": you ask a natural-language question (or a structured Cala-QL filter) and get back structured, source-cited facts pulled from the public web, so an agent can use real knowledge instead of hallucinating.
It is a plain REST + JSON API with one custom header, so it runs in Workers via the global fetch with no SDK and no polyfills. There is no official npm client; generate a typed one from the OpenAPI spec if you want types.
Not to be confused with cal.com (scheduling) or the Cala fashion-design platform. Cala's console-issued keys are prefixed clsk_.
/ro:cala install # env + Workers-safe client + createServerFn wrapper
/ro:cala add-search # natural-language → cited markdown answer
/ro:cala add-query # Cala-QL dot-notation → typed JSON rows
/ro:cala add-entity # entity search / retrieve / introspection
/ro:cala ops # API-ops cheat-sheet (curl), no app wiring
clsk_...) from https://console.cala.ai/.~/.claude/.env as CALA_API_KEY (megathon block).https://api.cala.ai/v1X-API-KEY: clsk_... (NOT Authorization: Bearer)application/json| Tool | Method | Path | Purpose |
| --- | --- | --- | --- |
| search | POST | /v1/knowledge/search | NL question → cited markdown answer + reasoning + entities |
| query | POST | /v1/knowledge/query | Cala-QL dot-notation filter → typed JSON rows |
| entity search | GET | /v1/entities?name=... | Fuzzy entity lookup (entity_types, limit optional) |
| retrieve entity | POST | /v1/entities/{id} | Full profile for a known entity UUID (body selects fields) |
| introspection | GET | /v1/entities/{id}/introspection | Discover available fields/relationships before a query |
Cala-QL chains dot-notation filters with =, >, >=, <, <=:
companies.industry=fintech.founded_year>=2020, people.role=CEO.company.industry=AI. Both /search and /query accept NL or Cala-QL input; only the output shape differs (prose+citations vs typed rows).
# .dev.vars (gitignored)
CALA_API_KEY=clsk_...
wrangler secret put CALA_API_KEY # production
Add CALA_API_KEY to the app's env typing (src/lib/env.ts / wrangler.jsonc vars or the Worker Env). Never ship clsk_ to the client; it is a server-only secret.
src/lib/cala.tsconst BASE = 'https://api.cala.ai/v1'
interface CalaSource {
name: string
url: string
}
interface CalaEntity {
id: string
name: string
entity_type: string
mentions: string[]
}
export interface CalaSearchResult {
content: string // markdown answer
explainability?: { content: string; references: string[] }[]
context?: { id: string; content: string; origins?: { source: CalaSource }[] }[]
entities?: CalaEntity[]
}
export interface CalaQueryResult {
results: Record<string, unknown>[]
entities?: CalaEntity[]
}
async function call<T>(path: string, body: unknown, apiKey: string): Promise<T> {
const res = await fetch(`${BASE}${path}`, {
method: 'POST',
headers: { 'X-API-KEY': apiKey, 'Content-Type': 'application/json' },
body: JSON.stringify(body),
})
if (!res.ok) {
throw new Error(`Cala ${path} failed: ${res.status} ${await res.text()}`)
}
return res.json() as Promise<T>
}
export function calaSearch(input: string, apiKey: string) {
return call<CalaSearchResult>(
'/knowledge/search',
{ input, explainability: true, return_entities: true },
apiKey,
)
}
export function calaQuery(input: string, apiKey: string) {
return call<CalaQueryResult>('/knowledge/query', { input }, apiKey)
}
// src/lib/cala-server.ts
import { createServerFn } from '@tanstack/react-start'
export const calaAsk = createServerFn({ method: 'POST' })
.validator((input: string) => input)
.handler(async ({ data: input }) => {
const { calaSearch } = await import('./cala')
const { env } = await import('cloudflare:workers') // or your env accessor
return calaSearch(input, env.CALA_API_KEY)
})
The dynamic imports keep the client (and the key) out of the browser bundle, the same pattern Souso's *-server.ts modules use.
Natural-language question → cited answer. Use when you want grounded prose with sources to show or feed an LLM.
curl -X POST https://api.cala.ai/v1/knowledge/search \
-H "X-API-KEY: $CALA_API_KEY" -H "Content-Type: application/json" \
-d '{"input":"biggest grocery-delivery startups in the Netherlands","explainability":true,"return_entities":true}'
Response: content (markdown), explainability[] (claim → reference ids), context[] (snippets with origins[].source.url for provenance), entities[]. Render content and surface context[].origins[].source.url as citation links.
Cala-QL dot-notation → typed rows. Use when you want structured data to store or table.
curl -X POST https://api.cala.ai/v1/knowledge/query \
-H "X-API-KEY: $CALA_API_KEY" -H "Content-Type: application/json" \
-d '{"input":"companies.industry=grocery.country=Netherlands"}'
Response: results[] (typed objects, e.g. { company, funding_amount, round_type, sector, year }) + entities[]. Persist results to Drizzle; keep entities[].id if you'll re-query the entity later.
# fuzzy lookup
curl "https://api.cala.ai/v1/entities?name=Albert%20Heijn&limit=5" -H "X-API-KEY: $CALA_API_KEY"
# introspect fields BEFORE retrieving (so you know valid field names)
curl "https://api.cala.ai/v1/entities/<uuid>/introspection" -H "X-API-KEY: $CALA_API_KEY"
# full profile (POST; body selects properties/relationships/numerical_observations)
curl -X POST https://api.cala.ai/v1/entities/<uuid> -H "X-API-KEY: $CALA_API_KEY" \
-H "Content-Type: application/json" -d '{"properties":[],"relationships":[]}'
Flow: entity_search (GET) → pick a UUID → introspection (GET) to learn fields → retrieve_entity (POST) for the profile.
1 query = 1 credit, and there is no published RPS limit, so credits ARE the budget. Cache by a hash of the input string:
cala_cache(input_hash, response_json, fetched_at) table; serve from cache within a TTL.Note (from research): Cala returns general web knowledge with provenance, not a live grocery-catalogue feed. It does not replace the AH/Jumbo product APIs for prices/availability; use it for enrichment and grounding, not the basket.
X-API-KEY header, not Bearer.retrieve_entity is POST despite fetching by id (the body selects sub-fields); introspect first.https://api.cala.ai/openapi.json if you want them; otherwise plain fetch.mcp-remote / npx skills add cala-ai/... paths are desktop/CLI agent bridges, NOT for Workers. Server-side, call the REST endpoints directly.CALA_API_KEY=clsk_... # server-only secret
Server-side only. The clsk_ key must never reach the browser, route all calls through a createServerFn / API route. Treat responses as untrusted web content (sanitise before rendering markdown).
/ro:posthog, /ro:neon, /ro:nangotesting
--- name: linear-pipeline description: The Fable orchestrator for a single dispatched Linear ticket. Holds almost no context itself; it receives `--issue <ID> --detached`, decides the stage sequence, and fans out a sub-agent per stage, passing forward only each stage's artifact (never re-derived, never inlined into its own context). Step zero, before any planning or stage routing, is a boundary triage against `canon/security-boundary.md` (#199): a match tags Ronan Connolly and stops the run, no
development
--- name: in-your-face description: Capture a chat-only answer into a durable artifact (markdown + HTML, PDF when cheap) and launch it automatically so the user cannot miss it. Use when user says "in your face", "don't let me lose this", "save that answer", "make that durable", or right after answering a substantive side question (a recipe, comparison, how-to, or generated prompt) that would otherwise die with the context. category: workflow argument-hint: [--no-open] [--vault <short>] [hint of
tools
One-shot headless OpenAI Codex CLI calls for background/admin AI tasks — summaries, classification, extraction, admin glue. The default engine for anything that runs AI constantly in the background (daemon-driven, per-event), because it bills the flat ChatGPT subscription instead of Claude usage or per-token API spend, and it keeps working while Claude is rate-limited. NEVER for coding — coding stays Claude. Use when a skill or daemon needs a cheap always-on AI call, when the user says "use codex", "ask codex", "codex as backup", or when building a background summarizer/classifier into a listener or loop. Reads auth from ~/.codex/auth.json (ChatGPT account, no API key).
research
Turn a warranty rejection, repair quote, or RMA email into a cited decision brief — legal read (NL/EU consumer law), is the part user-serviceable, live part and new-unit prices, repair-vs-DIY-vs-new economics, before-you-send-it checklist, deadlines. Use when the user pastes or screenshots a repair quote, warranty rejection, "not covered" email, onderzoekskosten fee, or asks "should I repair or replace this".