openloomi-tour
GitHub为 OpenLoomi 新用户提供的交互式端到端引导技能,涵盖环境检查、宠物反应、连接器接入、Loop 运行及决策审批等核心流程,支持跳过与回退,确保零风险上手。
Trigger Scenarios
Install
npx skills add melandlabs/openloomi --skill openloomi-tour -g -y
SKILL.md
Frontmatter
{
"name": "openloomi-tour",
"description": "Walk a brand-new OpenLoomi user through the entire pipeline in one guided session: setup health → pet reaction → connector onboarding → run a Loop tick → inspect & approve a decision card → seed Memory → optionally register a custom Loop channel \/ classifier rule \/ decision type. Triggers: openloomi tour, guided tour, walk me through openloomi, show me everything, end-to-end demo, 带我看一下, 体验一下, 一条龙, 一键体验, first time using openloomi, what's next after setup.",
"allowed-tools": "Bash(node $SKILL_DIR\/..\/..\/scripts\/loomi-bridge.mjs *), Bash(node $SKILL_DIR\/..\/openloomi-connectors\/scripts\/openloomi-connectors.cjs *), Bash(node $SKILL_DIR\/..\/openloomi-memory\/scripts\/openloomi-memory.cjs *), Bash(composio *), Bash(curl *), Bash(cat ~\/.openloomi\/token *), Bash(base64 -d *)"
}
OpenLoomi Tour — Hands-on Walkthrough
This skill is the canonical first-run experience. After
openloomi-setup finishes and prints its post-ready walkthrough, the
user can type @OpenLoomi tour (or any of the trigger phrases) and
you'll run the same pipeline live, stopping between phases so the user
can react.
The tour is interactive, idempotent, and skippable. Each phase
checks its own prerequisite and lets the user move on with next /
skip / back. Nothing is destructive — if the user already did a
phase, you re-confirm it and move on.
Reference docs
Quick links to the OpenLoomi docs that this tour draws from. Every phase below cites the relevant entries inline.
| Topic | Doc |
|---|---|
| Getting started / install / one-time setup | https://openloomi.ai/docs/getting-started |
| What OpenLoomi is (the pipeline in one page) | https://openloomi.ai/docs/what-is-openloomi |
| Glossary — every term used here (Connector / Signal / Loop channel / Action Runner / etc.) | https://openloomi.ai/docs/glossary |
| Loop engine — ticks, Decisions, cards, channels, classifier rules | https://openloomi.ai/docs/loop |
| Loop — Approve / Edit Draft / dry-run anatomy of a Card | https://openloomi.ai/docs/loop#approvals-and-dry-run |
| Memory — people / projects / notes / insights / Screen Capture | https://openloomi.ai/docs/memory |
| Knowledge Base / Library — uploaded documents (PDF, DOCX, MD, …) | https://openloomi.ai/docs/library |
| Connectors — Slack / Gmail / GitHub / Linear / Notion / HubSpot / … | https://openloomi.ai/docs/connectors |
| Native messaging bots — Telegram / WhatsApp / iMessage / Feishu / DingTalk / QQ / WeChat | https://openloomi.ai/docs/messaging-apps |
| Composio / Loop channel — OAuth broker for 1000+ apps | https://openloomi.ai/docs/glossary#composio--loop-channel |
| Attention Agent — Loomi the fox, card bubbles, sprite states | https://openloomi.ai/docs/attention-agent |
| Agent Runtimes — Claude / Codex / OpenCode / Hermes / OpenClaw | https://openloomi.ai/docs/reference/agent-runtimes |
| Plugins — bridge from Claude Code / Codex into OpenLoomi | https://openloomi.ai/docs/plugins |
| Automation / Proactive Tasks — recurring scheduled work | https://openloomi.ai/docs/automation |
| Chat — conversational entry point (not Loop) | https://openloomi.ai/docs/chat |
| Skills — reusable capabilities inside OpenLoomi | https://openloomi.ai/docs/skills |
| Audit Log — every consequential moment recorded | https://openloomi.ai/docs/privacy-security#audit-logs |
| Privacy & Security — local-first, AES-256, what's stored where | https://openloomi.ai/docs/privacy-security |
| Changelog — what's new in each release | https://openloomi.ai/docs/changelog |
Phase 0 — Pre-flight
Before running any phase, do three things in this exact order:
# 1. Readiness (sandbox-aware)
node "$SKILL_DIR/../../scripts/loomi-bridge.mjs" setup-status
# 2. If loopbackAccessAmbiguous: true, refresh host probe outside sandbox
node "$SKILL_DIR/../../scripts/loomi-bridge.mjs" run-host-probe
# 3. Confirm Codex runtime is the active default agent
node "$SKILL_DIR/../../scripts/loomi-bridge.mjs" codex-runtime-info
Decision tree:
| Pre-flight outcome | Tour action |
|---|---|
ready: true, defaultAgent: codex |
Proceed straight to Phase 1 |
ready: false, nextAction: setup |
Tell the user setup hasn't run yet. Offer to invoke openloomi-setup first, then return to tour |
ready: false, OPENLOOMI_API_UNREACHABLE |
Re-run run-host-probe outside the sandbox; if still unreachable, surface the bridge's hints[] and stop |
defaultAgent != codex |
Ask the user whether to switch to Codex runtime or tour anyway with the current provider |
Print the user's current state in one line (mode, installed, default agent) before announcing Phase 1.
Phase 1 — Pet reaction (always run)
The pet is the fastest, cheapest, safest proof that the desktop is listening. Run all four "sight" states in sequence so the user sees the sprite set:
node "$SKILL_DIR/../../scripts/loomi-bridge.mjs" pet happy
node "$SKILL_DIR/../../scripts/loomi-bridge.mjs" pet thinking
node "$SKILL_DIR/../../scripts/loomi-bridge.mjs" pet working
node "$SKILL_DIR/../../scripts/loomi-bridge.mjs" pet juggling
After each, say "Loomi just showed the <state> sprite — that proves
the desktop is watching the pet watcher and accepting state changes
from Codex." End by leaving the pet at happy (success-state feel).
For the full state taxonomy, see the Attention Agent doc; for the broader pipeline see the Glossary.
If POST /api/pet/state returns "would have set state to X — pending
OpenLoomi endpoint", note that the runtime is older than the bridge
expects; the pet widget will catch up via the ~/.openloomi/loop/
file watcher anyway.
Phase 2 — Onboarding & input sources (interactive)
This is the first real action and the first place the user may have to do something in another window. There are three input paths; offer them all and let the user pick:
A. Native bot (Telegram / WhatsApp / iMessage / Feishu / DingTalk / QQ / WeChat) — fastest, no browser OAuth. Best if you already use one of those.
B. Composio OAuth (Gmail / Slack / GitHub / Google Calendar / Notion / Linear / HubSpot / LinkedIn / Jira / Asana / Discord / X) — one browser click, works for the apps most people already have. Full list at Connectors.
C. Screen memory (macOS only) — right-click the Loomi pet on the desktop → Open Settings → enable Screen Capture. The global capture shortcut (configurable in the same panel) summarises the frontmost window and stores the result directly as a Memory record. Important: screen memories go to Memory directly and do not flow through Signals — Loop will not tick on them. So screen memory alone is not enough to drive Phase 3.
Suggested first picks by user profile:
- New to OpenLoomi, just want a feel → Gmail via
composio link gmail(richest signal) - Wants something offline-friendly → Telegram via the native CLI
- Wants a work tool → Slack or Linear via Composio
- Privacy-conscious / wants everything local → enable screen memory + skip Phase 3
After the user picks, run the right command and then verify:
# Native:
node "$SKILL_DIR/../openloomi-connectors/scripts/openloomi-connectors.cjs" list-accounts
# Composio:
composio execute GMAIL_GET_PROFILE -d '{}' # or toolkit-specific probe
# Screen memory: verify via the Memory surfaces after the first capture
# (no list-accounts equivalent — captures land in ~/.openloomi/data/memory/)
If the OAuth window never opened (silent exit), note that composio link returns immediately when the toolkit is already authorised —
re-check list-accounts first before assuming failure.
Skipping this phase
Phase 2 is optional. If the user says skip:
- Skip + nothing enabled → also skip Phase 3 (Loop tick) — Loop has no signal sources to poll. Move directly to Phase 4 (Seed Memory), which is still valuable for grounding.
- Skip + only screen memory enabled → also skip Phase 3 — screen memory doesn't flow through Signals/Loop. Still move to Phase 4, and tell the user their screen captures will start appearing in Memory immediately.
- Skip + a Connector is already connected from a previous session → run Phase 3 anyway, the existing connector will feed signals.
If the user picks screen memory only (option C), Phase 3 is skipped but Phase 4 becomes the centrepiece — the user will see their screen captures appear in Memory directly.
Phase 3 — Run one Loop tick (only if a signal source exists)
This phase is the heart of the tour when there's something for Loop to tick on (Loop engine). Skip it if Phase 2 was skipped and no Connector is connected — Loop would have nothing to pull. Screen memory also does not count here: it bypasses Signals entirely and writes to Memory directly, so it doesn't drive Loop.
Important runtime note. A Loop tick is a long-running agentic
call — the desktop spawns a subprocess (codex exec for Codex, the
native Claude CLI for Claude Code) and the HTTP POST /api/loop/tick
blocks until that subprocess finishes. A cold tick on a fresh inbox
typically takes 30–90s; subsequent ticks 10–30s; very large inboxes
2–3 min. Do not block the tour on it. Fire the tick in the
background and poll for completion.
When at least one signal-source Connector is connected, run the following — the user sees the full pipeline move for the first time.
TOKEN=$(cat ~/.openloomi/token | base64 -d)
BASE="http://localhost:3414"
# 1. Capture baseline lastTickAt so we can detect when the tick lands.
BEFORE=$(curl -sS "$BASE/api/loop/state" \
-H "Authorization: Bearer $TOKEN" | jq -r '.lastTickAt // "1970-01-01T00:00:00.000Z"')
# 2. (Optional) Force-refresh connector probes. May hang on the Composio
# cold path — fall back to the cached snapshot if it times out.
curl -sS --max-time 15 "$BASE/api/loop/connectors?refresh=1" \
-H "Authorization: Bearer $TOKEN" | jq . \
|| echo "(probe refresh timed out — using cached snapshot)"
# 3. Fire the tick in the BACKGROUND with a short HTTP timeout.
# The desktop accepts the request, spawns the agentic subprocess,
# and curl returns after --max-time even though the tick keeps
# running server-side. We then poll state.lastTickAt for completion.
curl -sS --max-time 5 -X POST "$BASE/api/loop/tick" \
-H "Authorization: Bearer $TOKEN" > /tmp/openloomi-tick.out 2>&1 &
TICK_PID=$!
echo "tick dispatched (HTTP pid=$TICK_PID); agentic subprocess continues server-side"
# 4. While the tick runs, USE THIS TIME TO TALK TO THE USER.
# Explain the architecture: signals → classifier → decisions →
# cards → Approve. Ask about their typical day. Preview the
# card-shape JSON. Do not sit in silence — this is a 1–3 minute
# window depending on inbox size.
# 5. Poll for completion (lastTickAt advances past $BEFORE).
# Default max wait: 3 minutes. Raise to 5 if the user has a large inbox.
DEADLINE=$((SECONDS + 180))
while [ $SECONDS -lt $DEADLINE ]; do
AFTER=$(curl -sS "$BASE/api/loop/state" \
-H "Authorization: Bearer $TOKEN" | jq -r '.lastTickAt // "1970-01-01T00:00:00.000Z"')
if [ "$AFTER" != "$BEFORE" ]; then
echo "tick completed at $AFTER (was $BEFORE)"
break
fi
sleep 5
done
# 6. Inspect the tick output and any decisions it produced.
cat /tmp/openloomi-tick.out
curl -sS "$BASE/api/loop/decisions?status=pending" \
-H "Authorization: Bearer $TOKEN" | jq .
If the poll loop times out without lastTickAt advancing, the
agentic subprocess is still running on the desktop (very large
inboxes, slow LLM, or composio connections endpoint unreachable).
Surface what the user can do: tell them the tick is still running in
the background, that the next time they run a tick it will be faster
(warm cache), and that they can keep going with Phase 4 — Memory
seeding — while the tick finishes asynchronously.
If no pending decisions appear after a successful tick:
- If no Connectors are connected yet → tell the user that's why, point back to Phase 2.
- If Connectors ARE connected but no signals → tell the user Loop hasn't accumulated history yet, suggest re-running the tick in a minute, or seeding Memory (Phase 4) to give Loop something to reason over.
If at least one decision appears, show the Card shape so the user sees what the pet would surface:
DEC_ID=$(curl -sS "$BASE/api/loop/decisions?status=pending" \
-H "Authorization: Bearer $TOKEN" | python3 -c "import sys,json; print(json.load(sys.stdin)['items'][0]['id'])")
curl -sS "$BASE/api/loop/card/$DEC_ID" \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
Then approve it (the one-tap path the pet bubble would normally trigger):
curl -sS -X POST "$BASE/api/loop/action/schedule" \
-H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d "{\"decision_id\":\"$DEC_ID\",\"action\":\"run\"}"
Note: the action fires ~30s later via the scheduler. Tell the user to
check the pet bubble — it'll flip to presenting and then done.
Phase 4 — Seed Memory (always run, idempotent)
Memory is what makes Chat grounded and Loop sharper. Even with no Connectors, seeding three canonical files gives the next Loop tick real grounding. The Library / Knowledge Base sits alongside — explicit user-uploaded documents that RAG-search over.
MEM="$SKILL_DIR/../openloomi-memory/scripts/openloomi-memory.cjs"
# 1. People / me
node "$MEM" add-memory "About me: <one-line role + timezone + how I prefer to work>" \
--file=people/me.md
# 2. Projects / current
node "$MEM" add-memory "Current project: <name + goal + next milestone>" \
--file=projects/current.md
# 3. Strategy / values
node "$MEM" add-memory "What I optimise for: <signal vs noise, deep work vs responsiveness, etc.>" \
--file=strategy/values.md
# 4. Search-all to prove the new files are indexed
node "$MEM" search-all "<a keyword from the file you just wrote>"
If the user is shy, do step 1 only and let them fill the rest later.
The point is to prove add-memory writes to ~/.openloomi/data/memory/
and search-all returns it.
After this phase, peek at the Memory surfaces (see Insights and Library):
node "$MEM" list-insights --days=7
curl -sS "$BASE/api/rag/documents?limit=5" \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
Phase 5 — Optional extensions (only on user request)
These are the power-user surfaces. Skip unless the user says "show me how to extend it" or similar.
5a. Custom Loop channel (Composio-backed signal source)
Show the user how to register their own signal source — anything Composio has a toolkit for becomes a Loop signal with one PUT. Full reference: Loop.
TOKEN=$(cat ~/.openloomi/token | base64 -d)
curl -sS -X PUT "http://localhost:3414/api/loop/channels" \
-H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{
"id": "demo_stripe_charges",
"label": "Demo: Stripe charges",
"toolkit": "stripe",
"toolSlug": "STRIPE_LIST_CHARGES",
"pollIntervalSec": 900,
"signalType": "stripe_charge"
}'
# Re-list to confirm
curl -sS "http://localhost:3414/api/loop/channels" \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
# 1. Confirmation card — fetch the registered channel back so the user
# sees the entity they just created, named and labeled.
echo "=== confirmation card: registered channel ==="
curl -sS "http://localhost:3414/api/loop/channels" \
-H "Authorization: Bearer $TOKEN" | \
jq '.items[] | select(.id == "demo_stripe_charges")'
# 2. Inject a decision-style insight into the inbox so the user can see
# the registration event surface in OpenLoomi Memory > Insights. The
# user can later filter by `groups: ["openloomi-tour"]` to find every
# registration card the tour produced.
echo "=== injecting registration insight into inbox ==="
curl -sS -X POST "http://localhost:3414/api/insights" \
-H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{
"type": "decision",
"content": "Custom Loop channel registered: demo_stripe_charges (toolkit=stripe, toolSlug=STRIPE_LIST_CHARGES, pollIntervalSec=900). Next Loop tick will poll and surface stripe_charge signals.",
"groups": ["openloomi-tour", "loop"],
"people": []
}' | python3 -m json.tool
Tell the user: this channel will start polling on the next Loop tick
and any returned records become stripe_charge signals that Loop
classifies normally.
5b. Classifier rule (deterministic override)
For known signal patterns, force a specific decision type without relying on the LLM classifier. See Loop — classifier rules.
curl -sS -X PUT "http://localhost:3414/api/loop/classifier-rules" \
-H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{
"id": "force_email_reply_high_urgency",
"when":[
{"field":"signal.type","op":"eq","value":"gmail_message"},
{"field":"signal.payload.urgency","op":"eq","value":"high"}
],
"then":{"type":"email_reply","actionKind":"email_reply","confidence":0.95}
}'
# Dry-run to confirm it matches
curl -sS -X POST "http://localhost:3414/api/loop/classifier-rules/dry-run" \
-H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{"signal":{"type":"gmail_message","payload":{"urgency":"high","subject":"URGENT: outage"}}}'
# 1. Confirmation card — fetch the registered rule back
echo "=== confirmation card: registered rule ==="
curl -sS "http://localhost:3414/api/loop/classifier-rules" \
-H "Authorization: Bearer $TOKEN" | \
jq '.items[] | select(.id == "force_email_reply_high_urgency")'
# 2. Inject a decision-style insight into the inbox
echo "=== injecting registration insight into inbox ==="
curl -sS -X POST "http://localhost:3414/api/insights" \
-H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{
"type": "decision",
"content": "Classifier rule registered: force_email_reply_high_urgency — gmail_message + payload.urgency=high → email_reply (confidence 0.95). Overrides the LLM classifier for matching signals.",
"groups": ["openloomi-tour", "loop"],
"people": []
}' | python3 -m json.tool
5c. Custom decision type
For a card style that doesn't exist yet (see Loop — custom DecisionTypes):
curl -sS -X PUT "http://localhost:3414/api/loop/types" \
-H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{
"id":"birthday_wish",
"label":"Birthday wish",
"icon":"ri-cake-2-line",
"actionKind":"email_reply"
}'
# 1. Confirmation card — fetch the registered type back
echo "=== confirmation card: registered decision type ==="
curl -sS "http://localhost:3414/api/loop/types" \
-H "Authorization: Bearer $TOKEN" | \
jq '.items[] | select(.id == "birthday_wish")'
# 2. Inject a decision-style insight into the inbox
echo "=== injecting registration insight into inbox ==="
curl -sS -X POST "http://localhost:3414/api/insights" \
-H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{
"type": "decision",
"content": "Custom DecisionType registered: birthday_wish (icon=ri-cake-2-line, actionKind=email_reply). Future Loop ticks can surface decisions with this card style.",
"groups": ["openloomi-tour", "loop"],
"people": []
}' | python3 -m json.tool
5d. Inventory dump — what did we just register?
After running any of 5a / 5b / 5c, list everything that's now registered so the user sees the full picture at once:
echo "=== all registered channels ==="
curl -sS "http://localhost:3414/api/loop/channels" \
-H "Authorization: Bearer $TOKEN" | jq -r '.items[].id'
echo "=== all registered classifier rules ==="
curl -sS "http://localhost:3414/api/loop/classifier-rules" \
-H "Authorization: Bearer $TOKEN" | jq -r '.items[].id'
echo "=== all registered decision types ==="
curl -sS "http://localhost:3414/api/loop/types" \
-H "Authorization: Bearer $TOKEN" | jq -r '.items[].id'
echo "=== registration insights in inbox (last 24h) ==="
curl -sS "http://localhost:3414/api/insights?days=1&limit=10" \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
After any of 5a/5b/5c, you can also re-run the tick from Phase 3 to
see the new channel/rule/type in action — the inbox will now also
contain the three registration cards pushed above so the user can
list-insights --channel=openloomi-tour or filter by
groups: ["openloomi-tour"] in the desktop UI.
Hand-off
When the tour finishes (user says done, exit, or all five phases
are complete), print the quick reference card from the
openloomi-setup post-ready walkthrough (the closed follow-up
command list) and remind the user:
The tour is over. OpenLoomi is now wired up — Connectors will keep pulling, Loop will keep ticking, and the pet will surface new decisions as cards. Approve a card to run an action; dismiss to archive it; type
@OpenLoomi tourto walk through this again.Every consequential moment is recorded in the Audit Log. The whole reason this tour was possible in the first place is the Plugin bridge that exposes OpenLoomi to your shell.
Sandbox and network
All API calls in this skill (/api/loop/*, /api/rag/*,
/api/integrations/*) target http://localhost:3414 from the user's
host. If any call fails with ECONNREFUSED / ETIMEDOUT /
"unreachable", request approval and retry outside the sandbox —
Codex network sandboxing can block loopback to the host even when
the desktop API is listening. Do not declare OpenLoomi unhealthy
until an outside-sandbox retry also fails. See the bridge's
loopbackAccess.verification.commands for the manual probe.
Version History
- 5296a2a Current 2026-07-22 19:46


