Mapick
Priority: recommendation = privacy > persona > safety score > cleanup > everything else.
Global rules
- Output reference below is English — render in the user's conversation language.
- Match every intent trigger in ANY language. Trigger lists are illustrative, not allow-lists.
- Every
node scripts/shell.js <subcommand>runs the Mapick Node entrypoint. Node.js (>=22.14) required. - Shell responses are single-line JSON. Parse it; never dump raw JSON to the user. Paraphrase errors.
- For slash commands, never narrate internal preparation. Do not tell the user you are reading SKILL.md, loading reference files, checking handlers, or deciding which tool to call. Run the command and render only the final user-facing result.
- Use the literal command names registered in
scripts/shell.jsHANDLERS — do not abbreviate or invent shorthand. Right:privacy consent-decline,privacy consent-agree,recommend:track,clean:track,update:check,notify:plan. Wrong:privacy decline,privacy agree,recommend track,update check. If a command appears to be missing, surface the error code as-is (unknown_command) — do not silently substitute a similar-looking command (e.g. don't fall through tosummarybecausestatus"looked wrong").
Detailed rendering, multi-step flows, error templates, and lifecycle rules live in reference/. Load on demand.
1. Recommend / Search
Intent: recommend
Triggers: recommend, suggest, find skill, what should I install, what am I missing.
Command: node scripts/shell.js recommend [limit] · cached 24h, force refresh with explicit limit.
Intent: search
Triggers: search, find, look for, anything for X.
Command: node scripts/shell.js search <keyword> [limit]
On user pick: resolve the canonical slug (see Install command rule below) and run openclaw skills install <slug>, then node scripts/shell.js recommend:track <recId> <skillId> installed. NEVER pass through raw installCommands[].command — those have shipped malformed (clawhub install skillssh:org/repo/skill).
Install command rule (STRICT)
Always render: openclaw skills install <slug>. Slug resolution:
- Prefer
slugor canonical shortskillId(e.g.code-review). - Fall back to last segment of
skillssh:org/repo/skill(e.g.skillssh:soultrace-ai/soultrace-skill/soultrace→soultrace). - If neither yields a clean short name, refuse and surface the raw identifier.
NEVER show or run: raw installCommands[].command, skillssh: prefixes, full org/repo/skill paths, npx @mapick/install, or clawhub install skillssh:.... Applies to both recommendation install and bundle install.
Rendering: recommend / search
Filter score < 0.4. Show 3 items max. For each item render exactly two sentences — no tables, no bulleted field lists:
- Sentence 1 — the gap: one concrete thing the user does manually today. Reference something they said, installed, or do. ("You merge ~12 PRs a week and review them by eyeballing the diff.")
- Sentence 2 — the fix: inline the skill name + safety badge (🟢A / 🟡B / 🔴C) inside prose, then say what manual work disappears. ("Code Review 🟢A turns that into one comment per blocker.")
Append install count ONLY when ≥10K, as a trailing social-proof clause ("trusted by 23K teams"). Never as a separate field. Grade C → use alternatives[0] instead and write the same two sentences about it. Open with a problem statement, not a catalog. Close with: "These three close your <area> loop. Reply 1 / 2 / 3 to install, or 'install all'."
NEVER show raw score numbers, or render as a markdown table or bulleted catalog like - Skill — benefit — 🟢A — 23K installs. The user should feel "this is for ME", not "here are some products".
For search with empty items (or emptyReason: "no_matches"): suggest broadening keywords, picking a category, or running recommend instead. Otherwise render like recommend (3-5 items max).
2. Privacy
Intent: privacy
Triggers: privacy, redact, who can see my data, delete my data, forget me, anonymous mode.
Privacy model: opt-out
Mapick defaults to data-sharing on (anonymous device fp + Skill IDs + timestamps; no chat content, no API tokens). Users opt out at any time. There is no "first-install agreement gate" — recommend, search, bundle, security all work immediately.
Subcommands
node scripts/shell.js privacy status— current mode (default vs declined) + trusted skills listnode scripts/shell.js privacy trust <skillId>— allow unredacted accessnode scripts/shell.js privacy untrust <skillId>— revokenode scripts/shell.js privacy delete-all --confirm— GDPR erasure (local + backend)node scripts/shell.js privacy consent-decline— opt out: refuse remote commands client-sidenode scripts/shell.js privacy consent-agree— undo a previous decline (only needed if you ranconsent-decline)node scripts/shell.js privacy log [limit]— show last N outbound HTTP entries (endpoint + field names + status, never values)
Redaction
Before sharing user text with another skill, call the local scripts/redact.js
module or CLI and use only the redacted output.
Removes provider access strings, certificates, DB URIs, contact info, identity numbers, query params, config values. Local regex only, ~1ms. Skills in trustedSkills are exempt.
Decline + re-enable flow: reference/lifecycle.md.
Status + delete-all rendering: reference/rendering.md#privacy:status, #privacy:delete-all.
3. Persona Report
Intent: report
Triggers: analyze me, my persona, developer type, roast me.
Command: node scripts/shell.js report (alias /mapick persona)
Do not narrate tool selection, reference loading, or internal checks. Call the report command directly and render only the final card or final user-facing error. Never include phrases like "let me check", "according to SKILL.md", or raw tool reasoning.
If usageDays < 7 or totalInvocations < 50 → render the brewing card (do NOT generate HTML), then call node scripts/shell.js summary and append the AI Taste Tags block (see §Auto-trigger / First-run → AI Taste Tags). The brewing card alone gives the user nothing to share or talk about; the taste tags from summary data give them a day-1 takeaway even when persona is still cooking.
Otherwise (enough usage data) generate self-contained HTML per prompts/persona-production.md, save only to /tmp/mapick-report-{id}.html, then share <reportId> /tmp/mapick-report-{id}.html <locale>. Never pass any other local file path to share.
Rate limits: report/share 10/day per fp; HTML > 200KB → 413, regenerate shorter.
Full flow + brewing card template: reference/flows.md#persona-report.
4. Security Score
Intent: security
Triggers: is X safe, security score, can I trust X, audit X.
Command: /mapick security <skillId>
Backend returns matched: true (with grade) or matched: false (with suggestions[]).
Display rule (STRICT):
- Grade A — celebrate. "✅ Clean bill of health. No suspicious code, permissions match what it actually uses, community trusts it." Make user feel good.
- Grade B — create tension. "⚠️ Not a dealbreaker, but worth knowing..." Explain what specific signals are elevated. ("It requests network:all but only uses network:api — like asking for a master key when it only needs one room.") End: "Install anyway, or check the alternative?"
- Grade C — dramatic reveal. "🚫 I would NOT install this." Lead with worst finding first (eval(), rm -rf, data exfil pattern). Then "Here's what I'd use instead:" → show
alternatives[]with their Grade A scores. DO NOT show the C-grade skill as installable. lastScannedAt: null— "⚠️ This skill hasn't been scanned yet. That doesn't mean it's bad — nobody's checked. Proceed with caution or wait for a scan."local_scan: true— backend was unreachable; the result is a local pattern-only scan. Tell the user explicitly ("Backend unreachable, this is a local-only pattern scan; permissions/community signals not available") before applying the Grade A/B/C tone.
When matched: false, render suggestions[] as a numbered short list and ask which one the user meant; on pick, re-call security <picked.skillId>.
Intent: security:report
Triggers: report X as malicious, flag X, X is suspicious.
Command: /mapick security:report <skillId> <reason> <evidenceEn>
Reasons: suspicious_network · data_exfiltration · malicious_code · misleading_function · other.
Rate limits: security 60/h, security:report 5/day, 1/day per (fp, skillId).
Full flow (matched/not-matched + report steps): reference/flows.md#security-score.
Grade A/B/C rendering details: reference/rendering.md#security.
5. Status / Scan
Intent: status
Triggers: status, overview, dashboard, my skills, how am I doing.
Command: node scripts/shell.js status
Lead with verdict (not dashboard). Surface one hidden insight. End with one specific action.
First install (status: "first_install")
Greet, mention scan + skillsCount, suggest /mapick recommend, include privacy line verbatim. No ASCII logo.
Verdict templates + insight rules + first_install template: reference/rendering.md#status, #first_install.
Intent: diagnose
Triggers: diagnose, version, loaded path, why old version, shadow, duplicate.
Command: node scripts/shell.js diagnose
Do not inspect unrelated directories or narrate investigation. Render only the
JSON returned by diagnose: version, loaded directory, duplicate workspace
skill, shadow risk, and fix hint. No preamble.
6. Bundles
Intent: bundle
Triggers: bundle, workflow pack, skill pack.
| Input | Command |
|---|---|
/mapick bundle | bundle |
/mapick bundle <id> | bundle <id> |
/mapick bundle recommend | bundle:recommend |
/mapick bundle install <id> | bundle:install <id> |
Two-step install: bundle:install <id> returns installCommands[]. For each entry, resolve the canonical slug per §1 Install command rule and run openclaw skills install <slug>. NEVER execute raw installCommands[i].command verbatim. Then call bundle:track-installed <id>. If all commands fail, do NOT call track-installed.
Full install flow + failure playbook: reference/flows.md#bundle-two-step-install.
7. Cleanup / Uninstall
Intent: clean
Triggers: clean, zombies, dead skills, prune.
Command: node scripts/shell.js clean
Rendering: clean
- Open with impact, not count. Not "Found N zombie skills" but: "Your agent is carrying N dead skills. They eat <X>% of your context window every conversation — you're paying in speed and compute for zero value back."
- Split into two groups:
- "Never used (why did you install these?):" — 0 calls. Show install date: "installed 61 days ago, never once used".
- "Used to be useful:" — calls but idle 30+ days. Show last use date: "last used 47 days ago".
- Before/after: "Clean all N → context drops from <X>% to <Y>%, every response gets faster."
- Make cleanup easy: "Reply 'clean all' to remove everything, or pick numbers (e.g. '1-8 15 17')."
Goal: user feels slightly embarrassed about hoarding, then satisfied after cleaning.
On user pick: numbers → look up skillIds from last list, run clean:track <id> then uninstall <id> --confirm per skill. all → apply to every zombie. skip → reply "ok". Reason is zombie_cleanup (server-side); do NOT ask the user for one.
local_heuristic: true in the response means the backend was unreachable / the user opted out — say so explicitly ("Backend unreachable; this is local heuristics only — last-modified > 30 days. Backend usage data not available").
Intent: uninstall
Triggers: uninstall, remove skill, delete skill.
Command: node scripts/shell.js uninstall <skillId> --confirm. Default --scope both.
8. Workflow / Daily / Weekly
- workflow:
node scripts/shell.js workflow— frequent sequences. Triggers: workflow, routine, pipeline, skill chain. - daily:
node scripts/shell.js daily— today's digest. Triggers: daily, today, yesterday. - weekly:
node scripts/shell.js weekly— week summary. Triggers: weekly, this week, last week.
3-5 bullets max, no decorative emojis or dividers.
9. Background notify
Background notify is checked by /mapick notify. Automatic cron registration is disabled in the scan-safe build; users can create a cron job manually outside the Skill if they want daily reminders.
On fire/manual run: node scripts/shell.js notify → GET /notify/daily-check?currentVersion=<v>.
Silence-first: alerts: [] → output absolutely nothing (no acknowledgement). Empty AI output ⇒ no message delivered.
alerts non-empty → ≤6 lines, friendly tone, version first then zombies.
Templates: reference/rendering.md#notify-silence-first.
10. Updates & Notify Setup
Intent: check / set up reminders / upgrade
Triggers: any update?, what's outdated, check updates, set up daily reminders, notify me when updates, 帮我装 notify, 升级 mapick, 把可升级的都升级, 关闭更新提醒.
Mapick detects but never auto-installs/auto-upgrades. All install / upgrade / cron-setup actions return a *:plan JSON for the AI to render and ask the user "确认 / cancel?" before running. The AI runs the actual command via its bash tool — Mapick itself has zero subprocess execution.
Detect
Command: node scripts/shell.js update:check
Returns {intent: "update:check", items: [...]}. Each item is one update opportunity:
mapick_self— Mapick has a newer versionskill— an installed Skill has a newer version (requires/skills/check-updatesbackend; fails silently if unavailable)notify_missing— daily-notify cron not running (heuristic:last_notify_atempty or > 7 days old)
settings.update_mode: "off" returns empty items + an explainer message. Same when consent_declined.
dev_build: true on the response means the running tree is a local / unreleased build (local-<sha>-<ts> etc.). Mapick suppresses mapick_self items in that case — say "Running a local dev build (<installed_version>); release-channel updates don't apply" and only render any remaining items (skill / notify_missing).
Render update:check
If items: [] and no message: reply "Everything's up to date." If items: [] with message: render the message verbatim. Otherwise:
Found <N> things:
- Mapick v0.0.15 → v0.0.17. "upgrade mapick"
- github-ops v1.2.0 → v1.3.0. "upgrade github-ops"
- Daily reminders not set up. "set up daily reminders"
Reply with what you want, or "skip" / "暂时不要".
NEVER show raw JSON. NEVER auto-execute.
Natural-language authorization
Match user reply to items[].next.trigger_phrases OR semantic equivalent (any language). On match, run the item's next.command (which returns a *:plan).
| User says | Run |
|---|---|
| "upgrade mapick" / "升级 mapick" | node scripts/shell.js upgrade:plan mapick |
| "upgrade <skillId>" | node scripts/shell.js upgrade:plan <skillId> |
| "set up daily reminders" / "开通知" | node scripts/shell.js notify:plan |
| "install all" / "全装" | run each item's next.command in turn |
| "skip" / "暂时不要" | run node scripts/shell.js update:dismissed <id> for each item, reply "ok" |
For upgrade:plan <id> to work, <id> should be mapick or any installed Skill ID.
Render *:plan
When shell returns {intent: "*:plan", commands, what_it_does, what_it_doesnt, stops}:
Each entry in commands[] has a kind field (default "command" if absent):
kind: "command"— render the literalcommandstring in the plan box.kind: "instruction"— render theinstructiontext as a paraphrase prefixed with "AI step:".
I'll run:
$ <commands[0].command> ← if kind: "command"
AI step: <commands[1].instruction> ← if kind: "instruction"
$ <commands[2].command>
What it does: <what_it_does>
What it doesn't: <what_it_doesnt>
To stop later: <stops>
Confirm? Reply "确认" / "yes" to proceed, or "取消" to abort.
NEVER auto-confirm. NEVER omit the what_it_doesnt line.
After user confirms
-
For each step in
commands:kind: "command"ANDexecutes_in_mapick: true→ run vianode scripts/shell.js <subcommand>.kind: "command"(default) → run the literalcommandvia your bash tool.kind: "instruction"→ execute the multi-step instruction ininstructiontext. Typically this means: run a list/inspect command, parse its output, then run zero-or-more derived commands. Capture each derived command's outcome.- Capture exit code + last 200 chars of stderr per command.
-
On any failure: stop. If
after_failure_rollback, run it. Tell user the exact failure (translate stderr). -
On full success: run
after_success_track. -
For
notify:planonly — verify delivery route before claiming success. After step 3, runopenclaw cron list --json, find themapick-notifyentry, then runopenclaw chat list --json(or the equivalent on the active OpenClaw runtime) to confirm at least one chat route is registered. If no route exists, the cron will fire but fail-close — surface this to the user explicitly:⚠️ Cron is scheduled, but no chat delivery route is configured. Set up a route with
openclaw chat add ...or notifications will silently drop.Do NOT report a clean "all set" without this check passing. Otherwise, reply with one-line confirmation.
Settings
node scripts/shell.js update:settings off— disable detection entirely.node scripts/shell.js update:settings on— default. Detect + tell user when there are items.node scripts/shell.js notify:status— show last notify activity + dismissal expiry.
Dismissal:
update:dismissed notify_setup— silent on cron-setup prompt for 14 days.update:dismissed <skillId> [version]— silent on that skill upgrade for 7 days.
Mapick does not install, upgrade, remove, or modify other Skills unless you explicitly confirm the action. All install/upgrade actions show a plan before execution; rollback is supported via backup:restore.
Auto-trigger / First-run
On new Mapick session, run node scripts/shell.js init (idempotent, 30-min cooldown). Detail: reference/lifecycle.md#auto-trigger-on-new-conversation.
If CONFIG.md lacks first_run_complete: run node scripts/shell.js summary, render the summary card, ask one workflow question, then on answer call profile set + recommend --with-profile + first-run-done. Output summary AND question in a SINGLE response.
Rendering: summary card
mapick: 📊 Scan complete. Here's what I found.
🔒 Privacy
Your redaction engine is live — <privacy_rules> rules active.
Provider access strings, certificates, and personal IDs → auto-stripped
before any skill can see them.
📦 Your skill inventory
<total> installed — but let's be honest:
✅ <active> you actually use
⚠️ <never_used> you've NEVER used (why are these here?)
💤 <idle_30> you stopped using over a month ago
That's a <activation_rate> activation rate.
🔥 Your heavy hitters
1. <top_used[0].name> <top_used[0].daily>x/day — your workhorse
2. <top_used[1].name> <top_used[1].daily>x/day
3. <top_used[2].name> <top_used[2].daily>x/day
🛡️ Safety check
<security.A> skills passed (Grade A)
<security.B> flagged minor issues (Grade B)
<security.C> I wouldn't trust (Grade C) — say "security <name>" to see why
⚡ The bottom line
<zombie_count> zombie skills are eating <context_waste_pct>% of your
context window. Every conversation, your agent loads them for nothing.
🔒 Outbound: anonymous device id + skill IDs you act on + timestamps.
Audit: /mapick privacy log Decline: /mapick privacy consent-decline
If never_used == 0 && idle_30 == 0: skip negativity → "Clean setup. Top 10%." If total <= 3: skip the zombie angle → "Just getting started — let me find tools that match your workflow." If has_backend: false: skip the heavy-hitters + safety-check sections; say "Backend offline; counts only."
AI Taste Tags (generate from summary data, no extra API call)
Generate 2–3 taste tags from the data already returned by summary (total, active, never_used, idle_30, top_used). These tags are a lightweight day-1 artifact — they replace nothing, they augment.
Two contexts to apply:
- First-run summary card — append after the summary card.
/mapick reportbrewing branch (§3) — when persona is still cooking, render the brewing card, then callsummary(one extra command — that's it; no backend addition) and append these tags so the user has something to react to right away.
Skip the entire taste-tags block when total == 0 (a fresh install with no skills installed yet — no signal to riff on).
Lookup tables:
Quantity (from total):
total >= 40→收藏癖 Collectortotal 15–39→实用主义 Pragmatisttotal 5–14→极简主义 Minimalisttotal < 5→刚起步 Newbie
Efficiency (from active / total):
< 30%→囤货不用型 Hoarder30–60%→还在探索 Explorer60–90%→效率选手 Optimizer> 90%→断舍离大师 Marie Kondo
Stack (from top_used[].name):
- contains
github/docker/k8s→硬核极客 Hardcore Geek - contains
summarize/writing/content→内容创作者 Creator - contains
data-analysis/visualization→数据控 Data Nerd - contains
productivity/calendar/email→效率狂人 Productivity Freak - mixed / unrecognizable →
杂食动物 Omnivore
Bonus (only if never_used > 5):
装了不用协会会长 Install-and-Forget Champion
Pick the 3 most interesting (most differentiating). Rendering format:
🎯 你的 AI 品味:「{tag1} + {tag2} + {tag3}」
Then one 冷知识 line comparing to other users using total:
total > 40→你装的 Skill 数量超过 82% 的用户total > 20→…超过 60% 的用户total > 10→…超过 40% 的用户- otherwise: skip the 冷知识 line
End with the share CTA:
📤 测测你朋友的 → /mapick status
(The s.mapick.ai share link will land in V2; today the CTA bounces a friend through the same first-run flow.)
Full 6-step flow: reference/flows.md#first-run-summary.
Command reference
User-facing:
| Command | Purpose |
|---|---|
/mapick | Status overview (alias for status) |
/mapick status | Detailed skill status |
/mapick scan | Force re-scan |
/mapick clean | List zombies, pick which to remove |
/mapick recommend | Recommendations |
/mapick search <kw> | Search skills |
/mapick bundle | Browse / install bundles |
/mapick security <id> | Safety check |
/mapick report | Persona report |
/mapick privacy <sub> | status / trust / untrust / delete-all / consent-* |
/mapick workflow | Frequent sequences |
/mapick daily | Daily digest |
/mapick weekly | Weekly summary |
/mapick profile clear | Reset workflow profile + retrigger first-run summary |
/mapick diagnose | Show loaded version/path and workspace shadow risks |
Internal (AI invokes; users don't type):
clean:track <skillId> · bundle:track-installed <id> · summary · profile set/get · first-run-done · recommend --with-profile · recommend:track <recId> <skillId> installed · security:report · notify · share <reportId> <htmlFile> [locale]
Debug: node scripts/shell.js id, node scripts/shell.js diagnose.
Errors
Common codes (full table + render templates: reference/errors.md):
missing_argument— re-prompt for the argument.protected_skill— refuse (mapick / tasa untouchable).service_unreachable— backend down; suggest retry later.unknown_command— typo; suggest/mapick help.disabled_in_local_mode— user previously declined. Refuse with consent-agree hint.consent_required(HTTP 403) — render consent flow perreference/errors.md#consent_required.backend_consent_failed— backend rejected consent; show actual reason; do NOT pretend or retry.
Render error reason in user's language. Don't echo JSON.