Documentation Index
Fetch the complete documentation index at: https://docs.fliiq.ai/llms.txt
Use this file to discover all available pages before exploring further.
Creating a Fliiq skill is straightforward: write 3 files, drop them in a directory, and Fliiq discovers them automatically.
Every skill needs three files (plus an optional test file):
.fliiq/skills/weather/
SKILL.md # Name, description, LLM instructions
fliiq.yaml # Input/output schema, credentials
main.py # Async handler
test_main.py # Pytest tests (optional)
Step-by-Step: Build a Weather Skill
1. Create the directory
mkdir -p ~/.fliiq/skills/weather
2. Write SKILL.md
---
name: weather
description: "Get current weather for a city using OpenWeatherMap API."
---
Use this tool to get the current weather for a given city.
Returns temperature, conditions, humidity, and wind speed.
Requires OPENWEATHER_API_KEY environment variable.
The YAML frontmatter (name, description) is used by the tool registry. The markdown body is injected into the LLM prompt as instructions.
3. Write fliiq.yaml
input_schema:
type: object
properties:
city:
type: string
description: "City name (e.g., 'San Francisco', 'London')"
units:
type: string
enum: ["metric", "imperial"]
description: "Temperature units. Defaults to metric."
required:
- city
output_schema:
type: object
properties:
temperature:
type: number
description: "Current temperature"
conditions:
type: string
description: "Weather conditions (e.g., 'Clear', 'Rain')"
humidity:
type: integer
description: "Humidity percentage"
wind_speed:
type: number
description: "Wind speed"
credentials:
- OPENWEATHER_API_KEY
input_schema — JSON Schema defining what the LLM passes to the handler. Must be type: object.
output_schema — Documents the return format. Used by the LLM to understand the response.
credentials — List of environment variable names. Fliiq loads these from .env before calling the handler.
4. Write main.py
import os
import httpx
async def handler(params: dict) -> dict:
"""Get current weather for a city."""
city = params["city"]
units = params.get("units", "metric")
api_key = os.environ.get("OPENWEATHER_API_KEY")
if not api_key:
raise ValueError(
"OPENWEATHER_API_KEY not set. "
"Get one at https://openweathermap.org/api"
)
async with httpx.AsyncClient(timeout=15) as client:
resp = await client.get(
"https://api.openweathermap.org/data/2.5/weather",
params={"q": city, "appid": api_key, "units": units},
)
resp.raise_for_status()
data = resp.json()
return {
"temperature": data["main"]["temp"],
"conditions": data["weather"][0]["main"],
"humidity": data["main"]["humidity"],
"wind_speed": data["wind"]["speed"],
}
5. Add the API key
OPENWEATHER_API_KEY=your-key-here
6. Test it
fliiq run "what's the weather in San Francisco?"
Fliiq auto-discovers the skill from ~/.fliiq/skills/weather/ and makes it available to the agent.
Handler Contract
The handler function must follow these rules:
| Rule | Detail |
|---|
| Function name | Must be handler |
| Signature | async def handler(params: dict) -> dict |
| Async | Must be an async function |
| Params | Dict matching input_schema from fliiq.yaml |
| Return | Dict matching output_schema from fliiq.yaml |
| HTTP client | Use httpx (not requests) for async compatibility |
| Errors | Raise ValueError with a clear message — never fail silently |
| Credentials | Read from os.environ (loaded from .env by the skill runtime) |
Real Example: web_search
Here’s the actual web_search skill that ships with Fliiq:
import json
import os
import httpx
BRAVE_API_URL = "https://api.search.brave.com/res/v1/web/search"
async def handler(params: dict) -> dict:
"""Search the web using Brave Search API."""
query = params["query"]
count = min(params.get("count") or 5, 20)
api_key = os.environ.get("BRAVE_API_KEY")
if not api_key:
raise ValueError(
"BRAVE_API_KEY not set. Get one at https://brave.com/search/api/"
)
headers = {
"Accept": "application/json",
"Accept-Encoding": "gzip",
"X-Subscription-Token": api_key,
}
async with httpx.AsyncClient(timeout=15) as client:
resp = await client.get(
BRAVE_API_URL,
params={"q": query, "count": count},
headers=headers,
)
resp.raise_for_status()
data = resp.json()
web_results = data.get("web", {}).get("results", [])
results = [
{
"title": r.get("title", ""),
"url": r.get("url", ""),
"snippet": r.get("description", ""),
}
for r in web_results[:count]
]
return {"results": json.dumps(results, indent=2)}
Once you’ve validated a local skill, promote it so it ships with your project:
fliiq skill-promote weather
This moves it from .fliiq/skills/weather/ to skills/core/weather/. See Promoting Skills for details.