Aartiq
Aartiq™
Download
Skills

Aartiq Skills

Aartiq has two skill mechanisms — on-demand Markdown prompt skills and a capability catalog — plus a separate agent API that exposes tools over MCP and HTTP. Every claim below cites the source file and line.

What a 'skill' is

In Aartiq, 'skills' refer to two distinct but related mechanisms. Both are documented here so the terminology is unambiguous.

Markdown prompt skills

A skill is a Markdown file in public/skills/<name>.md that holds instructions the AI loads on demand. SkillLoader is a singleton that reads built-in files (app bundle), a userData override, and falls back to inline instructions for some formats (e.g. pdf, docx) when no file is present. It strips YAML-ish frontmatter and 'Aartiq runtime note' blocks and caches the result.

Capability catalog (SkillRegistry)

A skill is also a metadata descriptor — id, label, description, icon, and a regex 'patterns' field — used for on-demand matching so the system prompt is not bloated with every skill's content on every request. matchSkills tests each skill's patterns against the user's message and always injects 'security' for credential terms and 'automation' for shell terms.

Built-in skill catalog

The catalog is defined in SkillRegistry.ts. These are the skills the model can discover and load; the Markdown bodies live in public/skills/.

Skill idWhat it is for
researchWeb research / deep reading
analysisData and content analysis
financeFinancial documents and calculations
documentsDocument generation and editing
browsingPage navigation and interaction
automationShell / scheduling / workflow automation (always injected for shell terms)
mcpModel Context Protocol tool use
apple-intelligencemacOS Apple Intelligence features
tab-intelligenceReasoning over open tabs
image-generationImage creation
schedulingBackground task scheduling
securityCredential / safe-handling guidance (always injected for credential terms)
settingsApp configuration help
xlsx / pptx / pdf / docxSpreadsheet / slide / PDF / Word generation
src/lib/SkillRegistry.ts:15-29 public/skills/ (research.md, analysis.md, finance.md, documents.md, browsing.md, automation.md, mcp.md, apple-intelligence.md, tab-intelligence.md, image-generation.md, scheduling.md, security.md, settings.md, xlsx.md, pptx.md, pdf.md, docx.md)

How skills are loaded at runtime

The chat interface parses user requests like 'load/use/activate <skill>', picks matching skills via matchSkills, and fetches their Markdown via window.electronAPI.loadSkill(skillId), which routes to SkillLoader.load. Loaded skill ids are shown to the user in a collapsible 'N skills loaded' chip (CollapsibleSkillMessage).

User trigger

AIChatSidebar parses 'load/use/activate <skill>' and resolves via matchSkills.

UI display

CollapsibleSkillMessage renders loaded skill ids as chips; returns null when none are loaded.

Agent API: MCP + HTTP tools

Separate from prompt skills, the agent API exposes executable tools over two transports from one codebase. This is what external agents / MCP clients use to drive Aartiq.

ToolRegistry (security pipeline)

Every tool call runs a fail-closed pipeline: verb gate -> tab lock -> handler -> untrusted-output injection scan. A rejected gate returns an error result, never the action. Untrusted tool output is scanned for prompt-injection and quarantined if unsafe.

Transports

HTTP: POST /api/<method> with an x-agent-id header (GET /health returns tool count). MCP: ListTools / CallTool over stdio via the MCP SDK. Both are started if enabled.

Providers

