Distills CLAUDE.md guardrails and .claude/skills/ into Vibe's instruction hierarchy, preserving Claude Code punch. Includes full pql skill integration for vault queries and project planning workflow. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
24 KiB
AGENTS.md for clide — Mistral Vibe operating as Claude Code peer
This file configures Mistral Vibe to operate in this repo with the same
effectiveness as Claude Code. It distills the CLAUDE.md guardrails and all
.claude/skills/ into Vibe's instruction hierarchy, preserving "Claude punch"
in a hybrid workflow.
Identity
You are operating in clide — a Flutter/Dart IDE for Claude Code CLI. Your role: peer to Claude Code, not replacement. Maintain Claude's behavioral standards, tool discipline, and architectural rigor.
Primary directive: Never lose "Claude punch" — the combination of strict guardrails, CLI-first interaction, and parity between UI and CLI that defines effective operation in this repo.
Non-Negotiable Guardrails (from CLAUDE.md)
These are load-bearing. Violating any means the design is wrong, not the rule.
- Flutter desktop is the host. No Electron, ever.
- Single process. The Flutter app hosts everything in-process: IPC server, subsystem handlers (pane, files, editor, git, pql), extensions.
- CLI-first, not MCP. Drive via
clide <subsystem> <verb>Bash commands. - Dart is the core; pql fills the query gap. PTY spawning is native Dart FFI
(
posix_openpt+posix_spawn).pql(Go) handles vault queries. - Own the rendering stack. PTY, markdown, graph, canvas — all clide-owned.
- User/Claude parity (D-6). Every CLI subcommand has a UI affordance, and every UI action has a CLI equivalent.
- pql: wrap, don't duplicate. Clide wraps pql; never re-implements it.
- Repo-is-the-workspace. Git repo root is the workspace.
- Decision discipline. All architectural choices live in
governance/decisions/<domain>.mdasD-NNNrecords. Open questions asQ-NNNundergovernance/questions/<domain>.md. Rejected asR-NNN. - No pre-existing excuse. Solo-dev repo — if
make testis red, fix it first, then your work. Surface blockers; don't push on top of broken state.
Tool Discipline (from CLAUDE.md)
Make targets are the entry points
| Purpose | Command | Never call directly |
|---|---|---|
| Launch app | make run |
flutter run |
| Static analysis | make analyze |
flutter analyze |
| Format | make format |
dart format |
| Fast test suite | make test |
flutter test |
| Core subsystem tests | make test-core |
underlying scripts |
| Accessibility tests | make test-a11y |
underlying scripts |
| Integration tests | make test-integration |
underlying scripts |
| Pre-push gate | make push-check |
ci/* scripts |
| Setup hooks | make hooks |
cp .githooks/* .git/hooks/ |
| Clean | make clean |
rm -rf build/ |
Shell hygiene
- Working directory is repo root — never
cd /path/to/clideorgit -C - One command per invocation — no
&&/;chaining - Exception:
git commit -FHEREDOC for multi-line messages - Prefer Read/Edit/Grep tools over
cat/sed/grepfor file inspection
Git Workflow (from git-commit skill)
Commit message format: Conventional Commits 1.0
<type>(<scope>): <imperative subject> (<T-NNN>)
Optional body: explain WHY, not WHAT. Wrap at ~72 chars.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
Type: feat, fix, docs, style, refactor, perf, test, build, chore
- Use
feat/fixfor user-visible behavior - Use
chorefor bookkeeping (chore(plan)for pql ticket housekeeping) - Append
!after scope for breaking changes:feat(ipc)!: ...
Scope: Optional but preferred — subsystem: settings, vim, pty, git, plan
- Lower-case, no spaces
Subject: ≤ 72 characters INCLUDING prefix. No emojis. No "and".
Ticket ref: Trailing (T-NNN) when work has a ticket.
Body: Explain the why. The diff shows the what. Don't restate it.
- Hard cap: 60 words per bullet for CHANGELOG entries
- No multi-paragraph bullets
- No sub-headers inside bullets
- No probe numbers, latency stats, or %-coverage deltas
Logically-separated commits
- Read
git status+git difffirst — never stage blind - Group by concern, not location:
- Bookkeeping:
.gitignore, lockfiles, config - Documentation:
README.md, ADRs, design notes - Tooling/skills: reusable, non-project-specific
- Project conventions: repo's own rules
- Feature/subsystem: one cohesive change
- Layer changes: app, sidecar CLI, daemon, IPC, pql wrapper, canvas, git panel
- Bookkeeping:
- Prefer many small focused commits over one large mixed one
- Use
git add <specific paths>— NEVERgit add -A,git add .,git add -u - Verify between commits with
git status,git diff --staged,git log -1
Changelog discipline
- Every user-visible commit must touch CHANGELOG.md under
## [Unreleased] - Use Keep a Changelog 1.1.0 format with sections: Added, Changed, Deprecated, Removed, Fixed, Security
- Entries: short imperative phrases describing user-facing impact
- 60 words hard cap per bullet — verify with
make changelog-gate - What skips changelog: pure bookkeeping with no user-visible effect
Safety rules (reinforced)
- Never
--no-verify - Never
--amendunless user explicitly asks - Never force-push to
mainormaster - Always check
git statusbefore staging,git diff --stagedbefore committing - Commit message via HEREDOC:
git commit -m "$(cat <<'EOF' <message> EOF )"
What NOT to commit
.env,*.env.local.claude/settings.local.json- Build artefacts:
sidecar/bin/,sidecar/dist/,build/,app/.dart_tool/ - SQLite index files:
*.sqlite,*.sqlite-wal,*.sqlite-shm,*.db - Coverage/test output:
*.out,coverage.*,*.test .pql/changelog/— auto-staged by pre-commit hook from pql DB
pql — Vault Queries + Project Planning
pql indexes a vault into SQLite and exposes structural queries plus a
planning layer for decision records and tickets. One binary, two surfaces.
Precondition
command -v pql
If absent, tell the user to install from https://github.com/postmeridiem/pql/releases/latest. Don't install it yourself. Don't fall back to grep unless the user explicitly asks.
First touch: learn the vault
pql schema
Returns one row per frontmatter key with observed types and file counts. Run once per session before writing queries.
Surface 1: Vault queries
Subcommands
| Command | Purpose |
|---|---|
pql files [glob] |
List indexed files; optional glob filter |
pql tags [--sort count] |
Distinct tags with counts |
pql backlinks <path> |
Files linking TO a path |
pql outlinks <path> |
Links FROM a file |
pql meta <path> |
Frontmatter + tags + outlinks + headings for one file |
pql schema |
Typed frontmatter schema |
pql base <name> |
Execute an Obsidian .base file |
pql shell |
Interactive REPL (indexes once, then query per line) |
pql query "<DSL>" |
SQL-derived DSL for complex queries |
pql doctor |
Resolved vault/config/DB/index state |
DSL examples
SELECT name, fm.date WHERE fm.type = 'meeting' ORDER BY fm.date DESC LIMIT 10
SELECT path WHERE 'project' IN tags ORDER BY path
SELECT name, fm.prior_job WHERE fm.type = 'council-member' ORDER BY name
Use --file q.pql or --stdin for long queries. Don't interpolate vault
content into the command line.
Query cookbook
- Files in folder →
pql files 'sessions/*' - Top tags →
pql tags --sort count --limit 20 - What links to X? →
pql backlinks members/vaasa/persona.md - Date range →
pql query "SELECT name, fm.date WHERE fm.date BETWEEN '2024-01-01' AND '2024-12-31'" - Run a Base →
pql base council-sessions - Inspect one file →
pql meta members/vaasa/persona.md --pretty
Surface 2: Planning (decisions + tickets)
Planning state lives in <vault>/.pql/pql.db (user-authored state, not a
cache). Decision records come from the DQR tree — governance/{decisions, questions,rejected}/<domain>.md by default (D-21), configurable via
dqr_dir in .pql/config.yaml or the PQL_DQR_DIR env var (env > file >
default); a legacy flat decisions/ is auto-detected as a fallback.
Tickets are SQLite-native.
Decision subcommands
| Command | Purpose |
|---|---|
pql decisions sync [--no-style] |
Parse the DQR tree → upsert into pql.db; surfaces style warnings (filename, subdir-type, domain pairing/conflicts) unless --no-style |
pql decisions validate [--no-style] |
Dry-run parse; structural errors exit non-zero, style issues warn (suppress with --no-style) |
pql decisions claim <D|Q|R> <domain> "title" |
Print next available ID |
pql decisions list [--type X] [--domain X] [--status X] |
List decisions |
pql decisions show <id> [--with-refs] [--with-tickets] |
Show with joins |
pql decisions coverage |
Confirmed decisions without tickets |
pql decisions refs <id> |
Cross-references involving a decision |
Always pql decisions sync before querying if decisions/*.md may have changed.
Ticket subcommands
| Command | Purpose |
|---|---|
pql ticket new <type> "title" [--parent T-NNN] [--decision D-NNN] [--priority P] [--id-only] |
Create (emits T-NNN; --parent files it under an epic/story in one step; --id-only prints the bare id for tree-creation scripts) |
pql ticket list [--status S] [--team T] [--assigned A] [--label L] [--under T-NNN] [--leaf] [--unblocked] |
List with filters. --under = recursive descendants of a ticket; --leaf = no children; --unblocked = blockers all reached a terminal status |
pql ticket show <id[,id,...]> [--with-context] [--with-blockers] [--with-children] [--tree] [--depth N] |
Show one or more (comma-batch → array of show-trees). --with-children = direct children; --tree = nested descendant subtree + direct parent (cap with --depth N) |
pql ticket status <id> <new-status> [--force] |
Change status. Closing (terminal status) is blocked while the ticket has open children; --force cascades that status to all not-yet-closed descendants and lists them |
pql ticket statuslist |
List the configured status vocabulary (name, label, class, order, is_default, is_terminal) — what a UI reads to render columns |
pql ticket relabel <id|record_id> [--new-label T-NNN] [--fix-prose] |
Reassign a ticket's friendly T-NNN label (reconcile a duplicate-label collision). Identity (record_id) and the structural graph are untouched; only the label moves. --fix-prose rewrites stale T-NNN mentions in DQR markdown |
pql ticket assign <id> <agent> |
Set assignee |
pql ticket setparent <id[,id,...]> <parent-id | none> |
Set (or clear with none) a ticket's parent — the hierarchy link (epic→story→task). Positional, not a flag. This is the parent/child relationship, distinct from blockers |
pql ticket append <id> <text|--file|--stdin> |
Append to the description (blank-line separated); never round-trips existing text |
pql ticket block <id> --by <other> |
Add a blocker (a dependency: can't start until is done) — NOT a parent/child link; use setparent or new --parent for hierarchy |
pql ticket unblock <id> --from <other> |
Remove blocker |
pql ticket team <id> <team> |
Set team |
pql ticket label <id> add|rm <label> |
Manage labels |
pql ticket board [--team T] |
Kanban board view |
pql ticket refine list |
Tickets with empty descriptions, status-priority-sorted |
pql ticket refine next [--skip N] |
Head of the unrefined queue with full show-tree + remaining count |
pql ticket refine write <id> <json|--file|--stdin> |
Patch writable fields (title, description, priority, type) |
Ticket types: initiative, epic, story, task, bug.
The id you type and see (T-NNN) is a friendly label backed by a stable
underwater record_id (also in output); two clones never collide on identity,
and a duplicate label is fixed with pql ticket relabel (D-26).
Statuses are a per-vault vocabulary (ticket_statuses in .pql/config.yaml),
defaulting to: backlog, ready, in_progress, review, done, cancelled. Each status
has a class — initial, active, review, terminal — that the engine reasons about.
Run pql ticket statuslist to discover the live set. Any status can transition
to any other — pql does not enforce a state machine — except that a ticket
cannot reach a terminal status while it has open children (use --force to
cascade the close down the subtree).
Plan subcommands
| Command | Purpose |
|---|---|
pql plan status |
Dashboard: decision counts, open Qs, ticket summary, coverage gaps |
pql plan whatsnext |
Next ticket to work on (active work, then the "ready" lane) with full context bundle |
pql plan review |
Next ticket awaiting review with full context bundle |
pql plan export [--stage] |
Append changed planning rows to .pql/changelog/<table>/<YYYY-MM>.sql (the git-tracked log of record); --stage also git adds them. Normally a no-op — mutations already write through |
pql plan import [--legacy FILE] |
Replay .pql/changelog/ into pql.db (or one-time --legacy pql-plan.json migration from the pre-D-15 snapshot) |
pql plan rebuild |
Drop replicated tables and replay .pql/changelog/ from scratch. Warns on stderr (changelog.ticket_id_collision) + lists collisions in the result if one ticket id was filed twice across clones |
Versioning planning state
pql.db is gitignored — the durable, git-tracked artifact is
.pql/changelog/ (D-15/D-16). Ticket mutations write through to the
changelog synchronously, so it is always current; you never have to
remember to "export". The hooks installed by pql init do the rest:
pre-commitstages.pql/changelog/so it lands in the same commit as the change that produced it.post-mergereplays incoming changelog edits (pql plan import) and re-syncs decisions from their markdown.post-checkout/post-rewriterebuildpql.dbfrom the changelog.
On a fresh clone, pql plan import (run automatically on first open)
replays the changelog into a new pql.db. There is no pql-plan.json
snapshot — that artifact is retired; pql plan export is now only a
manual catch-up/reconcile.
A ticket mutation (create / status transition / any write) leaves
.pql/changelog/ dirty by design — the pre-commit hook stages it onto
the next git commit. This is expected, not a problem to flag. Don't
narrate "the ticket won't persist until committed" on every edit; either
fold the bookkeeping into a commit or trust the normal commit flow.
Planning cookbook
- Sync and list confirmed →
pql decisions sync && pql decisions list --type confirmed - Show with refs →
pql decisions show D-5 --with-refs --pretty - Read full body →
pql decisions read D-5 - Create ticket →
pql ticket new task "implement X" --decision D-5 - Create ticket, capture id for a script →
id=$(pql ticket new task "implement X" --id-only)— prints justT-NNN - File a ticket under an epic →
pql ticket new bug "fix X" --parent T-276(one step), or reparent an existing one →pql ticket setparent T-9 T-276(clear withnone). Parent = hierarchy; useblockonly for blocking dependencies - Batch close →
pql ticket status T-1,T-2,T-3 done - Full context →
pql ticket show T-5 --with-context --pretty - Batch show →
pql ticket show T-1,T-2,T-3 --pretty - Refine next ticket →
pql ticket refine next --pretty, thenpql ticket refine write T-N '{"description":"..."}' - Append a note →
pql ticket append T-5 "benchmarked; TTL now 5m"— blank-line separated, never overwrites; use--file note.mdor--stdinfor longer content - Subtree of an epic →
pql ticket show T-2 --tree --pretty— nestedsubtree+ direct parent inancestors; add--depth Nto cap levels - Ready leaf work under an epic →
pql ticket list --under T-2 --leaf --unblocked— leaf tickets beneath T-2 whose blockers have all reached a terminal status; the batch complement toplan whatsnext - What's next? →
pql plan whatsnext --pretty - Review queue →
pql plan review --pretty - Coverage gaps →
pql decisions coverage - Dashboard →
pql plan status --pretty - Force a changelog catch-up →
pql plan export(normally a no-op; mutations already write through to.pql/changelog/)
Output contract (both surfaces)
- stdout: JSON array (default);
--jsonlfor one object/line;--pretty;--limit N. - stderr: JSON diagnostics
{"level":"...","code":"pql.<phase>.<kind>","msg":"..."}. - Exit codes:
0— success, including zero matches (empty[]— say "no matches", not "failed")64— bad flag65— parse/compile error (pass stderr back)66— vault/config not found69— unavailable70— internal error
Anti-patterns
- Don't pipe to
jqfor simple projections — use--limit,--pretty,--jsonl. - Don't chain
pql files+pql meta— onepql querywith WHERE. - Don't parse errors — pass stderr diagnostics back directly.
- Don't forget
pql decisions syncbefore querying decisions. - Don't try to install or upgrade pql — instruct the user if missing.
When NOT to use
- Body text search →
grep/rg. - Reading file contents →
Readtool. - Code structure → tree-sitter / LSP.
- Modifying vault files →
Write/Edit. pql doesn't write to vault content.
Driving clide UI via CLI (from clide skill)
Core principle: Every UI action has a CLI equivalent (D-6). Discover the live surface; don't hard-code it.
Discover capabilities
clide capabilities
Returns JSON: every registered command, split into subsystem + verb,
with argument schema. This is authoritative — re-run it, don't trust
remembered lists.
Slots (layout areas)
sidebar— leftworkspace— center (where Claude lives)context— rightstatusbar— bottom
Observe state
# One-shot orientation
clide status
# Narrower snapshots
clide pane list
clide editor active
clide git status
Drive UI
# Open a doc in a GUI reader
clide ui open <reader> <ref>
# readers: tickets, decisions, markdown, diff
# examples:
clide ui open tickets T-123
clide ui open decisions D-456
clide ui open diff lib/src/foo.dart
clide ui open markdown docs/bar.md
# Show diff and scroll to file
clide ui diff lib/src/foo.dart
# Raise a toast
clide ui toast "message" --severity success|warning|error|info
# General: clide <subsystem> <verb> [args]
clide files list
clide editor open <path>
clide pane focus <pane-id>
Convention: If a drive verb has no live GUI, return toolError ("no live UI to drive"), not hang. Exit code conveys ok/usage/tool error. JSON on stdout.
You only see what flows through clide
Your own non-clide shell work (plain file reads, make test, git) is
outside clide's view by design (D-83). Run it through clide ... if
you want clide to observe it.
Testmode (from testmode skill)
ClideTestApp is a lightweight Flutter app for integration testing.
Catches regressions unit tests cannot: missing binaries, broken subprocess
wiring, IPC dispatch failures, extension activation order, theme parse errors.
Running
make run-testmode # all categories, 60s timeout
make run-testmode TESTMODE_CATEGORY=toolchain
make run-testmode TESTMODE_CATEGORY=ipc
make run-testmode TESTMODE_CATEGORY=extensions
make run-testmode TESTMODE_TIMEOUT=120
Results: stdout + /tmp/clide-testmode.log
Last line is machine-readable JSON: {"passed":N,"failed":M,"total":N+M,"failures":[...]}
When to run
| Changed area | Category | Why |
|---|---|---|
| Toolchain, PATH, ptyc | toolchain |
Binary resolution + exec |
| IPC envelope, dispatcher | ipc |
Round-trip + error contract |
| Extension manifest, activate | extensions |
Register + activate lifecycle |
| Theme YAML, loader | extensions |
Theme parse is in this category |
| Platform config | all |
Full rebuild validates everything |
| Any doubt | all |
~30s, cheap insurance |
Interpreting output
[testmode] exec | ... | OK— subprocess ran, exit 0 or 1[testmode] PASS | ...— assertion passed[testmode] FAIL | ...— assertion failed[testmode] exec | ... | EXCEPTION— binary not found or not executable[testmode] exec | ... | TIMEOUT— subprocess hung
If no [testmode] lines appear, the testmode gate didn't fire — verify
CLIDE_TESTMODE is set to a non-empty string.
Adding tests
All test logic in lib/test_app.dart. Pattern:
await _testExec('label', binary, ['args'], workDir);
// or
_addResult('label', boolCondition, 'detail string');
Repo Layout
lib/
main.dart # Flutter entry point
app.dart # Root layout, workspace, panels
clide.dart # Barrel: shared types
src/ # Core subsystems (IPC, PTY, git, files, pql, panes)
kernel/ # Kernel services (theme, i18n, settings, panels)
builtin/ # Built-in extensions
widgets/ # Custom widget primitives
extension/ # Extension contract and registration
lua/ # Lua runtime support
test/ # All tests (core + widgets + goldens + a11y)
assets/ # Fonts, themes, grammars, licenses, logo
linux/, macos/, web/ # Flutter platform directories
native/ # Vendored native libs (tree-sitter, dugite)
governance/ # D/Q/R records
docs/ # Design docs, wireframes
legacy/ # Python Textual clide v1.2 (frozen)
Dependency & Supply Chain Discipline
- Prefer-zero-deps. Flutter-SDK widgets first; third-party needs justification
- Exact-pinned in pubspec.yaml — no caret ranges
- Advisories reviewed before every bump
- pubspec.lock committed
- Document every bundled dependency in
assets/licenses.yaml:- name, kind, version, homepage, license, purpose
- Adding a dep: two-step commit — artefact AND
licenses.yamlentry - Native deps (dugite, libtree-sitter): vendored in
native/, pinned by SHA
Pre-push Gate
make push-check
Runs: decisions validation + core tests + fast suite + a11y + coverage + changelog
Never bypass. If it fails, fix the underlying issue.
Session Setup
One-time setup on fresh clone:
make hooks && flutter pub get
pql init # wires up pql skill + perms
Mistral-Specific Notes
What to preserve from Claude
- CLI-first interaction model
- Strict guardrails enforcement
- Logically-separated commits
- Changelog discipline (60-word cap)
- pql as single source of truth for decisions/tickets
- UI/CLI parity
- Testmode for integration validation
What to adapt
- Tool names: Claude's
Bash(...)→ mybashtool - Agent spawning: Claude's
Agent{}→ mytasktool for subagents - Permissions:
.claude/settings.jsonallow/deny → my system prompt + your instructions
When in doubt
Ask: "What would Claude do?" Then do that. The AGENTS.md is the bridge, not a replacement.
Quick Reference Card
| Task | Command |
|---|---|
| Discover clide commands | clide capabilities |
| See current UI state | clide status |
| Open ticket in UI | clide ui open tickets T-NNN |
| Toast notification | clide ui toast "msg" --severity info |
| List actionable tickets | pql ticket list --under <id> --leaf --unblocked --status backlog --pretty |
| Sync decisions | pql decisions sync |
| Run integration tests | make run-testmode |
| Fast test suite | make test |
| Full pre-push | make push-check |
| Commit with message | git commit -m "$(cat <<'EOF'\n<message>\nEOF\n)" |
| Verify changelog | make changelog-gate |
Generated for Mistral Vibe. Preserves Claude Code punch for clide repo.