Model-agnostic config: LM Studio (http://127.0.0.1:1234/v1), Ollama (http://127.0.0.1:11434/v1), and OpenClaw (http://127.0.0.1:18789). The agent API binds to 127.0.0.1 by default (config.remote === true (defaults to false; no UI, env var, or IPC path sets it) is the only path to external reach) with defaultTrust 'limited'.

Tool surface

36 tools across 11 categories, registered via registerAllTools. Security (4): security_scan, security_audit, security_killswitch, trust_list. Agents (4): agent_register, agent_list, agent_revoke, tab_handoff. Snapshots (5): snapshot, click_ref, fill_ref, type_ref, element_action. Page (3): page_find, dom_query, get_page_text. Search (3): web_search, news_search, search_providers. Forms (5): fill_form, form_submit, autofill_match, vault_list, vault_unlock. Extensions (4): extension_list, extension_install_webstore, extension_import_chrome, extension_analyze. Theme (2): theme_resolve, ui_mode_set. Navigation (1): navigate. Tabs (3): list_tabs, new_tab, close_tab. System (2): browser_status, open_panel.

Reading one page instead of searching for it

page_find searches a page that is already open — no network request, no other tab touched. mode="tree" (the default) matches the accessible name, value, href or role of every node in the current snapshot and returns refs you can pass straight to click_ref / fill_ref; it finds nodes nested inside structural wrappers, and actionable-only filtering does not hide them. mode="text" scans rendered prose and returns context snippets. This exists because a search is real egress: it sends the query to a third party and returns somebody else's page, when the answer may already be on screen.

src/lib/agent-api/tools.ts:143-199 tests/agent-api-bridge-tools.test.js

Search: API-first, and it tells you when it had to scrape

Tavily is the recommended single key (1,000 free credits/month, no card). SerpAPI (250/month) and Brave (card + attribution) also work. Google Custom Search JSON is closed to new customers, existing keys end 2027-01-01. With no key configured, search still runs by scraping a search engine's HTML: rate-limited, slower, breaks without warning when the markup changes, and carries no publication dates. search_providers reports which provider is live, whether it scrapes, and whether it has a news index. news_search returns real publication dates and web_search does not — which is what makes "which source is most recent" answerable at all.

Filling a form is not submitting one

fill_form and form_submit are two tools rather than one tool with a submit flag, so the side effect cannot be reached by passing a parameter. fill_form carries the `input` verb and never submits; form_submit is `sideEffecting` and goes through the approval gate. Refs bind by a data-aartiq-ax stamp the collector writes onto actionable nodes (backendNodeId is first-priority identity), so a stale ref fails loudly instead of addressing a different element after the page shifts.

src/lib/agent-api/tools.ts:267-320 tests/page-scripts-forms.test.jstests/snapshot-ref-binding.test.js

Deep Research pipeline

A bounded plan -> search -> fetch -> extract -> cross-verify -> rank -> generate job, with the search provider, page fetcher and progress emitter injected so the whole pipeline tests without network access. Claims are keyed on subject|verb, so "450 million dollars" and "450 million euros" stay one claim carrying two conflicting figures rather than being matched into agreement. Corroboration needs >=2 distinct domains, re-derived from each claim's own URL, so news.reuters.com and uk.reuters.com cannot pass as two sources. A last source is named only when publication timestamps are reliable; otherwise the answer is unknown with the reason attached. Known limit: only numeric claims with a named subject are extracted, so the coverage percentage is numeric agreement specifically and disputed qualitative findings never reach the panel. Progress streams over the research-progress channel to a "Sources disagree" panel.

src/lib/research-pipeline.ts:474-678 src/lib/researchState.ts:153-201 tests/research-pipeline.test.jstests/research-progress-plumbing.test.js

Planning agents (note: not skill-driven)

The planner and agent registry are a separate agent-loop mechanism. The Planner breaks a goal into a structured Plan and re-plans every N steps; agent-registry tracks connected agents, their trust level, and tab locks. Neither imports SkillRegistry or SkillLoader — planning is orthogonal to the prompt-skill system.

Planner

planningInterval default 3, maxStepsPerPlan 10; completion scored on steps executed, answer length, and domain match (>=0.8 = high confidence).

Agent registry

Agents connect with default trust 'limited'; authorize() denies actions the trust level cannot perform; lockTab reserves a tab.

Agent types

AgentRole = 'planner' | 'navigator'; AgentState = idle | planning | executing | evaluating | finished | error.

Related