{"id":"8a81d0ad-37a0-491a-a52b-e8f795f6345c","entityType":"agent","slug":"clawhub-linggen-linggen","name":"linggen","canonicalUrl":"https://www.xpersona.co/agent/clawhub-linggen-linggen","canonicalPath":"/agent/clawhub-linggen-linggen","generatedAt":"2026-10-10T01:13:41.539Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T13:02:39.661Z","emptyReason":null},"description":"Linggen — durable cross-host memory plus browser control, over two local MCP servers: `ling-mem` for memory, the Linggen engine for browser, X and agents. Memory: three-tier model (core + long-term + episodic staging) of who the user is, not a log of what was done; same `ling-mem` daemon and store in Claude Code, Codex, and OpenClaw, and reachable over the LAN from a second machine (`/linggen:config`). Browser: agent control of the user's own Chrome with per-site permission prompts, and logged-in X session reads.","descriptionLabel":"Source description","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 2.6K downloads reported by the source. Last updated 10/9/2026.","installCommand":"clawhub skill install s172zz5p82y8keqvtq5gmsjhm5867sm9:linggen","sourceUrl":"https://clawhub.ai/linggen/linggen","homepage":"https://clawhub.ai/linggen/skills/linggen","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/linggen/linggen","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/linggen/skills/linggen","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":68,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"linggen technical dossier on Xpersona with agent coverage, OPENCLEW support, and live trust metadata."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-09T13:02:39.661Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T13:02:39.661Z","emptyReason":null},"stars":null,"forks":null,"downloads":2600,"packageName":null,"latestVersion":"2.4.0","tractionLabel":"2.6K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T13:02:39.661Z","emptyReason":null},"lastUpdatedAt":"2026-10-09T13:02:39.661Z","lastCrawledAt":"2026-10-09T13:02:39.661Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-10T13:02:39.661Z","lastVerifiedAt":null,"highlights":[{"version":"2.4.0","createdAt":"2026-10-05T19:08:18.450Z","changelog":"linggen 2.4.0 - CLI interface and documentation updated to match the latest `ling-mem` (v0.3.9+) with new and revised flags, outputs, and operation descriptions. - Documentation adds details for new options such as `--scope-root`, `--indexed`, session initialization, and supports per-directory scope for memory captures. - Command glossary clarified and expanded across major verbs (add, list, search, session-start, etc.) for correctness and completeness. - Outdated or redundant documentation (e.g., skill-card.md) removed for clarity. - Various reference and install script docs updated to reflect the newest tool and workflow design.","fileCount":17,"zipByteSize":70339},{"version":"2.3.2","createdAt":"2026-08-17T16:20:13.007Z","changelog":"Claims now match behavior exactly: the manual install line runs the bundled installer (no process substitution over curl), the bundled bootstrap forbids remote-script fallbacks via LINGGEN_NO_REMOTE_SCRIPT=1, and SHA-256 claims name ling-mem only — engine and bun checksum verification stated as roadmap.","fileCount":17,"zipByteSize":67899},{"version":"2.3.1","createdAt":"2026-08-17T15:16:32.861Z","changelog":"Security-review remediation: both installers now ship inside the bundle (no remotely fetched script is executed; binaries stay SHA-256 verified), first-ever install now asks the user before running, and disclosure covers the full capability bundle.","fileCount":17,"zipByteSize":67600},{"version":"2.3.0","createdAt":"2026-08-14T17:23:53.325Z","changelog":"MIT-0 license (ClawHub's platform terms); bootstrap.sh installs both binaries and labels the install channel from its own on-disk path (local marker only, never phones home); install chain falls back to linggen.dev/dl mirror where GitHub is blocked; works in mainland China end to end with ling-mem 1.7.0","fileCount":15,"zipByteSize":55943},{"version":"2.2.0","createdAt":"2026-07-29T16:33:02.277Z","changelog":"Points at the right port. The 2026-07 migration moved the engine to 9527 and this skill still told agents 9898 — a port nothing serves — so anyone installing since 2026-07-10 got a dead address for the MCP front door. Also brings the dream/review verbs current: ling-mem days --undreamed (was --pending), the issues / issue-resolve review queue, and Solve + Status modes.","fileCount":14,"zipByteSize":55371},{"version":"1.5.4","createdAt":"2026-07-27T19:53:32.301Z","changelog":"Autostart hook starts the daemon on 9527, not the pre-migration 9898 — a session start no longer spawns a second daemon the plugin never talks to.","fileCount":14,"zipByteSize":55491},{"version":"1.5.3","createdAt":"2026-07-17T20:15:33.202Z","changelog":"engine port 9898→9527, ling-mem 9888→9528; /linggen:status whole-install view; condense retired","fileCount":14,"zipByteSize":55404},{"version":"2.1.0","createdAt":"2026-07-10T18:16:55.694Z","changelog":"Linggen first-class on every channel: first-use gate now installs BOTH required binaries (ling-mem + the Linggen engine) — agent announces the one-time ~100MB engine download instead of asking; browser section covers the background-install window. Plugin channels install both via the session-start hook.","fileCount":14,"zipByteSize":53538}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s172zz5p82y8keqvtq5gmsjhm5867sm9:linggen","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s172zz5p82y8keqvtq5gmsjhm5867sm9:linggen` in an isolated environment before connecting it to live workloads.","No published capability contract is available yet, so validate auth and request/response behavior manually.","Review the upstream CLAWHUB listing at https://clawhub.ai/linggen/linggen before using production credentials."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-linggen-linggen/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-linggen-linggen/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-linggen-linggen/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-linggen-linggen/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-linggen-linggen/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-linggen-linggen/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T01:13:41.533Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-linggen-linggen/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-linggen-linggen/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-linggen-linggen/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-linggen-linggen/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T13:02:39.661Z","emptyReason":null},"readme":"Skill: linggen\n\nOwner: linggen\n\nSummary: Linggen — durable cross-host memory plus browser control, over two local MCP servers: `ling-mem` for memory, the Linggen engine for browser, X and agents. Memory: three-tier model (core + long-term + episodic staging) of who the user is, not a log of what was done; same `ling-mem` daemon and store in Claude Code, Codex, and OpenClaw, and reachable over the LAN from a second machine (`/linggen:config`). Browser: agent control of the user's own Chrome with per-site permission prompts, and logged-in X session reads.\n\nTags: latest:2.4.0\n\nVersion history:\n\nv2.4.0 | 2026-10-05T19:08:18.450Z | auto\n\nlinggen 2.4.0\n\n- CLI interface and documentation updated to match the latest `ling-mem` (v0.3.9+) with new and revised flags, outputs, and operation descriptions.\n- Documentation adds details for new options such as `--scope-root`, `--indexed`, session initialization, and supports per-directory scope for memory captures.\n- Command glossary clarified and expanded across major verbs (add, list, search, session-start, etc.) for correctness and completeness.\n- Outdated or redundant documentation (e.g., skill-card.md) removed for clarity.\n- Various reference and install script docs updated to reflect the newest tool and workflow design.\n\nv2.3.2 | 2026-08-17T16:20:13.007Z | user\n\nClaims now match behavior exactly: the manual install line runs the bundled installer (no process substitution over curl), the bundled bootstrap forbids remote-script fallbacks via LINGGEN_NO_REMOTE_SCRIPT=1, and SHA-256 claims name ling-mem only — engine and bun checksum verification stated as roadmap.\n\nv2.3.1 | 2026-08-17T15:16:32.861Z | user\n\nSecurity-review remediation: both installers now ship inside the bundle (no remotely fetched script is executed; binaries stay SHA-256 verified), first-ever install now asks the user before running, and disclosure covers the full capability bundle.\n\nv2.3.0 | 2026-08-14T17:23:53.325Z | user\n\nMIT-0 license (ClawHub's platform terms); bootstrap.sh installs both binaries and labels the install channel from its own on-disk path (local marker only, never phones home); install chain falls back to linggen.dev/dl mirror where GitHub is blocked; works in mainland China end to end with ling-mem 1.7.0\n\nv2.2.0 | 2026-07-29T16:33:02.277Z | user\n\nPoints at the right port. The 2026-07 migration moved the engine to 9527 and this skill still told agents 9898 — a port nothing serves — so anyone installing since 2026-07-10 got a dead address for the MCP front door. Also brings the dream/review verbs current: ling-mem days --undreamed (was --pending), the issues / issue-resolve review queue, and Solve + Status modes.\n\nv1.5.4 | 2026-07-27T19:53:32.301Z | user\n\nAutostart hook starts the daemon on 9527, not the pre-migration 9898 — a session start no longer spawns a second daemon the plugin never talks to.\n\nv1.5.3 | 2026-07-17T20:15:33.202Z | user\n\nengine port 9898→9527, ling-mem 9888→9528; /linggen:status whole-install view; condense retired\n\nv2.1.0 | 2026-07-10T18:16:55.694Z | user\n\nLinggen first-class on every channel: first-use gate now installs BOTH required binaries (ling-mem + the Linggen engine) — agent announces the one-time ~100MB engine download instead of asking; browser section covers the background-install window. Plugin channels install both via the session-start hook.\n\nv2.0.1 | 2026-07-10T18:00:27.526Z | user\n\nEngine install hint: when the linggen MCP server (:9898) is unreachable, memory falls back to the ling-mem CLI; browser/x need the Linggen engine — suggest curl -fsSL https://linggen.dev/install.sh | bash (consent-first, never auto-run).\n\nv2.0.0 | 2026-07-10T15:23:56.808Z | user\n\nRenamed from ling-mem. One MCP front door to the local Linggen daemon: memory_* tools (MCP-first, ling-mem CLI fallback), browser control in the user's own Chrome with per-site permission prompts, logged-in X session reads. Memory protocol unchanged — same daemon, same store.\n\nv1.3.0 | 2026-07-08T18:55:44.617Z | user\n\nUser-voice merge guard enforced at the daemon (user_directed flag); MCP episodic search-scope fix; memory_update/memory_harvest_day tools; recall footer fires on any hit\n\nv1.2.0 | 2026-07-07T18:24:01.372Z | user\n\n1.2.0: condense pass — chains scan (cited/marker/subject) for stale-cluster collapse; see-it-solve-it merge law (derived notes merge freely via replace_ids, user-voice asks first); state+lessons routing; source_session stamping for idempotent scan; tier honored on add (core writes fixed).\n\nv1.1.0 | 2026-06-09T19:16:15.113Z | user\n\n1.1.0 — hybrid search (cosine + IDF keyword boost) so keyword queries rank correctly; batched bulk import (add_batch); atomic upsert data-safety fix; console relevance fixes.\n\nv1.0.1 | 2026-06-03T18:03:07.627Z | user\n\n1.0.1: lowered the episodic capture gate (project-scoped milestones/decisions/learnings now captured, not dropped); added an always-on per-turn capture nudge in recall.sh; fixed broken ~1 install pins to ^1.\n\nv1.0.0 | 2026-06-02T18:00:36.124Z | user\n\n1.0.0 — first stable release (in lockstep with Linggen 1.0). Contract frozen: store schema v1 + CLI/HTTP/MCP API. Per-turn episodic capture + dream consolidation tuned for it; range-pin (~1) model live.\n\nv0.8.0 | 2026-06-02T16:41:25.611Z | user\n\n0.8.0: per-turn episodic capture (the agent stages signal each turn; dream promotes/evicts); store schema-version guard + export/import; one canonical ~/.local/bin binary across hosts; store-wide recall_min_score config.\n\nv0.7.4 | 2026-06-01T18:58:16.174Z | user\n\n0.7.4 (skill doc patch over binary 0.7.2): add a first-use ling-mem binary bootstrap gate to SKILL.md (command -v ling-mem || install.sh) so skill-only installs (ClawHub/skills.sh/manual) self-install the binary on first use. No-op when already present. Binary unchanged.\n\nv0.7.3 | 2026-06-01T18:13:21.688Z | user\n\n0.7.3 (skill doc-only patch over the 0.7.2 binary): fix ClawHub/OpenClaw install slug to ling-mem (was the wrong 'shared-memory'). Binary unchanged — install.sh still pulls ling-mem v0.7.2.\n\nv0.7.2 | 2026-06-01T17:58:10.832Z | user\n\nling-mem 0.7.2 — ordering fix: list/browse and the dashboard \"latest\" now sort/age by activity_timestamp (updated_at ?? created_at); list sorts the full filtered set before paging. Skill: portable timeout fallback + scan meta-header doc.\n\nv0.5.1 | 2026-05-12T14:51:42.587Z | auto\n\nling-mem 0.5.1\n\n- Updates runtime permission model for tighter control and greater clarity over read/write access to directories.\n- Skill manifest now separates permissions for ~/.linggen (write) and ~/.claude/projects (read only); warning text reflects new read-only status.\n- Adds clarity to SKILL.md on the memory agent's role in both tool and dashboard operation.\n- No changes to user-facing memory interface or feature set.\n\nv0.4.4 | 2026-05-07T17:27:18.291Z | user\n\ninstall.sh now wires the OpenClaw integration: detects ~/.openclaw/workspace/USER.md and appends a marker-bracketed 'use ling-mem as your second memory system' directive so the OpenClaw agent proactively reaches for ling-mem alongside its native MEMORY.md. Marker-bracketed and idempotent (LING_MEM_SKIP_OPENCLAW=1 to disable). Pins binary to v0.4.4 (dashboard add-fact UI sync fix).\n\nv0.4.3 | 2026-05-07T16:24:03.823Z | user\n\nTrack ling-mem CLI rename: 'self-update'/'update' renamed to 'upgrade'/'edit' (old names still work as aliases). Adds 'ling-mem init' for idempotent core-memory seeding (recovery + ClawHub-only installs). 'ling-mem status' now embeds the cached upgrade probe. Pins LING_MEM_VERSION default to v0.4.2.\n\nv0.4.2 | 2026-05-07T14:10:11.093Z | user\n\nPin README curl|bash URL to ling-mem-v0.4.2 tag (was main); scope chat-bridge.js postMessage targetOrigin from '*' to the iframe's actual origin and validate inbound e.origin; soften privacy copy to acknowledge that retrieved facts enter the agent's prompt context. Addresses ClawScan v0.4.1 findings 2 (supply chain), 3 (trust copy), 6 (postMessage).\n\nv0.4.1 | 2026-05-07T13:44:00.175Z | user\n\nPin install defaults (v0.4.1, ling-mem-v0.4.1 tag), verify release SHA-256 on binary download, exclude Linggen-only mission scheduler artifacts from publish via .clawhubignore. Addresses ClawScan findings 1, 2, 5; findings 3, 4 are inherent design choices.\n\nv0.4.0 | 2026-05-06T19:43:17.455Z | user\n\nv0.4.0: explicit Apache-2.0 SPDX in frontmatter; hardened install.sh (two-step download replaces curl|tar pipe + intent comment block); clawdis metadata declares ling-mem CLI dependency (requires.bins); intent/privacy comments on session-reading scripts; Install section in SKILL.md body; linggen.dev surfaced. Skill version aligned with binary version (0.4.0). CC + Linggen host integration unchanged.\n\nv0.3.2 | 2026-05-06T19:13:29.478Z | user\n\nMemory layer for AI assistants — Claude Code (with auto-recall hook), OpenClaw, Linggen, or any agent that shells out. Apache 2.0 skill code; MIT daemon binary fetched at install time. Apple Silicon and Linux x86_64/aarch64 prebuilt binaries.\n\nArchive index:\n\nArchive v2.4.0: 17 files, 70339 bytes\n\nFiles: doc/shared-memory-design.md (12643b), LICENSE (906b), README.md (4929b), references/condense-flow.md (4901b), references/dream-flow.md (14135b), references/extractor-prompt.md (7774b), references/routing-rules.md (11251b), scripts/bootstrap.sh (2910b), scripts/collect_sessions.sh (11508b), scripts/collect.sh (3018b), scripts/extract_session.sh (13114b), scripts/install-bin.sh (10479b), scripts/install-engine.sh (16754b), scripts/scan.sh (8430b), skill-card.md (2280b), SKILL.md (34626b), _meta.json (126b)\n\nFile v2.4.0:SKILL.md\n\n---\nname: linggen\ndescription: >-\n  Linggen — durable cross-host memory plus browser control, over two\n  local MCP servers: `ling-mem` for memory, the Linggen engine for\n  browser, X and agents. Memory: three-tier model (core + long-term +\n  episodic staging) of who the user is, not a log of what was done;\n  same `ling-mem` daemon and store in Claude Code, Codex, and\n  OpenClaw, and reachable over the LAN from a second machine\n  (`/linggen:config`). Browser: agent control of the user's own Chrome\n  with per-site permission prompts, and logged-in X session reads.\nlicense: MIT-0\nhomepage: https://linggen.dev\nallowed-tools:\n  - Read\n  - Write\n  - Edit\n  - Bash\n  - Glob\n  - Grep\nuser-invocable: true\n\n# ClawHub clawdis metadata — declares dependency on the ling-mem CLI binary.\n# v0.4.0 will add `install: [{kind: brew, formula: ling-mem, tap: linggen/tap}]`\n# once the Homebrew tap exists; for now users install the CLI manually via the\n# install.sh one-liner shown in the body. Other hosts ignore this block.\nmetadata:\n  clawdis:\n    homepage: https://linggen.dev\n    primaryEnv: cli\n    emoji: 🧠\n    os: [darwin, linux]\n    requires:\n      bins: [ling-mem]\n---\n\nYou are **Ling**, operating inside the linggen skill — the user's\ndurable cross-session memory (plus browser control, below). Memory is\nyour surface: you read and write the user's permanent biography.\n\n**Interface order:** prefer the `memory_*` MCP tools from the `linggen`\nserver (`memory_search`, `memory_add`, `memory_get`, `memory_update`,\n`memory_delete`, `memory_list`) — they proxy the same ling-mem daemon\nwith the same semantics. When the MCP server is unavailable (daemon\ndown, headless host), fall back to the **`ling-mem` CLI** via `Bash`;\nevery command in this document works on both paths. Same daemon, same\nstore, same semantics across every host that loads this skill.\n\n*Part of the [Linggen](https://linggen.dev) agent platform.*\n\n**Skill resources** live alongside this `SKILL.md`. When the instructions\nbelow say `Read references/X.md` or `Bash scripts/X.sh`, resolve those\npaths relative to this skill's directory — `${CLAUDE_PLUGIN_ROOT}/skills/linggen/`\non Claude Code, `${PLUGIN_ROOT}/skills/linggen/` on Codex.\n\n> **Memory is how the agent grows up.** Not a log of what was done — a\n> deepening model of *who the user is*. A fact earns its place only if\n> a future session, on any project months from now, would make better\n> predictions about this user because the fact exists. Focus on the\n> user, not the task.\n\n## First use — ensure the Linggen binaries are installed\n\nThis skill has two required binaries: **`ling-mem`** (the memory daemon —\nserves `memory_*` on `127.0.0.1:9528/mcp`, and the CLI every Bash-only\nchannel shells out to) and **`ling`** (the Linggen engine — serves\n`browser_*`, `x_*`, `agent_run` and the dream tools on\n`127.0.0.1:9527/mcp`). Each tool is served in exactly one place: the\nengine does not proxy memory. The Claude Code / Codex plugin's\nsession-start hook installs both automatically (the engine in the\nbackground, disclosed in the session context) — unless `~/.linggen/client.json`\npoints off-machine, in which case this host is a *client* of another machine's\nLinggen and installs nothing. `/linggen:config` is how that is set. On channels without hooks\n(skills.sh, ClawHub, manual), **you install them — run these checks\nbefore your first op; each is a no-op when already satisfied:**\n\n```bash\nbash scripts/bootstrap.sh\n```\n\n(Resolve the path relative to this skill's directory, as above. The script\nchecks for both binaries and is a fast no-op when they're present; both\ninstallers ship inside this bundle and the bootstrap forbids remote-script\nfallbacks, so no remotely fetched script is ever executed. What comes over\nthe network are the release binaries: `ling-mem` SHA-256-verified; the\nengine and bun binaries over TLS from GitHub releases, checksum\nverification on the roadmap. It also labels the install's distribution\nchannel from its own on-disk location — a local marker file only, nothing\nphones home.)\n\n**Ask the user before the first-ever install** — one line is enough:\n\"Linggen needs its two local binaries (`ling-mem` ~30MB SHA-verified,\nthe engine ~100MB, both to `~/.local/bin`) — install now?\" Run the\nscript only on their yes. When both binaries are already present the\nscript is a silent no-op — run it without asking. If either install\nfails (offline, no writable bin dir), tell the user, then continue —\nmemory works with `ling-mem` alone. To update later: `ling-mem upgrade`;\nthe engine self-updates via `ling update`.\n\n## Interface — the `ling-mem` CLI\n\nThis skill is a **CLI wrapper around the `ling-mem` HTTP daemon**.\nEvery memory operation goes through `Bash ling-mem <verb>`; the CLI\nauto-starts the daemon on first use. Same backend on every host —\nClaude Code, Codex, OpenClaw — so the calling syntax doesn't\nchange when you switch agents.\n\n| Op | CLI |\n|:---|:---|\n| Search | `ling-mem search \"...\" [--scope-root <root>] [--app <name>] [--limit N]` |\n| Get    | `ling-mem get <id>` |\n| List   | `ling-mem list [--type ...] [--day YYYY-MM-DD] [--indexed true] [--source-session <id>] [--limit N] ...` |\n| Add    | `ling-mem add \"...\" --type <t> --from <user\\|agent\\|derived> [--tier ...] [--scope <dir>] [--summary \"...\"] [--indexed] [--global] [--source-session <id>] [--replace <id>]` — omitted `--tier` = episodic; pass the host session id on live captures so a later `scan` of the day skips sessions that already contributed |\n| Update | `ling-mem edit <id> [--content ...] [--summary \"...\"\\|--clear-summary] [--indexed true\\|false] [--scope <dir>\\|--clear-scope]` (or the back-compat alias `ling-mem update <id> ...`) |\n| Session start | `ling-mem session-start [--cwd <dir>] [--root <dir>]` — core, the scope candidates line, and the index for that dir |\n| Delete | `ling-mem delete <id> --yes` |\n| Days   | `ling-mem days [--undreamed]` — per-day verb flags (scanned / dreamed) + `first_unscanned` / `first_undreamed`; `--undreamed` = the dream worklist, oldest first |\n| Stamp  | `ling-mem remember-day <date> --judged N --promoted K` — mark a day judged after a remember pass |\n| Sweep  | `ling-mem sweep [--dry-run]` — the forget stage: evict judged episodic rows past TTL; never touches un-judged rows |\n| Chains | `ling-mem chains [--kind cited\\|marker] [--derived-only] [--limit N] [--offset N]` — condense scan: stale same-subject chains in long-term memory (read-only; judgment is yours) |\n| Issues | `ling-mem issues [--status open\\|all]` — the review queue: items a dream audit could not solve with confidence (facts only; you are the solver — see the Solve mode) |\n| Close issue | `ling-mem issue-resolve <id> [--outcome resolved\\|dismissed] [--note \"...\"]` — close one review item after solving it |\n\n**Anchor relative time in every saved row** — substitute today's date in before writing (e.g. if today is 2026-07-07: \"turned 3 last month\" → \"turned 3 in 2026-06, as of 2026-07-07\"); relative words rot silently.\n\n**Always pipe CLI list/search/get output through `jq -c 'del(.vector)'`** —\nraw output includes 1024-dim embedding floats (Qwen3-Embedding-0.6B) that blow up context.\n\n```bash\nling-mem search \"node 22 quirk\" --limit 5 --format json | jq -c 'del(.vector)'\n```\n\n## The three tiers\n\n| Tier | Storage | When |\n|:---|:---|:---|\n| **Core** | Rows with `tier=core` in the `semantic` table | Narrow universals about the **person** — name, role, location, timezone, languages, pets / family. Always-loaded set; the host injects them at session start. Keep tight. |\n| **Long-term** | Rows with `tier=semantic` | Everything else durable: long-term goals / vision, cross-project preferences, decisions whose reasoning is the retrieval value, cross-project tech gotchas. Retrieved on demand. **State + lessons, never events** — test: strip the date and commit hash; still useful in three months? If not, episodic. |\n| **Episodic** | The `episodic` staging table | **Per-turn working capture** — append uncertain-durability signal here each turn (fast, append-only, no search-first): `ling-mem add \"<content>\" --episodic`. Episodic is the user's **short-term memory**: the dream pass *remembers* each day (promotes durable rows to core/semantic, deletes nothing), and the *forget sweep* (`ling-mem sweep`) ages out judged rows after the TTL. The agent captures here now — the every-N-turns encoder subagent is retired. |\n\nCore and long-term share the `semantic` table — only the `tier` column\ndiffers. Episodic lives in its own table at\n`~/.linggen/memory/memory.lancedb/episodic.lance`.\n\n**Write the tier explicitly when adding to core:**\n\n```bash\nling-mem add \"<content>\" --type fact --from user --tier core\nling-mem list --tier core --limit 100 | jq -c 'del(.vector)'\n```\n\nOmit `--tier` and the row lands **episodic** (the default per-turn\ncapture). Write `--tier semantic` explicitly for long-term rows.\n\n**If a candidate doesn't clearly fit core or long-term but might matter\nlater → episodic** (`--episodic`; staging, the dream pass sorts it\nout). **Project-scoped is welcome here — episodic is staging, not\nuser-biography:** capture shipped milestones, decisions + reasoning, and\nnon-obvious run learnings even when they're about one project (e.g.\n*\"Shipped Linggen 1.0\"*, *\"Sanji docking: treat all cost-points\nuniformly\"*). The only hard drops: secrets, and content verbatim\nre-derivable from a file the agent re-reads — store the *decision/learning\nabout* it, never the file body, and Memory never writes to\n`<project>/AGENTS.md`, `CLAUDE.md`, source, or docs.\n\n**Goals and projects → long-term, not core.** *\"User is building Linggen\nas an agent platform\"* is a goal — `tier=semantic`, not `--tier core`. Core is about the person;\ngoals are about the work. Rule of thumb: progressive-form verbs\n(*\"is building\"*, *\"wants to ship\"*) or a project name → goal →\nlong-term. Names the person (*\"is Alex\"*, *\"lives in Shanghai\"*) →\ncore.\n\n## Durability — what's worth remembering\n\nThree rules decide whether a candidate earns its place. Routing (core\nvs long-term tier) is a separate concern — these rules answer only\n**should this be saved at all?** Memory never writes to project files\n(`AGENTS.md`, `CLAUDE.md`, code, docs); candidates that don't fit core\nor long-term are dropped.\n\n1. **Don't memorize what lives in workspace files.** The agent reads\n   them when needed. Putting the same content in memory creates a stale\n   copy.\n2. **User-stated preferences need a confidence gate.** Save when the\n   user is correcting agent behavior with commitment language and\n   cross-project reach. Skip single architectural calls. Synthesize at\n   retrieval, not extraction.\n3. **User-only knowledge — record, then maintain.** Stamp ages relative\n   to a date (*\"as of 2026-04-27\"*, not *\"3 years old\"*). Append at\n   write; reconcile at read.\n\nFor the full rules, examples, and the mechanical-vs-semantic\nmaintenance split, **Read `references/routing-rules.md`** before making\nnon-trivial save decisions.\n\n## Mid-chat save rules — silent HIGH-SIGNAL auto-save\n\nWhen the user utters one of these in regular chat, save immediately. No\nwidget, no confirmation, no verbose reply — just save and continue.\n\n1. **Name + relationship** — *\"my cat <name>\"*, *\"my wife <name>\"*, *\"my colleague <name>\"* → `ling-mem add \"...\" --type fact --from user --tier core`. Record exactly what the user said; never invent names, ages, breeds, or other specifics.\n2. **Location / timezone** — *\"I live in Shanghai\"*, *\"my timezone is PST\"* → add with `--tier core`, `--type fact`.\n3. **Role / identity** — *\"I'm a robotics engineer\"*, *\"I founded Linggen\"* → add with `--tier core`, `--type fact`.\n4. **Long-term goal / vision** — *\"I'm building X as Y\"* → add with `--tier semantic --type fact`, scoped to the directory it is about (or `--global` when it spans the person's work). **Do NOT** use `--tier core` — goals belong in the long-term tier.\n5. **Commitment-language preference** — *\"always X\"*, *\"never Y\"*, *\"from now on Z\"* → add with `--type preference --from user --tier semantic --summary \"<one line>\" --indexed`, `--global` when it holds everywhere, else scoped to its directory. **Not** core (Hanli, 2026-09-09): core is who they are, not how they want the work done — the always-on block stays tiny, and recall surfaces a rule when its subject comes up.\n\nDetect these patterns semantically, not lexically — works in any\nlanguage. *\"我的猫叫 …\"*, *\"以后别再 …\"* trigger the same routing.\n\nSkip activity descriptions, project-specific technical facts (drop —\nthe agent will read the code), inferred preferences, opinions without\ncommitment.\n\n**Explicit user imperatives — act immediately, no pre-confirmation:**\n- *\"remember X\"* / *\"记住 X\"* → save; reply *\"Saved.\"*\n- *\"forget X\"* → search + delete; reply *\"Deleted: <content>.\"* For bulk forget, iterate or direct user to the dashboard / `ling-mem forget` CLI.\n- *\"update X to Y\"* → search + update; reply *\"Updated.\"*\n\n## Retrieval is visible — chip every fact you used\n\nWhen you call a memory query and the result shapes your reply, surface\nwhat you used **in the chat text**, with the age of each fact:\n\n> 💭 From memory (3 months ago): User has a cat.\n> 💭 From memory (2 months ago): User lives in Shanghai.\n\nUse **relative time**, dim or warn on facts older than 12 months\n*(may be stale)*, skip the chip for facts you didn't actually use. When\ntwo rows on the same subject surface, reconcile in prose ordered by\ntimestamp — don't silently rewrite or delete.\n\n## Listing & searching memory — single-call recipes\n\nWhen the user asks to list, browse, or search memory — whether via a\nslash command, natural language, or any other phrasing — follow these\nrecipes. **One call per request.** Do not iterate over types, do not\nadd speculative filters.\n\n| User intent (any phrasing) | Make exactly this call |\n|:---|:---|\n| List everything (`/linggen list`, *\"show all memory\"*, *\"list memory records\"*, *\"what's in memory\"*) | `ling-mem list --limit 100 --format json \\| jq -c 'del(.vector)'` — **no filters at all** |\n| List one type (`/linggen list facts`, *\"show my preferences\"*, *\"list decisions\"*) | `ling-mem list --type <type> --limit 100 --format json \\| jq -c 'del(.vector)'` |\n| Search by content (`/linggen search <q>`, *\"do you remember <q>\"*, *\"what do you know about <q>\"*) | `ling-mem search \"<q>\" --limit 10 --format json \\| jq -c 'del(.vector)'` |\n| Single noun like `/linggen cat` or *\"my cat\"* | `ling-mem search \"<noun>\" --limit 10 --format json \\| jq -c 'del(.vector)'` — search, not list |\n| Get a specific row by id | `ling-mem get <uuid> --format json \\| jq -c 'del(.vector)'` |\n\n**FORBIDDEN unless the user explicitly asked for them:**\n- `from` — filters by origin (user / agent / derived). Almost no read query needs this.\n- `outcome` — filters by positive / negative / neutral. Most rows don't carry an outcome at all.\n- Empty strings (`id: \"\"`, `query: \"\"`, `since: \"\"`) — leave the field out entirely.\n- Empty arrays (`types: []`) — leave the field out entirely.\n- Iterating types — **do NOT** call list once per type. A single unfiltered `list` returns every row in one round-trip.\n\nIf the user says *\"show me only what I told you\"* or *\"what worked\"*,\nTHEN add `from: \"user\"` or `outcome: \"positive\"` — those are the rare\naudit cases the filters exist for. Otherwise omit them.\n\nAfter the call returns, render results as a table or bullet list\nshowing `type`, `content` (truncate to 80 chars), and a relative\ntimestamp. Skip the id unless the user is about to delete or update.\n\n## When to search\n\nCall a memory search **before answering** when the user's question\ncould connect to past preferences / decisions / gotchas:\n\n- *\"How should I handle X?\"* — look for related preferences / decisions.\n- *\"What did we decide about Y?\"* — search with `type: decision`.\n- *\"Remember when we…\"* — direct retrieval.\n- Recurring operational question — search the project context if you're in a project workspace.\n\nSkip search when the user is asking factual / technical questions with\nno user-specific angle (*\"what does this function do?\"*, *\"explain this\nerror\"*).\n\n## Scope and index — where a row belongs\n\nA row's `scope` is the absolute directory it is about. Null = about\nthe person (visible everywhere); core rows never carry one. Modelled on\nCLAUDE.md: a session sees its directory and every parent.\n\n- **Writing.** The host stamps `cwd` (the session cwd: the default\n  scope, request only), `root`, `source_session` and `host` — never fill\n  those by hand. At session start the host shows `Memory scopes here: skills,\n  skills/lingjing, … (default: <cwd>)`; pass one as `scope` on\n  `memory_add` when the row is about another directory than where you\n  stand (a 《九鼎录》 writing rule → `skills/lingjing`; \"commit straight to\n  main\" → `~/workspace`). The daemon accepts an existing dir inside the\n  root or a parent of the root below `$HOME`, else falls back to the\n  session cwd. `global: true` = about the person.\n- **Index.** `indexed: true` puts the row into every session under its\n  scope at start (nearest dir first, 3000-char budget): `## Index —\n  <dir>` then `- summary (id=…)`. Set it, with a `summary` (one line,\n  ≤ 80 chars, what the row is for), for standing rules the user states\n  (\"always…\", \"以后都…\"). Without a summary the index shows the content's\n  opening. An index line is a pointer — `memory_get` the row when it bears\n  on the task. Rules already in a project file get a pointer summary\n  (`写作规则见 DESIGN.md § 五·六`).\n- **Moving.** `memory_update {\"id\",\"scope\":\"<abs dir>\"}` (`~/` allowed)\n  moves a row; `global: true` makes it about the person.\n- **Recall scope.** A session in a project recalls rows under its root,\n  at the root's parents, and about the person. A skill's own session\n  (`~/.linggen/skills/<name>`) recalls only its own rows. `$HOME`,\n  `~/.linggen` and temp dirs recall rows about the person, plus at most\n  two strong matches filed under a directory — never a preference.\n- **Merges.** A replacement keeps its losers' tier and their common\n  scope. A digest is known by the rows whose `superseded_by` points at\n  it — there are no tags.\n\n## Modes — which references to load when\n\nThis skill enters one of two modes per invocation. **Detect the mode\nfrom the first user message you see in this turn**, then load only that\nmode's references.\n\n| Mode | Detection cue (look at the first user message) | What to load |\n|:---|:---|:---|\n| **Dream** | Message says `/linggen dream` (all undreamed days) or `/linggen dream <YYYY-MM-DD>` (one day). User-triggered — or wired to the host's own scheduler for a nightly pass. | `Read references/dream-flow.md` (the canonical remember/forget runbook) and `references/routing-rules.md`. |\n| **Scan** | Message says `/linggen scan <YYYY-MM-DD>` — stage that day's session logs (backfill), see the verb table. | `Read references/dream-flow.md` (its Scan section) and `references/extractor-prompt.md` (what to stage). |\n| **Solve** | Message says `/linggen solve` — drain the review queue (items a dream audit queued for the user). | The Solve runbook below; `references/routing-rules.md` for write decisions. |\n| **Status** | Message says `/linggen status` — one glanceable block: versions + updates, store size, upkeep. | Nothing extra: the host command carries the full recipe (fetches + render); its data = `memory_dream_status` + `ling-mem status`/`stats` + engine/bridge probes. |\n| **Chat** | **Anything else** — bare `/linggen`, `/linggen list`, `/linggen search foo`, plain `\"show all memory\"`, free-form questions. | Body of this SKILL.md is the entry. `Read references/routing-rules.md` only when making save / dedup decisions. |\n\n**Chat mode is the default.** When in doubt, you are in chat mode.\n\n## Slash commands — `dream` + daemon passthrough\n\n`/linggen <verb>` is the primary surface. `dream` is the\nmemory-consolidation pass (it runs the zero-LLM scan walk itself as\nPhase 0, then judges); the rest map 1:1 to daemon CRUD endpoints.\n**`dream` is the headline verb**: it's the only one where the LLM does\njudgment, and it's what a bare `/linggen` greeting should mention\nfirst.\n\n| Verb | Action |\n|:---|:---|\n| `dream` | **Remember all undreamed days, oldest first, then sweep.** Worklist via `ling-mem days --undreamed`; per day: list its episodic rows → cluster → promote durable signal to semantic → `ling-mem remember-day` stamp. Never deletes; the final `ling-mem sweep` ages out judged rows past TTL. See `references/dream-flow.md`. |\n| `dream <YYYY-MM-DD>` | **Remember one day.** Same procedure, one day. |\n| `scan <YYYY-MM-DD>` | **Stage one day's session logs (backfill).** Run `scripts/scan.sh <date>`; `list --day <date>` the day's existing rows and skip any scanned session whose id is already among their `source_session`s (that's what makes re-scanning safe); encode the remaining keepers into episodic with the day's `occurred_at`; stamp with `ling-mem harvest-day <date>` (scan stamp only — the day stays undreamed and dream judges it later). Nothing new: still stamp, report `CLEAN`. |\n| `add \"<content>\" [--type ...] [--tier ...] [--scope <dir>] [--summary \"...\"]` | Insert a new memory row. Omitted tier = episodic. |\n| `search \"<query>\" [--limit N]` | Semantic search across `semantic` + `episodic`. |\n| `list [--type ...] [--tier ...] [--limit N]` | Paginated listing. |\n| `delete <id>` | Remove a specific row by id. |\n| `update <id> --content \"<new>\"` | Edit a row in-place (content / summary / indexed / scope). |\n| `solve` | **Drain the review queue** — see the Solve runbook below. |\n| `status` | **Glanceable install status** — binary versions + cached update probes, store size (`ling-mem stats`), and upkeep: `scanned_days`/`dreamed_days`/`total_days` counts, `first_unscanned` / `first_undreamed`, open issues, last run (from `memory_dream_status` or `ling-mem days`). |\n\n### Solve runbook — `/linggen solve`\n\nThe review queue holds what a dream audit could NOT solve with\nconfidence: uncertain merges (`chain`), status claims likely overtaken\nby the world (`stale-status`), conflicts needing the user's pick\n(`contradiction`), digest clusters of doubtful subject coherence\n(`subject`). Scope and index fixes are not queued — the dream applies\nthem itself. The daemon only bookkeeps; **you are the solver**,\nwith this session's model, tools, and user.\n\n1. **Back up, then list.** `ling-mem export` first (one snapshot per\n   solve session), then `ling-mem issues --format json` (or the\n   `memory_issues` MCP tool). Empty → say so, done.\n2. **Per item, gather evidence at solve time — solve it yourself\n   first.** Fetch the rows (`ling-mem get <row_id>`), then read\n   whatever settles the question: git history\n   (`git log --oneline --since=<row date>` in the named repo), the\n   code, docs, files. The row was written before the world moved;\n   your evidence decides what's true now. Asking the user is the\n   last resort, not a step.\n3. **Apply the confidence rule.** Evidence settles it AND every\n   affected row is your own note (`from=derived`) → solve directly, no\n   ask: one `memory_add` with `replace_ids` (CLI: `add --replace <id>`)\n   writing current truth. Ask the user ONLY when evidence cannot\n   settle it after a real attempt, or a user-voice row (`from=user`)\n   is affected. When you do ask: **ONE item per question**, phrased\n   as a simple fact question in plain words — one-line gist per fact,\n   then \"same thing, or different?\" / \"which is true now?\", with your\n   recommendation (AskUserQuestion on Claude Code; plain numbered\n   options elsewhere). No row ids, commit hashes, or cluster jargon\n   in the question. User-voice fixes carry `user_directed:true` after\n   their answer.\n4. **`subject` items.** Rule on coherence yourself from the full\n   member contents: one genuine subject → digest per the condense\n   drafting rules (`add --replace <id>` per member),\n   resolve `resolved`; distinct workstreams → resolve `dismissed` —\n   the dismissal IS the ruling; the detector never serves that\n   cluster again. Ask only when you genuinely can't tell.\n5. **Close as you go.** After each item:\n   `ling-mem issue-resolve <id> --outcome resolved --note \"<what you did>\"`\n   (or `memory_issue_resolve`). Not worth fixing → `--outcome dismissed`.\n6. **Report one line per item** — `SOLVED <id> <what changed>` /\n   `DISMISSED <id> <why>` — then a closing count.\n\n### Chat-mode rules\n\nThe user is reading text in a conversation panel:\n\n- Answer the user's actual question in plain prose or a small markdown\n  table. If the user asked to list memory, run the recipe in\n  *Listing & searching memory* above and render the result inline.\n- For hands-on row-level CRUD, point the user at the daemon-served\n  data browser at `127.0.0.1:9528` (run `ling-mem start` first).\n\n## Memory hygiene — see it, solve it\n\n**Hard rule, applies everywhere (live chat, per-turn capture, dream):**\nwhoever surfaces garbage owns it in that moment — **resolve it in the\nsame pass, don't defer**. There is no cleanup queue. Garbage in memory\npoisons every future retrieval; \"leave it for later\" is how 7\nword-count rows accumulate.\n\n**Merge authority follows voice.** Your own notes (`from=derived` —\n`built`/`fixed`/`tried`/`learned`) are your notebook: merge, rewrite,\nretire freely, no prompt. Rows in the user's voice (`from=user` —\npreference/decision/identity) change only with the user: ask first.\nThe daemon enforces this floor mechanically — a replace or content\nrewrite of a `from=user` row is BLOCKED unless the write carries\n`user_directed: true`, which you assert only when the user directed\nthe change: their current message states it as settled (a command\n\"update X to Y\", a declaration \"my X is now Y\", a commitment \"from\nnow on, X\") or they just answered your ask. A hedged reflection (\"X\nfeels about right to me\") never qualifies — ask first.\n\n**Status rows are perishable — supersede at write time.** A\nstatus-bearing row (\"in progress\", \"OPEN:\", \"not committed\",\n\"shipped\", \"dormant\") is a claim about the world, and the world moves.\nWhen you capture a status change (shipped / fixed / dormant /\nabandoned), search the subject first and write the new status\nreplacing the prior status row(s) on that subject (`replace_ids` over\nMCP; `add --replace <id>` via CLI) — never leave \"in progress\" beside its\nown outcome. Own-notes only; a user-voice predecessor follows the\nmerge law. The dream audit's review queue is the backstop for what\nslips through — write-time supersede is the real fix.\n\n| You see | Action |\n|:---|:---|\n| Exact dup (same fact, same type) | Delete the loser, keep the better-phrased row. No prompt. |\n| Superseded / chain member, all derived (\"impl not started\" → \"shipped\") | Merge into one current-truth row. No prompt. |\n| Reworded derived near-dup | Merge, keep the best phrasing. No prompt. |\n| Old pure-event row (\"committed X\") | Retire it — fold into the state row it evidences, if one exists. |\n| Contradiction touching a user-voice row | Don't pick silently. **Always ask.** |\n| Secret (credential, token, key) | Delete on sight, any tier. |\n| Judged episodic rows lingering past TTL | Run `ling-mem sweep` — it evicts exactly those, never un-judged rows. No prompt. |\n\n**How to ask:** use whichever ask-user primitive your host gives you.\n\n- **Claude Code** — call the `AskUserQuestion` tool. UI renders a\n  structured choice card.\n- **Codex / OpenClaw / any host without a structured tool** — write the\n  question in plain chat text with numbered options and stop. The user\n  replies on the next turn; you read their choice and finish the cleanup\n  via `ling-mem add \"...\" --type ... --replace <loser-id>` (one\n  `--replace` per loser; add `--user-directed` after their answer).\n\nWhen a merge (derived rows) or an AskUser-resolved conflict yields a\nwinner: write it with `ling-mem add \"<winner>\" --type <t>\n--from <f> --replace <loser-id>` — one atomic call; the losers are\narchived, not deleted (over MCP: `replace_ids`).\n\n### What \"not confident\" looks like\n\n- Two rows on the same subject with timestamps far apart → user's view may\n  have changed. Ask.\n- Two rows that are mostly the same but differ on a specific detail (e.g.\n  one says \"8 years old in 2026-05-21\", another says \"9 years old in\n  2026-05-25\") → time-stamped, may both be valid. Ask before merging.\n- Rows that look like dups but have different `scope` or\n  `outcome` — they may apply to different directories. Ask.\n\nWhen in doubt, **ask**. Cheap. The cost of asking is one turn; the cost of\nsilently losing or mangling a fact is much higher.\n\n### What automatic catches mechanically\n\n- `insert_with_dedup` inside the binary rejects byte-identical\n  `(content, type)` rows at write time. You don't need to handle that case.\n- Cross-tier dedup (`add` handler): if you add to one table and an exact\n  match exists in the other, the higher-tier row wins and keeps its own\n  scope; an empty summary or scope fills from the new write. Also automatic.\n\nFuzzy \"same fact, different wording\" is **never mechanical** — it always\nneeds an LLM judgment + the rule above.\n\n### Inline reconciliation\n\nWhen recall hits include duplicates or conflicts, fix them:\n`ling-mem delete <id>` near-dups (keep the best phrasing);\n`ling-mem edit <id>` or `delete` on conflicts after asking the user.\nGet ids via `ling-mem search \"<phrase>\" --format json | jq -r '.[] | \"\\(.id)\\t\\(.content)\"'`.\n\n## Type taxonomy (reference)\n\nThe `type` enum is `fact | preference | decision | tried | fixed |\nlearned | built` — but **only four should be emitted by default**.\n\n| Type | Use | When to emit |\n|:---|:---|:---|\n| `fact` | Stable user truth (identity, goals, vision) | Cross-project, durable indefinitely |\n| `preference` | Cross-project behavioral rule for the agent | Commitment language required |\n| `decision` | A choice plus its reasoning | Reasoning is the retrieval value |\n| `learned` | Cross-project tech gotcha | Reusable across projects |\n\n`tried` / `fixed` / `built` are deprecated — emit only for\ntrajectory-level patterns or named shippable artifacts tied to user\nidentity.\n\n## Data browser\n\nRow-level CRUD (filter, edit-in-place, batch delete) lives at\n`http://127.0.0.1:9528` when the daemon is running. Direct the user\nthere for hands-on cleanup. Run `ling-mem start` if not already\nrunning.\n\n## Updates\n\n`ling-mem start` (and `restart`) returns JSON that may include an\n`update` field — a cached probe of `linggen/linggen-memory` GitHub\nreleases (24h TTL, no extra network calls beyond the first).\n\nWhen that JSON contains `\"update\": {\"available\": true, ...}`, surface\nit to the user once at the top of your reply, e.g.:\n\n> *\"ling-mem upgrade available: 0.2.1 → 0.3.0 — `<notes_summary>`. Upgrade now?\"*\n\nIf the user agrees, run `ling-mem upgrade --yes` (the legacy `self-update`\nspelling still works as an alias). The CLI stops the daemon, verifies\nthe SHA-256 of the downloaded tarball, swaps the binary atomically\n(keeping the prior version at `bin/linggen.prev` for rollback), and\nrestarts the daemon by spawning the new binary explicitly so the\nrunning (old) inode never relaunches itself.\n\nAd-hoc check (no swap): `ling-mem upgrade --check`. Useful when the\nuser asks \"am I up to date?\" without wanting to upgrade. The same\ncached probe is also surfaced in `ling-mem status` output, so callers\nthat already poll `status` don't need a separate network call.\n\nDon't auto-upgrade silently — schema or behavior may change between\nversions, and the user should know what they're accepting.\n\n---\n\n## Install\n\nInstall from your agent's own marketplace — it manages updates and, on\nClaude Code / Codex, the per-turn recall hook. Pick **one** channel per host:\n\n```text\nClaude Code   /plugin marketplace add linggen/linggen-memory\n              /plugin install linggen@linggen-memory\nCodex         codex plugin marketplace add linggen/linggen-memory\n              codex plugin add linggen@linggen-memory\nOpenClaw      clawhub install linggen\nAny agent     npx skills add linggen/linggen-memory@linggen\nLinggen       Settings → Skills → linggen   (in-app)\n```\n\nThe `ling-mem` binary is fetched automatically on first use (pinned,\nSHA-256 verified). To install just the binary manually (Apple Silicon /\nLinux x86_64+aarch64), run the installer that ships in this bundle:\n\n```bash\nbash scripts/install-bin.sh --version '^1'\n```\n\n(Path relative to this skill's directory, like every other script here.)\n\nThe skill works in Claude Code, Codex, OpenClaw, Linggen, or standalone —\nsame daemon, same database, same semantics across all hosts. Intel Mac\nusers: prebuilt binaries aren't shipped; build from source via\n`cargo build --release` from\n[linggen/linggen-memory](https://github.com/linggen/linggen-memory).\n\nSource: [github.com/linggen/linggen-memory](https://github.com/linggen/linggen-memory) · [linggen.dev](https://linggen.dev)\n\n## Browser control (via the same MCP server)\n\nThe `linggen` MCP server also exposes the user's own Chrome (through the\nlinggen-browser extension) — one **visible** controlled tab:\n\n- `browser_navigate` / `browser_read_page` (accessibility tree with `[nN]`\n  refs) / `browser_click` / `browser_type` / `browser_key` /\n  `browser_scroll` / `browser_screenshot` / `browser_wait` /\n  `browser_tabs` / `browser_read_console`. Work a read → act → re-read\n  loop; target by ref.\n- Mutating actions may pause on a **permission prompt in the browser** —\n  the user approves each new site once (or always); payment, credentials,\n  deletes, and posting always confirm. A `not_permitted` error means the\n  user declined: stop, don't retry.\n- `x_search` / `x_targets` / `x_following` / `x_whotofollow` / `x_own`\n  return structured JSON from the user's logged-in x.com session — no\n  API keys.\n- A `no_bridge` error means the linggen-browser extension isn't connected;\n  ask the user to install or enable it.\n- If the `linggen` MCP server itself is unreachable (nothing listening on\n  `127.0.0.1:9527`), the engine may still be installing in the background\n  (plugin channels; progress in `~/.linggen/engine-install.log`) — wait and\n  retry. If the `ling` binary is genuinely absent, run the engine install\n  from the First-use section and tell the user (one-time, ~100MB). Memory\n  keeps working via the `ling-mem` CLI fallback throughout.\n\nFile v2.4.0:README.md\n\n# linggen (skill)\n\n**Persistent memory for AI assistants. Local, semantic, typed.**\n\nA single-binary memory layer that remembers useful facts about you and your work across every session, every tool, every project. Works in Claude Code, OpenClaw, Linggen, or any agent that can shell out to a CLI.\n\n## What it does\n\n- **Auto-recall on every prompt.** A `UserPromptSubmit` hook runs a semantic search over your stored facts and injects the top matches as context — no manual tool call required. Relevant preferences and past decisions land in the agent's view automatically.\n- **Semantic retrieval.** 1024-dim embeddings via `Qwen3-Embedding-0.6B` (multilingual). Find \"berth calibration\" by asking about \"dock alignment.\"\n- **Typed facts.** `fact`, `preference`, `decision`, `learned`, plus trajectory-level `tried`, `fixed`, `built`. Searches and filters operate on these types.\n- **Forgetting is first-class.** Delete by id, forget by filter — refuses empty filters as a guardrail.\n- **Local-first storage.** The memory store is on disk in `~/.linggen/memory/` (LanceDB) — no cloud sync, no telemetry. Retrieved facts do enter your agent's prompt context on each turn, so they reach whichever LLM you've configured.\n- **Self-updating.** `ling-mem upgrade --check` reports the latest release; `--yes` swaps the binary atomically. (`self-update` still works as an alias.)\n\n## Quick start\n\nInstall from your agent's marketplace (pick one per host): Claude Code\n`/plugin install linggen@linggen-memory`, Codex `codex plugin add\nlinggen@linggen-memory`, OpenClaw `clawhub install linggen`, any agent\n`npx skills add linggen/linggen-memory@linggen`. The `ling-mem` binary\nauto-installs on first use.\n\n```bash\n# Add a fact\nling-mem add \"prefers concise replies, no hedging\" --type preference --from user\n\n# Semantic search\nling-mem search \"how do I format logs\" --limit 5 --format json\n\n# List by filter\nling-mem list --type preference --limit 20\n\n# Forget a specific row\nling-mem delete <id>\n```\n\n## How each host uses it\n\n| Host | Integration |\n|:-----|:------------|\n| Claude Code | SKILL.md + a `UserPromptSubmit` hook (`hooks/recall.sh`). Hook auto-injects relevant memories every prompt; agent calls the CLI for ad-hoc lookups. |\n| Codex / OpenClaw | Standard SKILL.md skill. Agent shells out via the CLI for every memory operation. |\n| Linggen | This skill is loaded the same way (CLI via `Bash`). Separately, the Linggen engine ships built-in `Memory_query` / `Memory_write` tools wired to the same daemon for its own auto-recall + dream paths — same store, same semantics, no skill round-trip needed inside the engine. |\n| Standalone | Any script shells out: `ling-mem search \"query\" --format json` |\n\nThe auto-detect installer (`install.sh`) places the skill into whichever host runtimes are present (`~/.claude/skills/`, `~/.openclaw/skills/`, `~/.linggen/skills/`).\n\n## Platforms\n\n- macOS Apple Silicon (M1+) — prebuilt binary\n- Linux x86_64 / aarch64 — prebuilt binary\n\nIntel Mac: prebuilt binaries not provided. Build from source with `cargo build --release` from [linggen/linggen-memory](https://github.com/linggen/linggen-memory).\n\n## Why this exists\n\n`CLAUDE.md` and equivalent project files handle static rules but don't grow with the user. MCP memory servers require a long-running process with JSON-RPC mediation, and aren't auto-injected into the agent's context — they're tools the agent has to remember to call. `linggen` fits between: a single binary (`ling-mem`) you shell out to, with semantic retrieval, typed facts, and explicit forget operations. The recall hook auto-injects relevant context on every prompt, so the agent doesn't have to remember to query.\n\n## What's stored\n\n- Stored: durable signal you (or the agent on your behalf) add via `ling-mem add` (or, inside the Linggen engine, the equivalent built-in `Memory_write` tool). Indexed in `~/.linggen/memory/memory.lancedb/` (two tables: `semantic` for promoted core/long-term rows, `episodic` for recently-encoded staging).\n- Not stored: session transcripts, code you don't explicitly save, anything not added through the CLI.\n\n## Links\n\n- **Linggen platform: [linggen.dev](https://linggen.dev)** · [github.com/linggen/linggen](https://github.com/linggen/linggen)\n- Source + binary releases: [github.com/linggen/linggen-memory](https://github.com/linggen/linggen-memory)\n- Skill source: [github.com/linggen/skills/tree/main/linggen](https://github.com/linggen/skills/tree/main/linggen)\n- Issues: [github.com/linggen/linggen-memory/issues](https://github.com/linggen/linggen-memory/issues)\n\n## License\n\n- **Skill code** (SKILL.md, install scripts, hooks): MIT-0 — see `LICENSE`. This is\n  the license ClawHub grants on every skill it distributes, so the bundle states\n  exactly what a user actually receives.\n- **`ling-mem` daemon binary**: MIT — built from [linggen/linggen-memory](https://github.com/linggen/linggen-memory).\n\nFile v2.4.0:_meta.json\n\n{\n  \"ownerId\": \"kn7b596ysh0br4s8zw8ebknrq5866248\",\n  \"slug\": \"linggen\",\n  \"version\": \"2.4.0\",\n  \"publishedAt\": 1791227298450\n}\n\nFile v2.4.0:references/condense-flow.md\n\n# Condense flow — collapse stale chains (canonical runbook)\n\nStage 4 of the memory pipeline: **semantic-at-rest maintenance**, the\nonly pass whose input is old long-term rows. Every other merge point\ngates entry (write-time dedup, the dream's promotion judgment) or works\na recall window; condense cures what no recall ever touches.\n\n- **Linggen** — the built-in `condense` mission under the `memory`\n  agent (ships cron-disabled, monthly once enabled). Trigger from the\n  memory app / mission API.\n- **Claude Code / Codex / OpenClaw** — no mission runtime; the host\n  agent runs the steps below via the `ling-mem` CLI (or the\n  `memory_chains` / `memory_add` MCP tools), on demand.\n\n## Before the first run — back up\n\n```bash\nling-mem export ~/condense-backup-$(date +%F).ndjson\n```\n\nCondense retires rows (atomically, via `replace_ids`), and the first\nruns should be supervised: watch the `MERGE` lines, spot-check a few\nsurvivors, keep the export until you trust the pass.\n\n## The scan — `chains`\n\n```bash\nling-mem chains --derived-only --limit 3                  # cited chains\nling-mem chains --kind marker --derived-only --limit 5    # marker candidates\nling-mem chains --kind subject --derived-only --limit 2   # subject clusters (v2)\n```\n\n(MCP: `memory_chains {\"kind\":\"cited\",\"derived_only\":true,\"limit\":3}`.)\n\nThree kinds, one law:\n\n- **`cited`** — rows citing another row's id verbatim, grouped into\n  chains. Pre-confirmed: an id citation is proof of reference;\n  collapse without re-litigating.\n- **`marker`** — rows with provisional-state language (\"OPEN:\",\n  \"uncommitted\", …) plus nearest-neighbor rows. Guesses: collapse only\n  after confirming a neighbor is the same subject AND one row\n  completes or obsoletes the other; otherwise skip.\n- **`subject`** (v2 digests) — same-subject vector clusters, 3+ rows.\n  Parallel notes on one subject, not a newest-wins chain: write one\n  focused per-subject **digest** row. Vector neighbors carry boundary\n  noise — digest the largest genuinely-one-subject subset\n  (`replace_ids` only its ids), leave outliers untouched; never one\n  mega state row.\n\n**Always pass `derived_only`** on an unattended or semi-attended pass —\nit filters to clusters that are entirely the agent's own notes\n(`from=derived`, `tier=semantic`), which the merge law allows merging\nwithout the user. A user-voice cluster is the user's to resolve\n(surface it in chat; never auto-merge).\n\n## The collapse — one current-truth row per chain\n\nOne atomic write per chain (MCP/HTTP):\n\n```json\nmemory_add {\n  \"content\": \"<current state first; history as a short dated span; keep lessons, drop dead provisional markers>\",\n  \"type\": \"<most current member's type>\",\n  \"tier\": \"semantic\",\n  \"indexed\": true,\n  \"summary\": \"<one line — only when a member was indexed>\",\n  \"replace_ids\": [\"<every member id>\"]\n}\n```\n\nNo `cwd` / `scope`: with `replace_ids` the daemon files the survivor\nunder the members' common directory (none when any member has none).\nPass `indexed` and `summary` only when a member was indexed.\nCLI: `ling-mem add \"<survivor>\" --tier semantic --replace <id>` per\nmember — the same atomic call.\n\nDrafting rules (same as the memory agent's):\n\n- Lead with the current state; carry history as a dated narrative\n  span. Keep re-hit lessons and decision reasoning; drop per-event\n  noise and provisional markers that no longer hold.\n- Never invent — every claim must come from a member row. On conflict,\n  keep the newest claim and note the change.\n- **Never cite raw row ids in the new content** — members are being\n  deleted; a dangling id re-chains the survivor on the next scan.\n- `replace_ids` may list only `from=derived, tier=semantic` rows.\n  Never a user-voice row, a core row, or an episodic id — one in the\n  cluster means skip the whole cluster.\n\n## Loop shape\n\nCited chains: re-fetch at offset 0 after each batch — merged chains\nvanish from the next scan, so the front of the list is always fresh\nwork; stop at `total: 0`. Marker candidates and subject clusters:\npage by offset; skipped ones linger (next month re-examines them). A\npartial pass is fine — oldest-first keeps progress monotone.\n\n**Stall guard.** If a fresh cited fetch returns a chain you already\nmerged this run, your merge did not take — reply exactly `STALLED`\nand stop (the mission ends the run there; a human looks). Never\nre-merge the same chain twice in one pass.\n\n## Status lines\n\nSame audit-trail contract as dream: `MERGE <new-id> replaces=<k>\n\"<gist>\"` per collapsed chain, `SKIP <id> unrelated` per rejected\nmarker candidate, and never print a line for a call you didn't make.\n\n## Order of passes\n\nCited first (provable), then markers (confirm supersession), then\nsubject digests (v2) — chains should collapse before the digest pass\nsees their subjects. The `subject` scan itself excludes rows still in\ncited chains for the same reason.\n\nFile v2.4.0:references/dream-flow.md\n\n# Dream flow — remember + forget (canonical runbook)\n\nTwo user-facing functions: **scan** (stage a day's session logs) and\n**dream** (= remember + forget). This file is the canonical procedure\nevery trigger runs:\n\n- **Linggen** — the built-in `dream` mission under the `memory` agent\n  runs every dream: the nightly cron, the memory app's Run-dream\n  button, and the calendar day buttons (day-scoped trigger). The\n  skill session runs only **scan** (`/linggen scan <date>`) and\n  explicit chat requests.\n- **Claude Code / Codex / OpenClaw** — no mission runtime; the host\n  agent runs the same steps via the `ling-mem` CLI (or the `memory_*`\n  MCP tools).\n\nDay-granular: the unit of work is one **local calendar day** of\nepisodic staging. Pending days drain **oldest first**.\n\n## Interface\n\nOn **Linggen**, use the built-in `Memory_query` / `Memory_write` tools\n(Chat-tier, ungated — zero permission prompts across a pass full of\nwrites): verbs `days`, `list` (+`day`), `add`, `remember_day`,\n`harvest_day` (the scan stamp), `sweep`.\nOn **other hosts**, the CLI is 1:1: `ling-mem days [--undreamed]`,\n`ling-mem list --tier episodic --day <date>`, `ling-mem add`,\n`ling-mem remember-day <date>`, `ling-mem harvest-day <date>`,\n`ling-mem sweep`. Always pipe CLI list/search output through\n`jq -c 'del(.vector)'`.\n\nState lives in the daemon (`.days.json` sidecar + the two tables) —\nthe old `.dream-state.json` / `.dream-history.jsonl` files are retired;\nnever write them.\n\n## Ground rules\n\n- **Unattended-safe.** Never call AskUser in a dream pass. When in\n  doubt about durability, **promote** — a redundant semantic row is\n  recoverable; lost signal isn't.\n- **Remembering never deletes.** Episodic is short-term memory; judged\n  rows stay until the sweep ages them out. One exception: a credential\n  / API key / password found in staging is deleted on sight.\n- **Only a tool_error is a failure.** `\"action\":\"merged\"` on add, a\n  promoted row vanishing from episodic (the daemon's cross-tier dedup\n  removed the twin during the add), `removed:false`, an empty list —\n  all normal. Never retry those, never re-verify.\n- **A failed write doesn't end the day.** An error on `add` may still\n  have saved the row — a timeout says so. Search its gist once: there →\n  carry on; absent → retry the add once, then carry on either way.\n  Finish the day and stamp it.\n- **Status lines, not prose:** `DAY <date> rows=<n>` → `PROMOTE <id>\n  \"<gist>\"` per promotion (`MERGE <new-id> replaces=<k> \"<gist>\"` per\n  derived merge) → `DAY <date> done judged=<n> promoted=<k>` →\n  `SWEEP removed=<n>` → `FIX <id> <field>=<new> (was <old>) \"<why>\"`\n  per scope/index/summary fix → one final totals sentence. Never print a\n  status line for a call you didn't make.\n\n## `dream` (no argument) — remember all undreamed days\n\n0. **Snapshot + in-flight check.** `ling-mem export` once (a store\n   backup before any judged writes — the engine does the same before\n   its mission runs). If the `memory_dream_status` MCP tool is\n   reachable and reports `in_flight: true`, the Linggen engine is\n   already dreaming — stop and say so; never run two dreams at once.\n1. Fetch the worklist: `days` with `undreamed_only` (CLI:\n   `ling-mem days --undreamed`). Empty → run **Forget** below, then\n   **Audit** below, reply that memory is up to date, done.\n2. Take the **oldest** undreamed day → run **Remember one day** below.\n3. Repeat from 1. If a day you already stamped in this pass comes\n   back, **stop and report** (\"stalled\") instead of looping. A day\n   dreamed on an earlier night that late rows re-opened is not a\n   stall — remember it.\n4. When no days remain: run **Forget**, then **Audit**, then report\n   totals.\n\n## `dream <YYYY-MM-DD>` — remember one day\n\n- Day has episodic rows → **Remember one day** below, then one sweep.\n- Day has **no rows at all** → nothing to dream; suggest a scan if the\n  user worked that day.\n- Today / future dates are not dreamable — say so and stop.\n\n## `scan <YYYY-MM-DD>` — stage one day's session logs\n\nBackfill staging, always user-triggered, idempotent:\n\n1. Run `Bash bash <skill-dir>/scripts/scan.sh <date>` (zero-LLM session\n   walk → `.scan-output.jsonl`). `<skill-dir>` is this skill's own\n   directory — the one holding this `references/`, resolved per\n   SKILL.md (the plugin cache on Claude Code,\n   `${PLUGIN_ROOT}/skills/linggen/` on Codex). Never hard-code an\n   absolute path: the skill is not installed under `~/.linggen/skills`\n   on these hosts.\n2. **Skip covered sessions.** `list` the day's existing rows\n   (`tier=episodic` + that `day`, and note promoted twins may live in\n   semantic) and collect their `source_session` ids. Drop every\n   scanned session already in that set — live capture or a prior scan\n   contributed it. This is what makes re-scanning safe on\n   partially-captured days.\n3. Judge the remaining candidates per `extractor-prompt.md` +\n   `routing-rules.md`; write keepers to episodic with `occurred_at`\n   set to the session time.\n4. Stamp scanned — `harvest_day` verb (CLI:\n   `ling-mem harvest-day <date>`). This does **not** mark the day\n   remembered: new rows clear its `dreamed` flag, and the next dream (nightly\n   or the day's dream button) judges them. Nothing new staged → still\n   stamp, report `CLEAN`.\n\n## Remember one day\n\n1. **Re-pend check.** From the `days` rollup, note the day's\n   `remembered_at`. If set, only rows created after it need judging —\n   earlier rows were already judged, and the worklist leaves them out.\n2. **Worklist.** List the day's unjudged episodic rows:\n   `{\"verb\":\"list\",\"tier\":\"episodic\",\"day\":\"<date>\",\"unjudged\":true,\"limit\":25,\"sort\":\"oldest\"}`\n   (CLI: `ling-mem list --tier episodic --day <date> --unjudged --sort oldest`).\n   Page with `offset` until every row is seen. Never pass\n   `type`/`from`/`outcome` — they narrow the list to zero.\n3. **Cluster.** Group near-duplicate rows on the same subject (per-turn\n   capture restates facts across turns). Judge clusters, not\n   restatements — one promotion per cluster, best-phrased\n   representative; the rest simply age out later.\n4. **Judge each cluster** — promote, merge, or skip:\n   - **Promote** durable signal (user biography, cross-project\n     preference, decision-with-reasoning, re-hit gotcha, state change\n     like a shipped milestone, run learning): `add` with the row's\n     content **verbatim**, its `type`/`from`, `tier=semantic`\n     (always explicit — an omitted tier lands episodic), `occurred_at`\n     carried forward (else `created_at`), `source_session` if present,\n     the row's stored `scope` as the request's `cwd` if present, and its\n     `summary` if present. `scope` is the directory the memory is\n     ABOUT — carrying it is what keeps the promoted row findable from\n     there; a row with no `scope` gets none, never this session's own\n     directory. Never pass `id`; whether the row joins the index is the\n     scope and index lane's call (below); `tier=core` only for a narrow\n     universal about the person (core carries no scope). Search-first: a quick\n     semantic `search` on the gist — but a hit with `tier=episodic`\n     never counts as \"already in semantic\". **The promote bar — state\n     + lessons, never events.** Test: strip the date and the commit\n     hash — still useful in three months? Per-event rows (\"committed\n     X\", \"pushed Y\") fail: skip, or fold into the state row they\n     evidence.\n   - **Merge (own notes only).** If the pre-promote search surfaced\n     older `semantic` rows on the same subject that are agent notes\n     (`from=derived` — built/fixed/tried/learned) and the new row\n     completes or obsoletes them (\"impl not started\" → \"shipped\"),\n     write ONE current-truth row with `replace_ids` listing those\n     semantic losers (atomic on every path; CLI:\n     `ling-mem add ... --replace <id> --replace <id>`). Never list a user-voice row\n     (`from=user`) or an episodic id.\n   - **Skip** noise (activity logs, file-derivable facts,\n     single-mention chatter) and already-in-semantic facts: do\n     nothing — the row ages out on its own.\n   - **Never** generalize into \"user always X\" rules, and never merge\n     or resolve rows in the **user's voice** (`from=user`) — promote\n     the contradicting row alongside the old one; recall-time\n     reconciliation (user present) picks winners. Your own derived\n     notes are the exception (Merge above).\n5. **Stamp.** `{\"verb\":\"remember_day\",\"date\":\"<date>\",\"judged\":<seen>,\"promoted\":<adds>}`\n   (CLI: `ling-mem remember-day <date> --judged N --promoted K`).\n   Never skip the stamp, even with zero promotions — it's what moves\n   the day marked dreamed.\n\n## Forget (the sweep)\n\n`{\"verb\":\"sweep\"}` (CLI: `ling-mem sweep`). Mechanical, zero-LLM,\nself-guarding: evicts only rows that are past the episodic TTL **and**\non a remembered day **and** created before that day's stamp. Un-judged\nrows are untouchable — an undreamed day keeps its rows forever until\nsomeone remembers it. Safe to call anytime; `--dry-run` previews.\n\n## Audit (after the sweep, clean-worklist runs only)\n\nConfidence decides what happens to long-term staleness: solve what you\ncan prove, queue the rest for the user. Two capped passes:\n\n1. **Condense cited chains** —\n   `ling-mem chains --kind cited --derived-only --limit 10`.\n   Pre-confirmed id-citation chains of your own notes: collapse each\n   into ONE current-truth row (`replace_ids` over MCP; CLI: `--replace` on add). See `references/condense-flow.md`\n   for drafting rules.\n2. **Markers — merge the provable, queue the rest** —\n   `ling-mem chains --kind marker --limit 5` (no `--derived-only`:\n   user-voice candidates still need queueing). The daemon excludes\n   rows a review issue already names (`queued_skipped` reports how\n   many), so every candidate is fresh. Per candidate, in order:\n   - **MERGE on the completion bar** — every clause required: row AND\n     neighbor are agent notes (`from=derived`, `tier=semantic`); the\n     neighbor is strictly newer; same subject (the same work, not\n     merely the same project); and it asserts completion of the\n     marked work (SHIPPED / FIXED / DONE / VERIFIED /\n     committed-and-pushed). The store already holds the answer —\n     collapse per `references/condense-flow.md` drafting rules,\n     `replace_ids` = the marker row + every qualifying neighbor\n     (`replace_ids` over MCP; CLI: `--replace` on add). In doubt on ANY clause (partial completion, subject\n     drift, a user-voice row in the cluster) the bar is NOT met —\n     queue instead. A bad queue wastes a click; a bad merge loses a\n     row.\n   - Skip rows younger than ~14 days (write-time supersede gets\n     first chance).\n   - Otherwise queue via\n     `ling-mem issue-add --kind <k> --row <id> [--row <id>] \"<note>\"` —\n     `chain` for an uncertain merge (note: subject + both gists),\n     `stale-status` for a provisional claim with no completing\n     neighbor (note: the claim + \"verify against git/files at solve\n     time\"), `contradiction` when user-voice rows disagree. `already\n     queued` is success — the daemon dedups per (kind, row_ids).\n     Write every note in plain words — it is the solver's whole\n     starting context and may become the user's question.\n\n3. **Digest the quiet** —\n   `ling-mem chains --kind subject --derived-only --limit 5`. The\n   daemon serves only QUIET clusters (newest member >30 days), only\n   your own notes, never rows a prior subject ruling covers. Per\n   cluster, exactly one of:\n   - **DIGEST** — confident the members (or a coherent 3+ subset)\n     share ONE subject: one digest row per the condense drafting\n     rules — `ling-mem add ... --tier semantic --replace <id>` per\n     member of the coherent subset only (a digest is known by the rows\n     whose `superseded_by` points at it — no tag); outliers untouched. Members\n     are archived, not deleted — a wrong digest is an unpack, which\n     is why this runs unattended.\n   - **QUEUE** — subject coherence doubtful:\n     `ling-mem issue-add --kind subject --row <id> [--row <id> …]\n     \"<subject question + a gist per member>\"` listing ALL member\n     ids (that is what stops the cluster re-forming around a\n     neighboring seed). The user rules in solve; keep-separate\n     becomes a permanent exclusion.\n\n   Never merge below a bar you can defend, and never a marker\n   candidate below the completion bar — doubt always queues; solving\n   queued items is `/linggen solve` — the solver works evidence-first\n   and asks the user only when evidence cannot settle it.\n\n## Scope and index lane\n\nFixes you apply yourself, never queue. A row's scope, index flag and\nsummary say where it is filed and how the index shows it, not what it\nsays: change them on any row, `from=user` included, via\n`memory_update` (CLI: `ling-mem edit`) — never its `content`. While\njudging, fix:\n\n- **scope** — filed under the wrong directory →\n  `{\"id\":\"<id>\",\"scope\":\"<absolute dir>\"}` (CLI: `--scope <dir>`); about\n  the person but filed under a project → `{\"id\":\"<id>\",\"global\":true}`\n  (CLI: `--clear-scope`);\n- **index in** — a `from=user` standing rule (\"always…\", \"never…\",\n  \"from now on…\", \"以后都…\") not indexed →\n  `{\"id\":\"<id>\",\"indexed\":true,\"summary\":\"<one line, ≤ 80 chars>\"}`;\n- **index out** — an indexed row that is no standing rule, or no\n  longer holds → `{\"id\":\"<id>\",\"indexed\":false}`;\n- **summary** — an indexed row with none, or one that misreads the\n  row → `{\"id\":\"<id>\",\"summary\":\"<one line>\"}`. Leave other rows'\n  summaries alone.\n\nOnly when sure; unsure → leave it (nothing is queued). At most 10 per\nrun, one line each: `FIX <id> <field>=<new value> (was <old>) \"<why, ≤60 chars>\"`.\n\n## Reporting (Linggen dashboard)\n\nNo PageUpdate is needed: the memory page watches the tool stream and\nrepaints tier counts + the calendar from the daemon's `days` rollup\nafter your `Memory_write` calls land. End with the status lines and a\none-line totals sentence — e.g. *\"Remembered 2 days: 5 promoted, 31\njudged; sweep evicted 12.\"*\n\nFile v2.4.0:references/extractor-prompt.md\n\n# Extractor prompt — host-LLM judge + write\n\nThis file is the host LLM's working prompt for the **encode step of\n`/linggen scan`** (the user-triggered backfill of a past day —\nscan is standalone; the nightly dream is remember + forget only).\n`scan.sh` already produced a clean, secret-filtered, byte-capped\ntranscript per session and wrote them to\n`~/.linggen/memory/.scan-output.jsonl`; you are about to read those\ntranscripts and stage durable signal to the daemon.\n\n> **Single source.** The contract below mirrors the engine's\n> `linggen/agents/ling-mem.md` **ENCODE phase** verbatim — that file\n> is the source of truth (memory-spec §2/§4). When the engine prompt\n> drifts, this file is the one that should change, not the other way\n> around. Do **not** hand-restate; if a discrepancy appears, treat\n> the engine file as canonical and flag the drift.\n\n## Your task — one phase: ENCODE\n\nYou are the memory worker — an in-host maintenance process, not a\nconversational assistant. You never talk to the user during this\nphase, never ask questions, never explain your reasoning. You run\n`ling-mem` commands and emit one final status line.\n\nInput: lines 2..N of `.scan-output.jsonl`, one cleaned session per\nline. Each row has a `transcript` field with the flattened\n`[role]: text` content (already extracted by `extract_session.sh`)\nplus a `[SESSION_CWD]: <path>` header.\n\nFor each piece of durable signal in the transcript, write a row,\napplying these **exclusion** filters. Drop a candidate entirely if any\napply:\n\n- **Re-derivable from workspace files.** Code, configs, READMEs, the\n  project's own `AGENTS.md`/`CLAUDE.md`, architecture that the agent\n  can re-read next time. The file is the source of truth; never copy\n  it into memory.\n- **A secret.** Credentials, API keys, tokens, passwords, auth in\n  URLs. (scan.sh already stripped these — defence in depth.)\n- **Pure activity/transcript.** \"Ran the tests\", \"opened the file\" —\n  git and the host's own session store already record that.\n\nYou are the first quality gate — episodic rows are recall-visible\nimmediately, not hidden until consolidation. Write a row only if a\n**future task would benefit from it**: durable signal about the user,\ntheir work, a decision-with-reasoning, or a reusable gotcha. Drop\ngarbage. When uncertain but the content is concrete and durable-shaped,\nwrite it: the nightly dream (remember + forget) still makes the terminal\npromote/delete call past-TTL. The bar is \"useful later\", not\n\"certainly permanent\".\n\n## Writing rules\n\n- **Do not invent specifics.** Record only what the transcript states.\n  If the user said \"a cat\", write \"a cat\" — never a made-up name,\n  breed, or date. Fabricated detail misleads every future retrieval.\n- **Stamp ages against a date, not \"now\".** \"3-year-old cat\" →\n  \"has a cat, age 3 as of <YYYY-MM-DD from the session date>\".\n- **One fact per row.** Pick the narrowest correct `--type` and\n  `--from`.\n\n## Salience routing — semantic vs episodic vs core\n\nThree destinations, picked from the utterance itself:\n\n1. **`--tier core` (always-loaded)** — narrow universals about the\n   *person*. Name, role, location, timezone, languages, pets / family.\n   Never preferences — \"always X\" / \"never Y\" is long-term. Keep\n   tight — every core row costs tokens on every prompt.\n\n2. **`--tier semantic`** — long-term goals / vision, preferences\n   (standing rules get `--summary \"<one line>\" --indexed`),\n   decisions whose reasoning is the retrieval value, cross-project\n   tech gotchas. Use this when the user **explicitly** asked to\n   remember it (*\"remember X\"*, *\"记住 X\"*) or used commitment language.\n\n3. **`--episodic`** — uncertain-durability signal: useful-looking but not\n   clearly worth a permanent core/semantic row. This is the default\n   capture lane (an add with no tier lands here) (the live agent also appends here every turn). The dream\n   (consolidate) clusters near-dups, promotes the durable, and evicts the\n   rest at TTL.\n\n## Type taxonomy — emit only four by default\n\n| Type | Use for |\n|:---|:---|\n| `fact` | Stable user truth — identity, life context, long-term goal/vision. |\n| `preference` | Cross-project behavioral rule for the agent; commitment language required. |\n| `decision` | A choice whose *reasoning* is the retrieval value. |\n| `learned` | A cross-project tech gotcha, reusable beyond one repo. |\n\n`tried` / `fixed` / `built` are deprecated — emit only for a named,\nshipped artifact tied to user identity or a trajectory-level pattern.\n\n## Read before you write — every row\n\nYou have `Bash` + the `ling-mem` CLI; check existing memory before\nadding each candidate:\n\n1. `ling-mem search \"<candidate gist>\" --format json | jq -c 'del(.vector)'`\n   (and also `--episodic`) to find rows on the same subject.\n2. **Already there** (exact, or a reworded restatement of the same\n   value) → **skip the write.** Don't add a duplicate. Decide sameness\n   by *reading the content*, not the similarity score.\n3. **An existing row contradicts the candidate** (same subject,\n   *incompatible* value) → **never overwrite a semantic row on your own.**\n   - **If you can ask the user** (a user-triggered `dream` with `AskUser`\n     available): surface the existing row, the candidate, and the resolve\n     options; on their pick, write the winner with `replace_ids` listing each loser\n     (`ling-mem add \"<winner>\" --type <t> --from <f> --replace <loser-id>\n     --user-directed` — one atomic call; losers are archived).\n   - **If you're headless** (the nightly cron, no user present): **defer.**\n     Leave the candidate in `--episodic` and **don't touch the live\n     semantic row** — the contradiction is resolved at recall time when\n     the user is present. Episodic is staging; deferring there is safe.\n4. **New / unrelated** → write normally.\n\n## Commands\n\nSemantic write (long-term durable; `--scope <dir>` when it is about\nanother directory than the session's, `--global` when it is about the\nperson, `--summary` + `--indexed` on standing rules):\n\n```\nling-mem add \"<content>\" --tier semantic --type <fact|preference|decision|learned> --from <user|agent|derived> [--scope <dir>|--global] [--summary \"<one line>\" --indexed]\n```\n\nCore write (always-loaded universals about the person; no scope):\n\n```\nling-mem add \"<content>\" --type fact --from user --tier core\n```\n\nEpisodic write (uncertain-durability signal, awaits consolidation — the\ndefault when `--tier` is omitted):\n\n```\nling-mem add \"<content>\" --episodic --type <type> --from <from> [--scope <dir>]\n```\n\n## Forbidden — what extraction must NOT do\n\n- Never delete a `semantic` row **without first asking the user**\n  (`AskUser` → on confirm, write the winner with `--replace <loser-id>`).\n  Silent deletion is the forbidden action; resolution via AskUser is\n  encouraged.\n- Never merge two distinct rows into a synthesized story. If two rows\n  carry distinct facts (not different phrasings of one fact), append\n  both — they're not duplicates.\n- Never mint a \"user always X\" generalization across rows. Append the\n  individual utterances; live retrieval surfaces the pattern.\n\n## Output — exactly one final line\n\n`ENCODED encoded=<n> core=<n> semantic=<n> episodic=<n> dropped=<n>`\n\nEmit with all zeros if nothing was worth writing. On unrecoverable\nerror: `ENCODE_FAILED <short reason>` and stop. No prose, no markdown,\nnothing before or after that final line.\n\n## Source cwd\n\nThe transcript starts with a `[SESSION_CWD]: <path>` header (emitted\nby `extract_session.sh`). Pass it through as `--cwd <path>` on writes\nso the row's scope defaults to *where* it happened (the project root for a coding\nsession, the home dir for a casual chat). If the header is missing,\nomit `--cwd` entirely — never guess.\n\nFile v2.4.0:references/routing-rules.md\n\n# Routing rules — what to save, where, and how\n\nThis file is the canonical reference for save decisions. Both\n`SKILL.md` (chat / dashboard / scan modes) and the dream `mission.md`\n(nightly extractor) Read this when making save / dedup choices.\n\nThe principle: **memory grows with genuinely durable signal; drift\ngets reconciled.** Net value goes up over time; row count alone is not\nthe measure.\n\n## The durability test — both questions must pass\n\n> 1. Would this still be true 6 months from now, in a totally different project?\n> 2. Would a future agent, starting cold, make better predictions about what this user wants and how they work because this memory exists?\n\nIf both pass and the fact is about the **person** → core\n(`ling-mem add ... --tier core`).\nIf both pass and it's not about the person (goal, preference, decision,\ncross-project learning) → long-term (`tier=semantic`, written\nexplicitly — an omitted tier lands episodic), scoped to the directory it\nis about (`scope`) or `global` when it holds everywhere.\nIf either answer is NO → **skip**. The candidate is not memory.\n**Memory does not write to project files** (`<project>/AGENTS.md`,\n`CLAUDE.md`, source, docs); those are user-curated. If the user wants\nproject-internal knowledge captured there, that's a hand-edit they\nmake to their own file.\n\nThat covers everything: project-internal implementation detail,\nactivity / session-arc, meta-feedback about the memory skill or Linggen\ntooling — all skipped.\n\n## The three save rules\n\n### 1. Don't memorize what lives in workspace files\n\nCode, configs, READMEs, project docs — the agent reads them when it\nneeds them. Memory storing the same content creates a stale copy.\n\n> *\"In repo1, the planner module exposes a facade that returns a\n> context object per tick\"* — **skip.** The agent will read the planner\n> sources next time it matters. Memory does not auto-write to the\n> project's `AGENTS.md` either; the user authors that file by hand if\n> they want the rule there.\n\n### 2. User-stated preferences need a confidence gate\n\n- **Save** — user is correcting agent behavior with commitment language\n  and cross-project reach:\n  > *\"I want the agent to always keep UI and server aligned, don't leave\n  > one half-done into the next task.\"*\n\n  Record as `preference`.\n\n- **Skip** — single architectural call, true today and possibly reversed\n  next month:\n  > *\"We should decouple layer 1 from the core engine.\"*\n\n  Belongs in design notes / PR description / project AGENTS.md, not\n  cross-project memory.\n\n- **Synthesize at retrieval, not extraction** — when many similar\n  utterances accumulate (*\"split this module\"*, *\"factor out Y\"*,\n  *\"decouple X\"*), the extractor still appends each one as its own row.\n  It does **not** mint a higher-order rule. Synthesis happens live: when\n  retrieval pulls several rows on the same theme, the agent reconciles\n  in prose — the user sees the generalization and corrects it.\n\n### 3. User-only knowledge — record, then maintain\n\nFacts only the user can supply: life context, history, relationships,\ndates, equipment, the people and animals around them.\n\n- **Stamp ages relative to a date.**\n  > *\"I have a 3-year-old cat\"* → save as *\"User has a cat, age 3 as of\n  > 2026-04-27\"*, not *\"the cat is 3 years old\"*. Without the as-of\n  > date, \"3 years old\" silently rots into \"still 3 years old\" forever.\n\n  Record only what the user said. Don't invent a name, breed, or any\n  other detail to make the entry feel complete — fabricated specifics\n  mislead every future retrieval.\n\n- **Append at extraction; reconcile at retrieval.** When the user\n  revises a fact, append a new timestamped row — don't overwrite the\n  existing one. Reconciliation happens at read time: when multiple\n  matching rows surface, the agent merges them in the response,\n  ordered by timestamp, and the user sees the synthesis live.\n\n  > Stored: *\"User has a cat\"* (2024). Later: *\"When I relocated, I\n  > left the cat with a friend\"* (2026). Retrieval surfaces both; the\n  > agent renders *\"From memory: you had a cat that you left with a\n  > friend during your 2026 relocation.\"*\n\n  Stale rows are removed only by an explicit user instruction\n  (*\"forget that I have a cat\"*). The extractor never picks a winner\n  and never marks one row as superseding another — that's destructive\n  judgment reserved for the live agent + user.\n\n## What NOT to save\n\n| ❌ Wrong | Why | What to do |\n|:---|:---|:---|\n| `\"User is leading X feature\"` | Activity, not identity | Skip. Git log records it. |\n| `\"Agent fixed an issue in src/foo.rs\"` | Bug fix, not cross-project wisdom | Skip. Commit message records it. |\n| `\"In repo1, function X does Y\"` | Project-internal implementation detail | Skip. The agent will read the source next time. Memory does not write to `AGENTS.md`. |\n| `\"Always run npm build after UI changes\"` | Project convention | Skip. If the user wants this rule in `<project>/CLAUDE.md`, they hand-edit it themselves. |\n| `\"User decided the dashboard wording should be 'Scan Today'\"` | Meta-feedback about the memory skill itself | Skip. Code change is the artifact. |\n| Two candidates restating the same fact | Dedup failure | Search + update the clearer one (mechanical rephrase only) |\n| Inferred preferences (*\"user seems to prefer Y\"*) | No explicit statement | Skip — ask if it matters |\n| Single architectural opinion (*\"we should decouple X\"*) | Rot-prone | Skip. Memory does not author project files; user-curated `AGENTS.md` is the right home if anywhere. |\n| The user's API key / password / git remote with embedded PAT | **Never store secrets at any layer** | Skip the credential. Memory does not write the gotcha to a project file either — the user hand-edits if they want it there. |\n\nRule of thumb — **for core/long-term writes**: if the entry reads as\n*\"true about this person in any context\"*, it's right. If it reads as\n*\"what they worked on this week\"* or *\"how this specific project\nworks\"*, it doesn't belong in the long-term tier — but project-scoped\nmilestones, decisions + reasoning, and run learnings still go to\n**episodic staging** (see SKILL.md); the dream pass judges promotion.\nOnly secrets and file-re-derivable content are dropped outright.\nMemory does not write to project files; the user authors those by hand.\n\n## Maintenance — fix when you see it; ask when unsure\n\nMemory hygiene is a hard floor for every memory pass — live chat or a\ndream run: when you see drift, fix it in the same pass. Don't\naccumulate it. The only split is whether you ask first or act silently\n(a headless dream run can't ask — it defers).\n\n### Mechanical maintenance — fire-and-forget\n\nPure rule application. No LLM judgment, no asking.\n\n| Operation | Where | Why |\n|:---|:---|:---|\n| Append a new row | Anywhere | Pure additive |\n| Exact-content dedup at write (binary `insert_with_dedup` rejects identical content) | Binary | Pure equality check |\n| Cross-tier exact-content dedup on `add` (HTTP path) | Daemon | Equality check + tier-rank merge |\n| Evict past-TTL episodic on remembered days (`sweep`) | `dream` | Mechanical forget — only touches rows a remember pass already judged |\n\n### Semantic maintenance — silent when confident, AskUser when not\n\nThese need LLM judgment, available on every memory pass (live chat,\nper-turn capture, the `dream` consolidation). The line between silent\nand ask-first is **confidence**, not \"live vs offline\" — except that a\nheadless `dream` (no user present) can't ask, so it **defers** a\ncontradiction (leaves the candidate in episodic) rather than guessing:\n\n| Operation | Silent if… | Ask if… |\n|:---|:---|:---|\n| Dedup two rows that mean the same thing | Same value, near-identical phrasing, same `scope` | Different scopes, different timestamps, or any value drift between them |\n| Resolve a contradiction (same subject, incompatible values) | **Never silent.** Always ask. | Always |\n| Generalize utterances into a \"user always X\" rule | **Never.** Append individual utterances; live retrieval surfaces patterns. | — |\n| Merge distinct facts into one synthesized story | **Never.** They're distinct; append both. | — |\n\n**How to ask** depends on the host (`SKILL.md` → *Memory hygiene*):\nLinggen's `AskUser` engine tool, Claude Code's `AskUserQuestion`, or\nplain chat text + numbered options when neither exists.\n\n**Bulk forget by filter** is user-initiated only. The model can iterate\n`search` → `delete` for small sets when explicitly asked.\n\n### Hard rules — what extraction must NEVER do\n\n- Never delete a `semantic` row **silently** to resolve a contradiction.\n  Ask first via AskUser; on the user's pick, write the winner with\n  `ling-mem add \"<winner>\" --type ... --from ... --replace <id>\n  --user-directed` (losers archived, one atomic call). Silent deletion\n  is the floor violation.\n- Never write a contradicting pair as separate atoms hoping live recall\n  resolves it later. That's drift accumulation — the cost we're trying\n  to stop paying. Ask now.\n- Never merge two distinct rows into one synthesized story. If they're\n  distinct facts (not phrasings of one fact), append both — they're\n  not duplicates.\n- Never mint a \"user always X\" generalization across rows. Append the\n  individual utterances; live retrieval surfaces the pattern.\n\n## Routing summary by tier\n\nWhen a candidate emerges, route to one of two tiers or drop it.\n**Memory does not write to project files** (`<project>/AGENTS.md`,\n`CLAUDE.md`, source, docs); those are user-curated.\n\n| tier | When | Action |\n|:---|:---|:---|\n| `core` | Universal about the person (no scope, never a preference) | `ling-mem add \"...\" --tier core --type fact ...` |\n| `semantic` | Intent / decision / preference / learning | `ling-mem add \"...\" --tier semantic --type <type> [--scope <dir>\\|--global] [--summary \"...\" --indexed] ...` |\n| (skip) | Project-internal implementation detail / activity / session-arc / meta-feedback | Drop. The agent reads code or user-authored project files when needed. |\n\nMost candidates skip. The core tier grows slowly by design — a noisy\n`core` set pollutes every session's prompt. Long-term stays dense;\nwe'd rather miss 3 saves than force the user to curate 30 low-signal\nrows.\n\n## Scope, summary, index\n\n- **Scope** — the absolute directory a row is about. The host stamps the\n  session cwd (request `cwd`) as the default; pass `scope` (one of the session's\n  \"Memory scopes here\" candidates) when the row is about another\n  directory; `global: true` when it is about the person. Core has none.\n- **`indexed`** — the row loads at every session start under its\n  scope. For standing rules the user states; the dream also sets or\n  clears it on its own when sure.\n- **Summary** — one line, ≤ 80 chars, what the row is for. Matters only\n  on indexed rows: the index shows it, or the content's opening when\n  there is none.\n\n## Outcome field — only for action-flavored types\n\n`outcome: positive | negative | neutral` is meaningful **only** for\n`tried` / `fixed` / `decision`. Omit entirely for `fact` /\n`preference` / `learned` / `built` — setting `outcome: neutral` as a\nplaceholder for those types is visual noise on the dashboard and a\nsign of extractor drift.\n\nFile v2.4.0:doc/shared-memory-design.md\n\n---\ntype: design\nreader: Coding agent\nguide: |\n  Design for the `shared-memory` skill. Says what to build and the open\n  decisions that gate it. Aligns to — never duplicates — the canonical\n  contract in linggen/doc/memory-spec.md and the binary in linggen-memory.\n  Brief. No justification prose beyond what an open decision needs.\n---\n\n# shared-memory — design\n\n> **Status: content workstreams B/A/C/D/E LANDED (2026-05-20).**\n> Workstream F (distribution migration: rename roll-out + version-skew\n> handshake) is the remaining work and ships separately. Canonical\n> memory contract is still `linggen/doc/memory-spec.md`; binary\n> contract is `linggen-memory/doc/tech-spec.md` + `DESIGN.md`. This\n> doc only covers the *third-party host bridge*. §7 resolved; §8\n> version-skew still gates the release.\n\n## 1. What this is\n\nThe cross-agent bridge skill for **non-Linggen hosts** (Claude Code,\nCodex, OpenClaw). One user memory, shared across every AI tool.\n\nThree distinct names — keep them straight everywhere:\n\n- **`ling-mem`** — the binary. LLM-free mechanical store (semantic +\n  episodic tables, `search_scored` / `insert_with_dedup` / `--supersedes`\n  / `evict`). Name unchanged by this rename.\n- **built-in memory** — the Linggen engine's encoder + `dream` mission.\n  Linggen-only.\n- **`shared-memory`** — this skill. A host adapter, **not** a memory\n  system and **not** a separate store. Same `~/.linggen/memory/\n  memory.lancedb` as Linggen.\n\n## 2. Coexistence model (affirmed — do not re-litigate)\n\n- **One store**: `~/.linggen/memory/memory.lancedb` (semantic +\n  episodic), owned by the binary, outside the skill bundle.\n- **Per-host wake-encode**: each host encodes **its own** sessions —\n  the Linggen engine for Linggen sessions, this skill for CC / Codex /\n  OpenClaw sessions.\n- **Two dreams, one consolidate/evict contract.** The *Linggen* `dream`\n  mission is **RAG-only** — consolidate + evict over the shared store;\n  it doesn't scan because the engine already holds Linggen's live\n  exchange and encodes it directly (`f915e6b`). The *shared-memory\n  skill* `dream` does **scan + process**: a non-Linggen host hands the\n  skill no live exchange (it doesn't own the host's agent loop), so the\n  skill's per-host in-host encode reads *its own host's* session files\n  (CC/Codex/OpenClaw) → extract → judge/write → then the *same*\n  consolidate + evict. Same back-half; only the skill adds the scan\n  front-half.\n- **This is the coexistence model, not an exception.** `f915e6b` scopes\n  \"no log-scraping\" to the *engine* (it has a live exchange) and to\n  Linggen not reaching into other tools. A skill reading *its own\n  host's* sessions is the sanctioned **per-host in-host encode** —\n  cross-tool memory still emerges from the one shared store; no host\n  reads another tool's logs.\n- **Binary/judgment split unchanged**: binary = mechanical; judgment\n  (encode filter, reconcile, consolidate) = the host LLM. On Linggen\n  that is the engine; on other hosts the host's own model drives the\n  skill's prompts.\n\n## 3. Rename (`ling-mem` skill → `shared-memory`)\n\nDistribution-level change — **needs its own migration plan** (separate\nartifact). Surfaces touched: skills repo dir, `vendor/skills` submodule,\n`~/.linggen/skills`, `install.sh`, install URLs, ClawHub listing\n(currently soft-deleted), `linggen-vscode` (consumes the bundle via\n`install-ling-mem.sh`), docs, existing installs.\n\n- Binary name and `provides: [memory]` **unchanged** — only the skill\n  renames.\n- Old slug keeps a redirect where the channel supports it (e.g.\n  `clawhub rename`).\n- Existing installs: `install.sh` does an idempotent in-place rename of\n  `~/.linggen/skills/ling-mem` → `shared-memory`. Memory data is under\n  `~/.linggen/memory` (separate) — never touched.\n\n## 4. `/shared-memory dream` — scan + process\n\nThe skill is the **per-host in-host encoder** for non-Linggen hosts. It\nisn't handed a live exchange (it doesn't own the host's agent loop), so\nit encodes by reading *that host's own* session files. This is the\ncoexistence model's per-host wake-encode — not Linggen reaching into\nother tools, and not in tension with `f915e6b` (engine-scoped).\n\n`/shared-memory dream` = scan → extract → judge/write → consolidate +\nevict.\n\n**Scan + extract (script, token-cheap).** A script parses the host's\non-disk transcripts, strips tool noise, hash-dedups, secret-filters —\nno LLM, so no token cost on raw logs. Verified sources:\n\n| Host | Path | Format |\n|:--|:--|:--|\n| Claude Code | `~/.claude/projects/<enc-cwd>/<uuid>.jsonl` | JSONL |\n| Codex | `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl` (+ `archived_sessions/`, `history.jsonl`) | JSONL |\n| OpenClaw | `~/.openclaw/logs/` (markdown memory under `~/.openclaw/memory/`) | probe at impl |\n\nPer-source watermark (mtime/offset) → a re-run never re-processes a\nhandled transcript.\n\n**Judge + write (host LLM) → then consolidate + evict.** The host LLM\napplies the engine contract verbatim: memory-spec §2/§4 exclusions +\nwrite-time usefulness bar + salience routing (explicit → semantic,\nincidental → episodic). Writes go through the daemon; dedup is\nexact-content only (binary `88da2ae`); no `supersedes` (CRUD-only,\n`bfa1bd5`). Then the same consolidate + evict the Linggen `dream` runs\n(§7b — one shared contract).\n\n**Floors**: secrets stripped in the script before the LLM sees them\n(memory-spec §3 r6); never store file-derivable content (§4 r1).\n\n## 5. Interface\n\nPrimary surface = **chat slash commands**, thin wrappers over the one\ndaemon: `/shared-memory add | search | dream | delete | update`.\n- `add | search | delete | update` — daemon passthrough.\n- `dream` — scan + extract + judge/write + consolidate + evict (§4).\n  User-invoked: the user is the scheduler on hosts with no mission\n  system. **Capture happens here (the scan)** — not via a continuous\n  encode hook.\n\nOne *optional* hook: `SessionStart` → recall-inject (query the daemon,\ntoken-budgeted) so memory surfaces without the user asking. Installed\nby `install.sh`, never hand-edited; per-host wiring verified at impl.\n\n## 6. UI duality\n\nKeep the current web app + workflow. One codebase, host-detected mode:\n\n- **On Linggen** — AI-native app (skill app surface, dashboard /\n  `PageUpdate`).\n- **On other hosts** — standard skill (CLI / chat, no `PageUpdate`\n  canvas) + the daemon-served data browser at `127.0.0.1:9528`.\n\nRestripe today's Linggen-coupled bits (the `implements:` block,\ndashboard mode) to **host-detected**, not hardwired.\n\n## 7. Resolved decisions\n\n**(a) No-Linggen consolidation — RESOLVED.** `dream` is one contract,\ntwo triggers: an autonomous mission on Linggen; the manual\n`/shared-memory dream` on hosts with no scheduler (the user is the\nscheduler). The skill's `dream` runs the full pass — scan → host-LLM\njudge → consolidate + evict — so third-party hosts get real promotion\nand eviction, not a degraded cache. No autonomous skill scheduler, no\nsecond memory system.\n\n**(b) Reconcile reach — RESOLVED.** The consolidate/evict + Reconcile\ncontract lives in one shared place — `agents/ling-mem.md` (memory-spec\n§2) — reused verbatim by both the Linggen mission and the skill\n`dream`. Judgment is whatever LLM hosts the run (engine LLM on Linggen,\nhost LLM on CC/Codex/OpenClaw); the binary stays mechanical. Single\nsource ⇒ no drift.\n\n## 8. Ops risks — de-risk before ship (blocking)\n\n- **Concurrent writers — NOT a risk (already handled).** There are no\n  independent `ling-mem` processes. One `ling-mem` **daemon** is the\n  sole writer; every host (CC, Codex, Linggen, the skill) talks to that\n  single server, which serializes writes. The skill **already checks\n  for a running `ling-mem` server and uses it** — implemented, not a\n  TODO. Nothing to verify here.\n- **Version / schema skew.** Two installers (Linggen vs skill\n  `install.sh`) updating `ling-mem` independently → schema skew on one\n  shared store under the **no-forward-migration** policy → wipe-and-\n  fresh data loss. Need a single source of truth for the installed\n  binary version, or a version handshake that refuses rather than\n  corrupts.\n\n## 9. Implementation pointers\n\n- **Do not duplicate** the contract. Reference `linggen/doc/\n  memory-spec.md` (memory rules) and `linggen-memory/doc/tech-spec.md`\n  + `DESIGN.md` (CLI/schema).\n- **Drift is the sustainability risk**: the skill's durability/salience\n  instruction prose must derive from the same spec the engine uses, not\n  be hand-restated (a stale skill misrepresents the product — the\n  failure mode just cleaned up on ClawHub).\n- **Edit order**: standalone `skills/` first → `vendor/skills`\n  submodule + `~/.linggen/skills` (never the reverse). Hook/install\n  wiring lives in `install.sh` only.\n\n## 10. Sequence\n\nde-risk §8 version-skew only (concurrency already handled — single\ndaemon) → write the rename distribution plan → implement the skill\n`dream` (scan + extract + host-LLM judge + consolidate/evict, contract\nfrom `agents/ling-mem.md`) + the optional recall hook → restripe UI\nduality. **Never rename-first.**\n\n## 11. Update plan\n\nStatus snapshot 2026-05-20: B/A/C/D/E shipped on `skills/main` (local\nworking tree, not yet pushed). F deferred to its own distribution plan.\n\n1. **B — Stale store facts** ✅ LANDED.\n   - Embedder string: 1024-dim Qwen3-Embedding-0.6B everywhere\n     (README.md, SKILL.md, install.sh-written CLAUDE.md).\n   - Path: `memory/memory.lancedb/` (semantic + episodic) in README.md.\n   - `supersedes` removed from SKILL.md Consolidate section + all\n     routing-rules.md tables. Reconcile = append + read-time +\n     explicit user delete.\n   - Dedup language switched to exact-content-at-write-time (binary\n     `insert_with_dedup`); fuzzy moved to `dream` / live agent.\n\n2. **A — Core: markdown → `tier=core`** ✅ LANDED.\n   - `identity.md` / `style.md` retired from the two-tier model.\n     SKILL.md, routing-rules.md, dashboard.md all switched to\n     `ling-mem add … --tier core` / `ling-mem list --tier core`.\n   - `permission.warning` frontmatter updated.\n   - `install.sh` `seed_core_memory()` no longer touches markdown\n     files; `configure_claude_md` block no longer `@`-imports\n     identity/style.\n\n3. **C — `dream` flow** ✅ LANDED.\n   - SKILL.md Modes: \"Scan\" mode → \"Dream\" mode; new slash-command\n     table (`add | search | list | delete | update | dream`).\n     Consolidate section reworked: automatic in dream, interactive\n     destructive edits only with user present.\n   - `references/scan-flow.md` deleted; replaced by\n     `references/dream-flow.md` (Phases 1–5: scan → script-extract\n     → host-LLM judge+write → consolidate+evict → persist+report).\n     dashboard.md re-routed to it.\n   - `references/extractor-prompt.md` rewritten as a single-source\n     pointer to engine `agents/ling-mem.md` ENCODE phase (anti-drift\n     marker included).\n   - `scripts/collect_sessions.sh` + `extract_session.sh`: added\n     Codex + OpenClaw sources, per-source mtime watermark\n     (`--watermark <file>`), defence-in-depth secret filter, byte\n     cap. Both still syntax-clean.\n\n4. **D — Recall hook** ✅ AUDITED. `install.sh` already wires\n   `UserPromptSubmit` → `recall.sh` → `ling-mem search \"$prompt\"\n   --limit 8 --min-score 0.30 --format json` → `head -3`. 3s timeout,\n   cwd-aware project filter, silent failure, env-var disable. Hits\n   `/api/memory/search` via the daemon. Token-budgeted.\n\n5. **E — Linggen-coupling restripe** ✅ LANDED. SKILL.md body\n   opening + dashboard-mode prose now host-detected (`PageUpdate`\n   capability gate) rather than naming Linggen / Claude Code\n   directly. `implements:` and `permission:` frontmatter blocks\n   remain Linggen-only (opt-in by host, harmless to others) — the\n   right shape.\n\n6. **F — Distribution migration** ⏳ DEFERRED (own plan, §3).\n   `install.sh` existing-install dir migration (`~/.linggen/skills/\n   ling-mem` → `shared-memory`), hook-marker idempotency on rename,\n   scoped release tags, ClawHub re-publish, `vendor/skills` +\n   `~/.linggen/skills` sync. Skill-bundled `assets/mission.md` is now\n   dead code (engine ships its own dream mission); removal lives in\n   F as well. The `ling-mem` **binary** stays unchanged throughout.\n\n**Ship precondition (still open):** resolve §8 version-skew —\nsingle-source the installed-binary version or refuse-on-mismatch —\nbefore any release; shared-store corruption under no-migration is\nunrecoverable. Content workstreams above can land independently of\nthis; the release that brings them to users is gated on F + §8.\n\nFile v2.4.0:skill-card.md\n\n## Description:\n\nProvides durable cross-agent memory and optional browser and X session control through local tools.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[linggen](https://clawhub.ai/user/linggen)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and other agent users use Linggen to save and retrieve durable preferences and decisions across sessions and hosts, and to control an authorized browser or read their logged-in X session.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Persistent memory may retain sensitive personal facts without an explicit save request.\n\nMitigation: Prefer explicit saves, avoid secrets, and regularly review and delete stored memories.\n\nRisk: Scan and dream workflows can read prior agent transcripts.\n\nMitigation: Run these workflows only when authorized to read the affected sessions.\n\nRisk: Recalled memories can reach the configured LLM provider through agent prompts.\n\nMitigation: Store only information appropriate to share with that provider.\n\nRisk: First-run installation and upgrades can introduce powerful local binaries.\n\nMitigation: Review and approve installation and upgrades before running them.\n\nRisk: Browser control can act in a logged-in session, and usage pings may be unwanted.\n\nMitigation: Use browser permissions deliberately and disable telemetry with the documented no-telemetry file if desired.\n\n## Reference(s):\n\n- [Linggen on ClawHub](https://clawhub.ai/linggen/skills/linggen)\n- [Linggen homepage](https://linggen.dev)\n- [Memory routing rules](references/routing-rules.md)\n- [Dream workflow](references/dream-flow.md)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Guidance, Shell commands, Configuration instructions]\n\n**Output Format:** [Markdown with inline shell commands]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May include recalled memory and browser or X session information.]\n\n## Skill Version(s):\n\n2.4.0 (source: ClawHub release)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v2.4.0:LICENSE\n\nMIT No Attribution\n\nCopyright (c) 2026 Linggen\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nArchive v2.3.2: 17 files, 67899 bytes\n\nFiles: doc/shared-memory-design.md (12653b), LICENSE (906b), README.md (4928b), references/condense-flow.md (4756b), references/dream-flow.md (10982b), references/extractor-prompt.md (7537b), references/routing-rules.md (11146b), scripts/bootstrap.sh (2910b), scripts/collect_sessions.sh (11508b), scripts/collect.sh (3018b), scripts/extract_session.sh (13114b), scripts/install-bin.sh (9783b), scripts/install-engine.sh (16754b), scripts/scan.sh (8430b), skill-card.md (2625b), SKILL.md (32504b), _meta.json (126b)\n\nFile v2.3.2:SKILL.md\n\n---\nname: linggen\ndescription: >-\n  Linggen — durable cross-host memory plus browser control, over two\n  local MCP servers: `ling-mem` for memory, the Linggen engine for\n  browser, X and agents. Memory: three-tier model (core + long-term +\n  episodic staging) of who the user is, not a log of what was done;\n  same `ling-mem` daemon and store in Claude Code, Codex, and\n  OpenClaw, and reachable over the LAN from a second machine\n  (`/linggen:config`). Browser: agent control of the user's own Chrome\n  with per-site permission prompts, and logged-in X session reads.\nlicense: MIT-0\nhomepage: https://linggen.dev\nallowed-tools:\n  - Read\n  - Write\n  - Edit\n  - Bash\n  - Glob\n  - Grep\nuser-invocable: true\n\n# ClawHub clawdis metadata — declares dependency on the ling-mem CLI binary.\n# v0.4.0 will add `install: [{kind: brew, formula: ling-mem, tap: linggen/tap}]`\n# once the Homebrew tap exists; for now users install the CLI manually via the\n# install.sh one-liner shown in the body. Other hosts ignore this block.\nmetadata:\n  clawdis:\n    homepage: https://linggen.dev\n    primaryEnv: cli\n    emoji: 🧠\n    os: [darwin, linux]\n    requires:\n      bins: [ling-mem]\n---\n\nYou are **Ling**, operating inside the linggen skill — the user's\ndurable cross-session memory (plus browser control, below). Memory is\nyour surface: you read and write the user's permanent biography.\n\n**Interface order:** prefer the `memory_*` MCP tools from the `linggen`\nserver (`memory_search`, `memory_add`, `memory_get`, `memory_update`,\n`memory_delete`, `memory_list`) — they proxy the same ling-mem daemon\nwith the same semantics. When the MCP server is unavailable (daemon\ndown, headless host), fall back to the **`ling-mem` CLI** via `Bash`;\nevery command in this document works on both paths. Same daemon, same\nstore, same semantics across every host that loads this skill.\n\n*Part of the [Linggen](https://linggen.dev) agent platform.*\n\n**Skill resources** live alongside this `SKILL.md`. When the instructions\nbelow say `Read references/X.md` or `Bash scripts/X.sh`, resolve those\npaths relative to this skill's directory — `${CLAUDE_PLUGIN_ROOT}/skills/linggen/`\non Claude Code, `${PLUGIN_ROOT}/skills/linggen/` on Codex.\n\n> **Memory is how the agent grows up.** Not a log of what was done — a\n> deepening model of *who the user is*. A fact earns its place only if\n> a future session, on any project months from now, would make better\n> predictions about this user because the fact exists. Focus on the\n> user, not the task.\n\n## First use — ensure the Linggen binaries are installed\n\nThis skill has two required binaries: **`ling-mem`** (the memory daemon —\nserves `memory_*` on `127.0.0.1:9528/mcp`, and the CLI every Bash-only\nchannel shells out to) and **`ling`** (the Linggen engine — serves\n`browser_*`, `x_*`, `agent_run` and the dream tools on\n`127.0.0.1:9527/mcp`). Each tool is served in exactly one place: the\nengine does not proxy memory. The Claude Code / Codex plugin's\nsession-start hook installs both automatically (the engine in the\nbackground, disclosed in the session context) — unless `~/.linggen/client.json`\npoints off-machine, in which case this host is a *client* of another machine's\nLinggen and installs nothing. `/linggen:config` is how that is set. On channels without hooks\n(skills.sh, ClawHub, manual), **you install them — run these checks\nbefore your first op; each is a no-op when already satisfied:**\n\n```bash\nbash scripts/bootstrap.sh\n```\n\n(Resolve the path relative to this skill's directory, as above. The script\nchecks for both binaries and is a fast no-op when they're present; both\ninstallers ship inside this bundle and the bootstrap forbids remote-script\nfallbacks, so no remotely fetched script is ever executed. What comes over\nthe network are the release binaries: `ling-mem` SHA-256-verified; the\nengine and bun binaries over TLS from GitHub releases, checksum\nverification on the roadmap. It also labels the install's distribution\nchannel from its own on-disk location — a local marker file only, nothing\nphones home.)\n\n**Ask the user before the first-ever install** — one line is enough:\n\"Linggen needs its two local binaries (`ling-mem` ~30MB SHA-verified,\nthe engine ~100MB, both to `~/.local/bin`) — install now?\" Run the\nscript only on their yes. When both binaries are already present the\nscript is a silent no-op — run it without asking. If either install\nfails (offline, no writable bin dir), tell the user, then continue —\nmemory works with `ling-mem` alone. To update later: `ling-mem upgrade`;\nthe engine self-updates via `ling update`.\n\n## Interface — the `ling-mem` CLI\n\nThis skill is a **CLI wrapper around the `ling-mem` HTTP daemon**.\nEvery memory operation goes through `Bash ling-mem <verb>`; the CLI\nauto-starts the daemon on first use. Same backend on every host —\nClaude Code, Codex, OpenClaw — so the calling syntax doesn't\nchange when you switch agents.\n\n| Op | CLI |\n|:---|:---|\n| Search | `ling-mem search \"...\" [--context ...] [--limit N]` |\n| Get    | `ling-mem get <id>` |\n| List   | `ling-mem list [--type ...] [--day YYYY-MM-DD] [--limit N] ...` |\n| Add    | `ling-mem add \"...\" --type <t> --from <user\\|agent\\|derived> [--context ...] [--tag ...] [--source-session <id>]` — pass the host session id on live captures so a later `scan` of the day skips sessions that already contributed |\n| Update | `ling-mem edit <id> [--content ...] [--context ...] [--tag ...]` (or the back-compat alias `ling-mem update <id> ...`) |\n| Delete | `ling-mem delete <id> --yes` |\n| Days   | `ling-mem days [--undreamed]` — per-day verb flags (scanned / dreamed) + `first_unscanned` / `first_undreamed`; `--undreamed` = the dream worklist, oldest first |\n| Stamp  | `ling-mem remember-day <date> --judged N --promoted K` — mark a day judged after a remember pass |\n| Sweep  | `ling-mem sweep [--dry-run]` — the forget stage: evict judged episodic rows past TTL; never touches un-judged rows |\n| Chains | `ling-mem chains [--kind cited\\|marker] [--derived-only] [--limit N] [--offset N]` — condense scan: stale same-subject chains in long-term memory (read-only; judgment is yours) |\n| Issues | `ling-mem issues [--status open\\|all]` — the review queue: items a dream audit could not solve with confidence (facts only; you are the solver — see the Solve mode) |\n| Close issue | `ling-mem issue-resolve <id> [--outcome resolved\\|dismissed] [--note \"...\"]` — close one review item after solving it |\n\n**Anchor relative time in every saved row** — substitute today's date in before writing (e.g. if today is 2026-07-07: \"turned 3 last month\" → \"turned 3 in 2026-06, as of 2026-07-07\"); relative words rot silently.\n\n**Always pipe CLI list/search/get output through `jq -c 'del(.vector)'`** —\nraw output includes 1024-dim embedding floats (Qwen3-Embedding-0.6B) that blow up context.\n\n```bash\nling-mem search \"node 22 quirk\" --limit 5 --format json | jq -c 'del(.vector)'\n```\n\n## The three tiers\n\n| Tier | Storage | When |\n|:---|:---|:---|\n| **Core** | Rows with `tier=core` in the `semantic` table | Narrow universals about the **person** — name, role, location, timezone, languages, pets / family. Always-loaded set; the host injects them at session start. Keep tight. |\n| **Long-term** | Rows with `tier=semantic` (default) | Everything else durable: long-term goals / vision, cross-project preferences, decisions whose reasoning is the retrieval value, cross-project tech gotchas. Retrieved on demand. **State + lessons, never events** — test: strip the date and commit hash; still useful in three months? If not, episodic. |\n| **Episodic** | The `episodic` staging table | **Per-turn working capture** — append uncertain-durability signal here each turn (fast, append-only, no search-first): `ling-mem add \"<content>\" --episodic`. Episodic is the user's **short-term memory**: the dream pass *remembers* each day (promotes durable rows to core/semantic, deletes nothing), and the *forget sweep* (`ling-mem sweep`) ages out judged rows after the TTL. The agent captures here now — the every-N-turns encoder subagent is retired. |\n\nCore and long-term share the `semantic` table — only the `tier` column\ndiffers. Episodic lives in its own table at\n`~/.linggen/memory/memory.lancedb/episodic.lance`.\n\n**Write the tier explicitly when adding to core:**\n\n```bash\nling-mem add \"<content>\" --type fact --from user --tier core\nling-mem list --tier core --limit 100 | jq -c 'del(.vector)'\n```\n\nOmit `--tier` to default to `semantic` (long-term).\n\n**If a candidate doesn't clearly fit core or long-term but might matter\nlater → episodic** (`--episodic`; staging, the dream pass sorts it\nout). **Project-scoped is welcome here — episodic is staging, not\nuser-biography:** capture shipped milestones, decisions + reasoning, and\nnon-obvious run learnings even when they're about one project (e.g.\n*\"Shipped Linggen 1.0\"*, *\"Sanji docking: treat all cost-points\nuniformly\"*). The only hard drops: secrets, and content verbatim\nre-derivable from a file the agent re-reads — store the *decision/learning\nabout* it, never the file body, and Memory never writes to\n`<project>/AGENTS.md`, `CLAUDE.md`, source, or docs.\n\n**Goals and projects → long-term, not core.** *\"User is building Linggen\nas an agent platform\"* is a goal — `tier=semantic` with\n`tags: [\"intent:goal\"]`, not `--tier core`. Core is about the person;\ngoals are about the work. Rule of thumb: progressive-form verbs\n(*\"is building\"*, *\"wants to ship\"*) or a project name → goal →\nlong-term. Names the person (*\"is Liang\"*, *\"lives in Shanghai\"*) →\ncore.\n\n## Durability — what's worth remembering\n\nThree rules decide whether a candidate earns its place. Routing (core\nvs long-term tier) is a separate concern — these rules answer only\n**should this be saved at all?** Memory never writes to project files\n(`AGENTS.md`, `CLAUDE.md`, code, docs); candidates that don't fit core\nor long-term are dropped.\n\n1. **Don't memorize what lives in workspace files.** The agent reads\n   them when needed. Putting the same content in memory creates a stale\n   copy.\n2. **User-stated preferences need a confidence gate.** Save when the\n   user is correcting agent behavior with commitment language and\n   cross-project reach. Skip single architectural calls. Synthesize at\n   retrieval, not extraction.\n3. **User-only knowledge — record, then maintain.** Stamp ages relative\n   to a date (*\"as of 2026-04-27\"*, not *\"3 years old\"*). Append at\n   write; reconcile at read.\n\nFor the full rules, examples, and the mechanical-vs-semantic\nmaintenance split, **Read `references/routing-rules.md`** before making\nnon-trivial save decisions.\n\n## Mid-chat save rules — silent HIGH-SIGNAL auto-save\n\nWhen the user utters one of these in regular chat, save immediately. No\nwidget, no confirmation, no verbose reply — just save and continue.\n\n1. **Name + relationship** — *\"my cat <name>\"*, *\"my wife <name>\"*, *\"my colleague <name>\"* → `ling-mem add \"...\" --type fact --from user --tier core`. Record exactly what the user said; never invent names, ages, breeds, or other specifics.\n2. **Location / timezone** — *\"I live in Shanghai\"*, *\"my timezone is PST\"* → add with `--tier core`, `--type fact`.\n3. **Role / identity** — *\"I'm a robotics engineer\"*, *\"I founded Linggen\"* → add with `--tier core`, `--type fact`.\n4. **Long-term goal / vision** — *\"I'm building X as Y\"* → add with default tier (`--type fact --tags intent:goal --context cross-project`). **Do NOT** use `--tier core` — goals belong in the long-term tier.\n5. **Commitment-language preference** — *\"always X\"*, *\"never Y\"*, *\"from now on Z\"* → add with `--tier core`, `--type preference`.\n\nDetect these patterns semantically, not lexically — works in any\nlanguage. *\"我的猫叫 …\"*, *\"以后别再 …\"* trigger the same routing.\n\nSkip activity descriptions, project-specific technical facts (drop —\nthe agent will read the code), inferred preferences, opinions without\ncommitment.\n\n**Explicit user imperatives — act immediately, no pre-confirmation:**\n- *\"remember X\"* / *\"记住 X\"* → save; reply *\"Saved.\"*\n- *\"forget X\"* → search + delete; reply *\"Deleted: <content>.\"* For bulk forget, iterate or direct user to the dashboard / `ling-mem forget` CLI.\n- *\"update X to Y\"* → search + update; reply *\"Updated.\"*\n\n## Retrieval is visible — chip every fact you used\n\nWhen you call a memory query and the result shapes your reply, surface\nwhat you used **in the chat text**, with the age of each fact:\n\n> 💭 From memory (3 months ago): User has a cat.\n> 💭 From memory (2 months ago): User lives in Shanghai.\n\nUse **relative time**, dim or warn on facts older than 12 months\n*(may be stale)*, skip the chip for facts you didn't actually use. When\ntwo rows on the same subject surface, reconcile in prose ordered by\ntimestamp — don't silently rewrite or delete.\n\n## Listing & searching memory — single-call recipes\n\nWhen the user asks to list, browse, or search memory — whether via a\nslash command, natural language, or any other phrasing — follow these\nrecipes. **One call per request.** Do not iterate over types, do not\nadd speculative filters.\n\n| User intent (any phrasing) | Make exactly this call |\n|:---|:---|\n| List everything (`/linggen list`, *\"show all memory\"*, *\"list memory records\"*, *\"what's in memory\"*) | `ling-mem list --limit 100 --format json \\| jq -c 'del(.vector)'` — **no filters at all** |\n| List one type (`/linggen list facts`, *\"show my preferences\"*, *\"list decisions\"*) | `ling-mem list --type <type> --limit 100 --format json \\| jq -c 'del(.vector)'` |\n| Search by content (`/linggen search <q>`, *\"do you remember <q>\"*, *\"what do you know about <q>\"*) | `ling-mem search \"<q>\" --limit 10 --format json \\| jq -c 'del(.vector)'` |\n| Single noun like `/linggen cat` or *\"my cat\"* | `ling-mem search \"<noun>\" --limit 10 --format json \\| jq -c 'del(.vector)'` — search, not list |\n| Get a specific row by id | `ling-mem get <uuid> --format json \\| jq -c 'del(.vector)'` |\n\n**FORBIDDEN unless the user explicitly asked for them:**\n- `from` — filters by origin (user / agent / derived). Almost no read query needs this.\n- `outcome` — filters by positive / negative / neutral. Most rows don't carry an outcome at all.\n- Empty strings (`id: \"\"`, `query: \"\"`, `since: \"\"`) — leave the field out entirely.\n- Empty arrays (`contexts: []`) — leave the field out entirely.\n- Iterating types — **do NOT** call list once per type. A single unfiltered `list` returns every row in one round-trip.\n\nIf the user says *\"show me only what I told you\"* or *\"what worked\"*,\nTHEN add `from: \"user\"` or `outcome: \"positive\"` — those are the rare\naudit cases the filters exist for. Otherwise omit them.\n\nAfter the call returns, render results as a table or bullet list\nshowing `type`, `content` (truncate to 80 chars), and a relative\ntimestamp. Skip the id unless the user is about to delete or update.\n\n## When to search\n\nCall a memory search **before answering** when the user's question\ncould connect to past preferences / decisions / gotchas:\n\n- *\"How should I handle X?\"* — look for related preferences / decisions.\n- *\"What did we decide about Y?\"* — search with `type: decision`.\n- *\"Remember when we…\"* — direct retrieval.\n- Recurring operational question — search the project context if you're in a project workspace.\n\nSkip search when the user is asking factual / technical questions with\nno user-specific angle (*\"what does this function do?\"*, *\"explain this\nerror\"*).\n\n## Reading legacy project rows\n\nOlder rows may carry `contexts: [\"project/<name>\"]` from earlier\nversions when project-internal facts were stored in the long-term\ntier. They still\nretrieve normally — include both the project context and `cross-project`\nin your searches when you're in a project workspace:\n\n```bash\nling-mem search \"...\" --context project/<name> --context cross-project\n```\n\nDerive `<name>` as the **single last path component** of the workspace\nroot (no segment concatenation).\n\n**Don't write new `project/<name>` rows.** Project-internal facts that\nfail the durability test get dropped — the agent reads the project's\ncode or its user-curated `AGENTS.md` / `CLAUDE.md` next time. Memory\nneither stores nor authors that content.\n\n## Modes — which references to load when\n\nThis skill enters one of two modes per invocation. **Detect the mode\nfrom the first user message you see in this turn**, then load only that\nmode's references.\n\n| Mode | Detection cue (look at the first user message) | What to load |\n|:---|:---|:---|\n| **Dream** | Message says `/linggen dream` (all undreamed days) or `/linggen dream <YYYY-MM-DD>` (one day). User-triggered — or wired to the host's own scheduler for a nightly pass. | `Read references/dream-flow.md` (the canonical remember/forget runbook) and `references/routing-rules.md`. |\n| **Scan** | Message says `/linggen scan <YYYY-MM-DD>` — stage that day's session logs (backfill), see the verb table. | `Read references/dream-flow.md` (its Scan section) and `references/extractor-prompt.md` (what to stage). |\n| **Solve** | Message says `/linggen solve` — drain the review queue (items a dream audit queued for the user). | The Solve runbook below; `references/routing-rules.md` for write decisions. |\n| **Status** | Message says `/linggen status` — one glanceable block: versions + updates, store size, upkeep. | Nothing extra: the host command carries the full recipe (fetches + render); its data = `memory_dream_status` + `ling-mem status`/`stats` + engine/bridge probes. |\n| **Chat** | **Anything else** — bare `/linggen`, `/linggen list`, `/linggen search foo`, plain `\"show all memory\"`, free-form questions. | Body of this SKILL.md is the entry. `Read references/routing-rules.md` only when making save / dedup decisions. |\n\n**Chat mode is the default.** When in doubt, you are in chat mode.\n\n## Slash commands — `dream` + daemon passthrough\n\n`/linggen <verb>` is the primary surface. `dream` is the\nmemory-consolidation pass (it runs the zero-LLM scan walk itself as\nPhase 0, then judges); the rest map 1:1 to daemon CRUD endpoints.\n**`dream` is the headline verb**: it's the only one where the LLM does\njudgment, and it's what a bare `/linggen` greeting should mention\nfirst.\n\n| Verb | Action |\n|:---|:---|\n| `dream` | **Remember all undreamed days, oldest first, then sweep.** Worklist via `ling-mem days --undreamed`; per day: list its episodic rows → cluster → promote durable signal to semantic → `ling-mem remember-day` stamp. Never deletes; the final `ling-mem sweep` ages out judged rows past TTL. See `references/dream-flow.md`. |\n| `dream <YYYY-MM-DD>` | **Remember one day.** Same procedure, one day. |\n| `scan <YYYY-MM-DD>` | **Stage one day's session logs (backfill).** Run `scripts/scan.sh <date>`; `list --day <date>` the day's existing rows and skip any scanned session whose id is already among their `source_session`s (that's what makes re-scanning safe); encode the remaining keepers into episodic with the day's `occurred_at`; stamp with `ling-mem harvest-day <date>` (scan stamp only — the day stays undreamed and dream judges it later). Nothing new: still stamp, report `CLEAN`. |\n| `add \"<content>\" [--type ...] [--tier core] [--context ...]` | Insert a new memory row. Defaults to `--tier semantic`. |\n| `search \"<query>\" [--limit N] [--context ...]` | Semantic search across `semantic` + `episodic`. |\n| `list [--type ...] [--tier ...] [--limit N]` | Paginated listing. |\n| `delete <id>` | Remove a specific row by id. |\n| `update <id> --content \"<new>\"` | Edit a row in-place (content / contexts / tags). |\n| `solve` | **Drain the review queue** — see the Solve runbook below. |\n| `status` | **Glanceable install status** — binary versions + cached update probes, store size (`ling-mem stats`), and upkeep: `scanned_days`/`dreamed_days`/`total_days` counts, `first_unscanned` / `first_undreamed`, open issues, last run (from `memory_dream_status` or `ling-mem days`). |\n\n### Solve runbook — `/linggen solve`\n\nThe review queue holds what a dream audit could NOT solve with\nconfidence: uncertain merges (`chain`), status claims likely overtaken\nby the world (`stale-status`), and conflicts needing the user's pick\n(`contradiction`). The daemon only bookkeeps; **you are the solver**,\nwith this session's model, tools, and user.\n\n1. **Back up, then list.** `ling-mem export` first (one snapshot per\n   solve session), then `ling-mem issues --format json` (or the\n   `memory_issues` MCP tool). Empty → say so, done.\n2. **Per item, gather evidence at solve time.** Fetch the rows\n   (`ling-mem get <row_id>`). For `stale-status`: check the WORLD —\n   `git log --oneline --since=<row date>` in the named repo, working\n   tree, file existence. The row was written before the world moved;\n   your evidence decides what's true now.\n3. **Apply the confidence rule.** Evidence is conclusive AND every\n   affected row is your own note (`from=derived`) → solve directly, no\n   ask: one `memory_add` with `replace_ids` (CLI: `add --replace <id>`)\n   writing current truth. Evidence is ambiguous, OR any affected row is\n   user-voice (`from=user`) → **ask the user, ONE item per question**\n   (AskUserQuestion on Claude Code; plain numbered options elsewhere) —\n   never batch the whole queue into one wall of questions. User-voice\n   fixes carry `user_directed:true` after their answer.\n4. **Close as you go.** After each item:\n   `ling-mem issue-resolve <id> --outcome resolved --note \"<what you did>\"`\n   (or `memory_issue_resolve`). Not worth fixing → `--outcome dismissed`.\n5. **Report one line per item** — `SOLVED <id> <what changed>` /\n   `DISMISSED <id> <why>` — then a closing count.\n\n### Chat-mode rules\n\nThe user is reading text in a conversation panel:\n\n- Answer the user's actual question in plain prose or a small markdown\n  table. If the user asked to list memory, run the recipe in\n  *Listing & searching memory* above and render the result inline.\n- For hands-on row-level CRUD, point the user at the daemon-served\n  data browser at `127.0.0.1:9528` (run `ling-mem start` first).\n\n## Memory hygiene — see it, solve it\n\n**Hard rule, applies everywhere (live chat, per-turn capture, dream):**\nwhoever surfaces garbage owns it in that moment — **resolve it in the\nsame pass, don't defer**. There is no cleanup queue. Garbage in memory\npoisons every future retrieval; \"leave it for later\" is how 7\nword-count rows accumulate.\n\n**Merge authority follows voice.** Your own notes (`from=derived` —\n`built`/`fixed`/`tried`/`learned`) are your notebook: merge, rewrite,\nretire freely, no prompt. Rows in the user's voice (`from=user` —\npreference/decision/identity) change only with the user: ask first.\nThe daemon enforces this floor mechanically — a replace or content\nrewrite of a `from=user` row is BLOCKED unless the write carries\n`user_directed: true`, which you assert only when the user directed\nthe change: their current message states it as settled (a command\n\"update X to Y\", a declaration \"my X is now Y\", a commitment \"from\nnow on, X\") or they just answered your ask. A hedged reflection (\"X\nfeels about right to me\") never qualifies — ask first.\n\n**Status rows are perishable — supersede at write time.** A\nstatus-bearing row (\"in progress\", \"OPEN:\", \"not committed\",\n\"shipped\", \"dormant\") is a claim about the world, and the world moves.\nWhen you capture a status change (shipped / fixed / dormant /\nabandoned), search the subject first and write the new status\nreplacing the prior status row(s) on that subject (`replace_ids` over\nMCP; `add --replace <id>` via CLI) — never leave \"in progress\" beside its\nown outcome. Own-notes only; a user-voice predecessor follows the\nmerge law. The dream audit's review queue is the backstop for what\nslips through — write-time supersede is the real fix.\n\n| You see | Action |\n|:---|:---|\n| Exact dup (same fact, same type) | Delete the loser, keep the better-phrased row. No prompt. |\n| Superseded / chain member, all derived (\"impl not started\" → \"shipped\") | Merge into one current-truth row. No prompt. |\n| Reworded derived near-dup | Merge, keep the best phrasing. No prompt. |\n| Old pure-event row (\"committed X\") | Retire it — fold into the state row it evidences, if one exists. |\n| Contradiction touching a user-voice row | Don't pick silently. **Always ask.** |\n| Secret (credential, token, key) | Delete on sight, any tier. |\n| Judged episodic rows lingering past TTL | Run `ling-mem sweep` — it evicts exactly those, never un-judged rows. No prompt. |\n\n**How to ask:** use whichever ask-user primitive your host gives you.\n\n- **Claude Code** — call the `AskUserQuestion` tool. UI renders a\n  structured choice card.\n- **Codex / OpenClaw / any host without a structured tool** — write the\n  question in plain chat text with numbered options and stop. The user\n  replies on the next turn; you read their choice and finish the cleanup\n  via `ling-mem add \"...\" --type ...` followed by `ling-mem delete\n  <loser-id> --yes` for each loser.\n\nWhen a merge (derived rows) or an AskUser-resolved conflict yields a\nwinner: write the winner first (`ling-mem add \"<winner>\" --type <t>\n--from <f>`), then delete the losers (`ling-mem delete <loser-id>\n--yes`). The CLI doesn't expose an atomic replace verb; the two-step\nordering (write before delete) keeps the worst-case window safe — a\nconcurrent recall either sees the old rows or both, never an empty hole\non the subject. (Over MCP/HTTP, use `replace_ids` on the add instead —\none atomic call.)\n\n### What \"not confident\" looks like\n\n- Two rows on the same subject with timestamps far apart → user's view may\n  have changed. Ask.\n- Two rows that are mostly the same but differ on a specific detail (e.g.\n  one says \"8 years old in 2026-05-21\", another says \"9 years old in\n  2026-05-25\") → time-stamped, may both be valid. Ask before merging.\n- Rows that look like dups but have different `cwd` / `contexts` /\n  `outcome` — they may apply to different scopes. Ask.\n\nWhen in doubt, **ask**. Cheap. The cost of asking is one turn; the cost of\nsilently losing or mangling a fact is much higher.\n\n### What automatic catches mechanically\n\n- `insert_with_dedup` inside the binary rejects byte-identical\n  `(content, type)` rows at write time. You don't need to handle that case.\n- Cross-tier dedup (`add` handler): if you add to one table and an exact\n  match exists in the other, the higher-tier row wins; metadata\n  (contexts / tags) is merged into it. Also automatic.\n\nFuzzy \"same fact, different wording\" is **never mechanical** — it always\nneeds an LLM judgment + the rule above.\n\n### Inline reconciliation\n\nWhen recall hits include duplicates or conflicts, fix them:\n`ling-mem delete <id>` near-dups (keep the best phrasing);\n`ling-mem edit <id>` or `delete` on conflicts after asking the user.\nGet ids via `ling-mem search \"<phrase>\" --format json | jq -r '.[] | \"\\(.id)\\t\\(.content)\"'`.\n\n## Type taxonomy (reference)\n\nThe `type` enum is `fact | preference | decision | tried | fixed |\nlearned | built` — but **only four should be emitted by default**.\n\n| Type | Use | When to emit |\n|:---|:---|:---|\n| `fact` | Stable user truth (identity, goals, vision) | Cross-project, durable indefinitely |\n| `preference` | Cross-project behavioral rule for the agent | Commitment language required |\n| `decision` | A choice plus its reasoning | Reasoning is the retrieval value |\n| `learned` | Cross-project tech gotcha | Reusable across projects |\n\n`tried` / `fixed` / `built` are deprecated — emit only for\ntrajectory-level patterns or named shippable artifacts tied to user\nidentity.\n\n## Contexts and tags\n\n- **`contexts`** — hierarchical scope (1–3 typical, primary filter).\n  - `cross-project` — retrieves in any session.\n  - `code/linggen`, `music/piano`, `trip-japan-2026` — domain scopes.\n  - **Don't** add `project/<name>` for new writes. Project-internal\n    facts get dropped — the agent reads the project's own files next\n    time. Legacy `project/<name>` rows still retrieve.\n- **`tags`** — free-form metadata (0–5 typical, prefix convention).\n  - `intent:goal`, `topic:networking`, `person:maria`.\n\n## Data browser\n\nRow-level CRUD (filter, edit-in-place, batch delete) lives at\n`http://127.0.0.1:9528` when the daemon is running. Direct the user\nthere for hands-on cleanup. Run `ling-mem start` if not already\nrunning.\n\n## Updates\n\n`ling-mem start` (and `restart`) returns JSON that may include an\n`update` field — a cached probe of `linggen/linggen-memory` GitHub\nreleases (24h TTL, no extra network calls beyond the first).\n\nWhen that JSON contains `\"update\": {\"available\": true, ...}`, surface\nit to the user once at the top of your reply, e.g.:\n\n> *\"ling-mem upgrade available: 0.2.1 → 0.3.0 — `<notes_summary>`. Upgrade now?\"*\n\nIf the user agrees, run `ling-mem upgrade --yes` (the legacy `self-update`\nspelling still works as an alias). The CLI stops the daemon, verifies\nthe SHA-256 of the downloaded tarball, swaps the binary atomically\n(keeping the prior version at `bin/linggen.prev` for rollback), and\nrestarts the daemon by spawning the new binary explicitly so the\nrunning (old) inode never relaunches itself.\n\nAd-hoc check (no swap): `ling-mem upgrade --check`. Useful when the\nuser asks \"am I up to date?\" without wanting to upgrade. The same\ncached probe is also surfaced in `ling-mem status` output, so callers\nthat already poll `status` don't need a separate network call.\n\nDon't auto-upgrade silently — schema or behavior may change between\nversions, and the user should know what they're accepting.\n\n---\n\n## Install\n\nInstall from your agent's own marketplace — it manages updates and, on\nClaude Code / Codex, the per-turn recall hook. Pick **one** channel per host:\n\n```text\nClaude Code   /plugin marketplace add linggen/linggen-memory\n              /plugin install linggen@linggen-memory\nCodex         codex plugin marketplace add linggen/linggen-memory\n              codex plugin add linggen@linggen-memory\nOpenClaw      clawhub install linggen\nAny agent     npx skills add linggen/linggen-memory@linggen\nLinggen       Settings → Skills → linggen   (in-app)\n```\n\nThe `ling-mem` binary is fetched automatically on first use (pinned,\nSHA-256 verified). To install just the binary manually (Apple Silicon /\nLinux x86_64+aarch64), run the installer that ships in this bundle:\n\n```bash\nbash scripts/install-bin.sh --version '^1'\n```\n\n(Path relative to this skill's directory, like every other script here.)\n\nThe skill works in Claude Code, Codex, OpenClaw, Linggen, or standalone —\nsame daemon, same database, same semantics across all hosts. Intel Mac\nusers: prebuilt binaries aren't shipped; build from source via\n`cargo build --release` from\n[linggen/linggen-memory](https://github.com/linggen/linggen-memory).\n\nSource: [github.com/linggen/linggen-memory](https://github.com/linggen/linggen-memory) · [linggen.dev](https://linggen.dev)\n\n## Browser control (via the same MCP server)\n\nThe `linggen` MCP server also exposes the user's own Chrome (through the\nlinggen-browser extension) — one **visible** controlled tab:\n\n- `browser_navigate` / `browser_read_page` (accessibility tree with `[nN]`\n  refs) / `browser_click` / `browser_type` / `browser_key` /\n  `browser_scroll` / `browser_screenshot` / `browser_wait` /\n  `browser_tabs` / `browser_read_console`. Work a read → act → re-read\n  loop; target by ref.\n- Mutating actions may pause on a **permission prompt in the browser** —\n  the user approves each new site once (or always); payment, credentials,\n  deletes, and posting always confirm. A `not_permitted` error means the\n  user declined: stop, don't retry.\n- `x_search` / `x_targets` / `x_following` / `x_whotofollow` / `x_own`\n  return structured JSON from the user's logged-in x.com session — no\n  API keys.\n- A `no_bridge` error means the linggen-browser extension isn't connected;\n  ask the user to install or enable it.\n- If the `linggen` MCP server itself is unreachable (nothing listening on\n  `127.0.0.1:9527`), the engine may still be installing in the background\n  (plugin channels; progress in `~/.linggen/engine-install.log`) — wait and\n  retry. If the `ling` binary is genuinely absent, run the engine install\n  from the First-use section and tell the user (one-time, ~100MB). Memory\n  keeps working via the `ling-mem` CLI fallback throughout.\n\nFile v2.3.2:README.md\n\n# linggen (skill)\n\n**Persistent memory for AI assistants. Local, semantic, typed.**\n\nA single-binary memory layer that remembers useful facts about you and your work across every session, every tool, every project. Works in Claude Code, OpenClaw, Linggen, or any agent that can shell out to a CLI.\n\n## What it does\n\n- **Auto-recall on every prompt.** A `UserPromptSubmit` hook runs a semantic search over your stored facts and injects the top matches as context — no manual tool call required. Relevant preferences and past decisions land in the agent's view automatically.\n- **Semantic retrieval.** 1024-dim embeddings via `Qwen3-Embedding-0.6B` (multilingual). Find \"berth calibration\" by asking about \"dock alignment.\"\n- **Typed facts.** `fact`, `preference`, `decision`, `learned`, plus trajectory-level `tried`, `fixed`, `built`. Searches and filters operate on these tags.\n- **Forgetting is first-class.** Delete by id, forget by filter — refuses empty filters as a guardrail.\n- **Local-first storage.** The memory store is on disk in `~/.linggen/memory/` (LanceDB) — no cloud sync, no telemetry. Retrieved facts do enter your agent's prompt context on each turn, so they reach whichever LLM you've configured.\n- **Self-updating.** `ling-mem upgrade --check` reports the latest release; `--yes` swaps the binary atomically. (`self-update` still works as an alias.)\n\n## Quick start\n\nInstall from your agent's marketplace (pick one per host): Claude Code\n`/plugin install linggen@linggen-memory`, Codex `codex plugin add\nlinggen@linggen-memory`, OpenClaw `clawhub install linggen`, any agent\n`npx skills add linggen/linggen-memory@linggen`. The `ling-mem` binary\nauto-installs on first use.\n\n```bash\n# Add a fact\nling-mem add \"prefers concise replies, no hedging\" --type preference --from user\n\n# Semantic search\nling-mem search \"how do I format logs\" --limit 5 --format json\n\n# List by filter\nling-mem list --type preference --limit 20\n\n# Forget a specific row\nling-mem delete <id>\n```\n\n## How each host uses it\n\n| Host | Integration |\n|:-----|:------------|\n| Claude Code | SKILL.md + a `UserPromptSubmit` hook (`hooks/recall.sh`). Hook auto-injects relevant memories every prompt; agent calls the CLI for ad-hoc lookups. |\n| Codex / OpenClaw | Standard SKILL.md skill. Agent shells out via the CLI for every memory operation. |\n| Linggen | This skill is loaded the same way (CLI via `Bash`). Separately, the Linggen engine ships built-in `Memory_query` / `Memory_write` tools wired to the same daemon for its own auto-recall + dream paths — same store, same semantics, no skill round-trip needed inside the engine. |\n| Standalone | Any script shells out: `ling-mem search \"query\" --format json` |\n\nThe auto-detect installer (`install.sh`) places the skill into whichever host runtimes are present (`~/.claude/skills/`, `~/.openclaw/skills/`, `~/.linggen/skills/`).\n\n## Platforms\n\n- macOS Apple Silicon (M1+) — prebuilt binary\n- Linux x86_64 / aarch64 — prebuilt binary\n\nIntel Mac: prebuilt binaries not provided. Build from source with `cargo build --release` from [linggen/linggen-memory](https://github.com/linggen/linggen-memory).\n\n## Why this exists\n\n`CLAUDE.md` and equivalent project files handle static rules but don't grow with the user. MCP memory servers require a long-running process with JSON-RPC mediation, and aren't auto-injected into the agent's context — they're tools the agent has to remember to call. `linggen` fits between: a single binary (`ling-mem`) you shell out to, with semantic retrieval, typed facts, and explicit forget operations. The recall hook auto-injects relevant context on every prompt, so the agent doesn't have to remember to query.\n\n## What's stored\n\n- Stored: durable signal you (or the agent on your behalf) add via `ling-mem add` (or, inside the Linggen engine, the equivalent built-in `Memory_write` tool). Indexed in `~/.linggen/memory/memory.lancedb/` (two tables: `semantic` for promoted core/long-term rows, `episodic` for recently-encoded staging).\n- Not stored: session transcripts, code you don't explicitly save, anything not added through the CLI.\n\n## Links\n\n- **Linggen platform: [linggen.dev](https://linggen.dev)** · [github.com/linggen/linggen](https://github.com/linggen/linggen)\n- Source + binary releases: [github.com/linggen/linggen-memory](https://github.com/linggen/linggen-memory)\n- Skill source: [github.com/linggen/skills/tree/main/linggen](https://github.com/linggen/skills/tree/main/linggen)\n- Issues: [github.com/linggen/linggen-memory/issues](https://github.com/linggen/linggen-memory/issues)\n\n## License\n\n- **Skill code** (SKILL.md, install scripts, hooks): MIT-0 — see `LICENSE`. This is\n  the license ClawHub grants on every skill it distributes, so the bundle states\n  exactly what a user actually receives.\n- **`ling-mem` daemon binary**: MIT — built from [linggen/linggen-memory](https://github.com/linggen/linggen-memory).\n\nFile v2.3.2:_meta.json\n\n{\n  \"ownerId\": \"kn7b596ysh0br4s8zw8ebknrq5866248\",\n  \"slug\": \"linggen\",\n  \"version\": \"2.3.2\",\n  \"publishedAt\": 1786983613007\n}\n\nFile v2.3.2:references/condense-flow.md\n\n# Condense flow — collapse stale chains (canonical runbook)\n\nStage 4 of the memory pipeline: **semantic-at-rest maintenance**, the\nonly pass whose input is old long-term rows. Every other merge point\ngates entry (write-time dedup, the dream's promotion judgment) or works\na recall window; condense cures what no recall ever touches.\n\n- **Linggen** — the built-in `condense` mission under the `memory`\n  agent (ships cron-disabled, monthly once enabled). Trigger from the\n  memory app / mission API.\n- **Claude Code / Codex / OpenClaw** — no mission runtime; the host\n  agent runs the steps below via the `ling-mem` CLI (or the\n  `memory_chains` / `memory_add` MCP tools), on demand.\n\n## Before the first run — back up\n\n```bash\nling-mem export ~/condense-backup-$(date +%F).ndjson\n```\n\nCondense retires rows (atomically, via `replace_ids`), and the first\nruns should be supervised: watch the `MERGE` lines, spot-check a few\nsurvivors, keep the export until you trust the pass.\n\n## The scan — `chains`\n\n```bash\nling-mem chains --derived-only --limit 3                  # cited chains\nling-mem chains --kind marker --derived-only --limit 5    # marker candidates\nling-mem chains --kind subject --derived-only --limit 2   # subject clusters (v2)\n```\n\n(MCP: `memory_chains {\"kind\":\"cited\",\"derived_only\":true,\"limit\":3}`.)\n\nThree kinds, one law:\n\n- **`cited`** — rows citing another row's id verbatim, grouped into\n  chains. Pre-confirmed: an id citation is proof of reference;\n  collapse without re-litigating.\n- **`marker`** — rows with provisional-state language (\"OPEN:\",\n  \"uncommitted\", …) plus nearest-neighbor rows. Guesses: collapse only\n  after confirming a neighbor is the same subject AND one row\n  completes or obsoletes the other; otherwise skip.\n- **`subject`** (v2 digests) — same-subject vector clusters, 3+ rows.\n  Parallel notes on one subject, not a newest-wins chain: write one\n  focused per-subject **digest** row. Vector neighbors carry boundary\n  noise — digest the largest genuinely-one-subject subset\n  (`replace_ids` only its ids), leave outliers untouched; never one\n  mega state row.\n\n**Always pass `derived_only`** on an unattended or semi-attended pass —\nit filters to clusters that are entirely the agent's own notes\n(`from=derived`, `tier=semantic`), which the merge law allows merging\nwithout the user. A user-voice cluster is the user's to resolve\n(surface it in chat; never auto-merge).\n\n## The collapse — one current-truth row per chain\n\nOne atomic write per chain (MCP/HTTP):\n\n```json\nmemory_add {\n  \"content\": \"<current state first; history as a short dated span; keep lessons, drop dead provisional markers>\",\n  \"type\": \"<most current member's type>\",\n  \"contexts\": [<union of members'>],\n  \"cwd\": \"<the members' shared value when they agree; omit otherwise>\",\n  \"replace_ids\": [\"<every member id>\"]\n}\n```\n\nCLI hosts (no atomic replace verb): `ling-mem add` the survivor first,\nthen `ling-mem delete <member-id> --yes` each member — write before\ndelete.\n\nDrafting rules (same as the memory agent's):\n\n- Lead with the current state; carry history as a dated narrative\n  span. Keep re-hit lessons and decision reasoning; drop per-event\n  noise and provisional markers that no longer hold.\n- Never invent — every claim must come from a member row. On conflict,\n  keep the newest claim and note the change.\n- **Never cite raw row ids in the new content** — members are being\n  deleted; a dangling id re-chains the survivor on the next scan.\n- `replace_ids` may list only `from=derived, tier=semantic` rows.\n  Never a user-voice row, a core row, or an episodic id — one in the\n  cluster means skip the whole cluster.\n\n## Loop shape\n\nCited chains: re-fetch at offset 0 after each batch — merged chains\nvanish from the next scan, so the front of the list is always fresh\nwork; stop at `total: 0`. Marker candidates and subject clusters:\npage by offset; skipped ones linger (next month re-examines them). A\npartial pass is fine — oldest-first keeps progress monotone.\n\n**Stall guard.** If a fresh cited fetch returns a chain you already\nmerged this run, your merge did not take — reply exactly `STALLED`\nand stop (the mission ends the run there; a human looks). Never\nre-merge the same chain twice in one pass.\n\n## Status lines\n\nSame audit-trail contract as dream: `MERGE <new-id> replaces=<k>\n\"<gist>\"` per collapsed chain, `SKIP <id> unrelated` per rejected\nmarker candidate, and never print a line for a call you didn't make.\n\n## Order of passes\n\nCited first (provable), then markers (confirm supersession), then\nsubject digests (v2) — chains should collapse before the digest pass\nsees their subjects. The `subject` scan itself excludes rows still in\ncited chains for the same reason.\n\nFile v2.3.2:references/dream-flow.md\n\n# Dream flow — remember + forget (canonical runbook)\n\nTwo user-facing functions: **scan** (stage a day's session logs) and\n**dream** (= remember + forget). This file is the canonical procedure\nevery trigger runs:\n\n- **Linggen** — the built-in `dream` mission under the `memory` agent\n  runs every dream: the nightly cron, the memory app's Run-dream\n  button, and the calendar day buttons (day-scoped trigger). The\n  skill session runs only **scan** (`/linggen scan <date>`) and\n  explicit chat requests.\n- **Claude Code / Codex / OpenClaw** — no mission runtime; the host\n  agent runs the same steps via the `ling-mem` CLI (or the `memory_*`\n  MCP tools).\n\nDay-granular: the unit of work is one **local calendar day** of\nepisodic staging. Pending days drain **oldest first**.\n\n## Interface\n\nOn **Linggen**, use the built-in `Memory_query` / `Memory_write` tools\n(Chat-tier, ungated — zero permission prompts across a pass full of\nwrites): verbs `days`, `list` (+`day`), `add`, `remember_day`,\n`harvest_day` (the scan stamp), `sweep`.\nOn **other hosts**, the CLI is 1:1: `ling-mem days [--undreamed]`,\n`ling-mem list --tier episodic --day <date>`, `ling-mem add`,\n`ling-mem remember-day <date>`, `ling-mem harvest-day <date>`,\n`ling-mem sweep`. Always pipe CLI list/search output through\n`jq -c 'del(.vector)'`.\n\nState lives in the daemon (`.days.json` sidecar + the two tables) —\nthe old `.dream-state.json` / `.dream-history.jsonl` files are retired;\nnever write them.\n\n## Ground rules\n\n- **Unattended-safe.** Never call AskUser in a dream pass. When in\n  doubt about durability, **promote** — a redundant semantic row is\n  recoverable; lost signal isn't.\n- **Remembering never deletes.** Episodic is short-term memory; judged\n  rows stay until the sweep ages them out. One exception: a credential\n  / API key / password found in staging is deleted on sight.\n- **Only a tool_error is a failure.** `\"action\":\"merged\"` on add, a\n  promoted row vanishing from episodic (the daemon's cross-tier dedup\n  removed the twin during the add), `removed:false`, an empty list —\n  all normal. Never retry, never re-verify.\n- **Status lines, not prose:** `DAY <date> rows=<n>` → `PROMOTE <id>\n  \"<gist>\"` per promotion (`MERGE <new-id> replaces=<k> \"<gist>\"` per\n  derived merge) → `DAY <date> done judged=<n> promoted=<k>` →\n  `SWEEP removed=<n>` → one final totals sentence. Never print a\n  status line for a call you didn't make.\n\n## `dream` (no argument) — remember all undreamed days\n\n0. **Snapshot + in-flight check.** `ling-mem export` once (a store\n   backup before any judged writes — the engine does the same before\n   its mission runs). If the `memory_dream_status` MCP tool is\n   reachable and reports `in_flight: true`, the Linggen engine is\n   already dreaming — stop and say so; never run two dreams at once.\n1. Fetch the worklist: `days` with `undreamed_only` (CLI:\n   `ling-mem days --undreamed`). Empty → run **Forget** below, then\n   **Audit** below, reply that memory is up to date, done.\n2. Take the **oldest** undreamed day → run **Remember one day** below.\n3. Repeat from 1. If the same day comes back with an undropped\n   `unjudged` count, **stop and report** (\"stalled\") instead of\n   looping.\n4. When no days remain: run **Forget**, then **Audit**, then report\n   totals.\n\n## `dream <YYYY-MM-DD>` — remember one day\n\n- Day has episodic rows → **Remember one day** below, then one sweep.\n- Day has **no rows at all** → nothing to dream; suggest a scan if the\n  user worked that day.\n- Today / future dates are not dreamable — say so and stop.\n\n## `scan <YYYY-MM-DD>` — stage one day's session logs\n\nBackfill staging, always user-triggered, idempotent:\n\n1. Run `Bash bash <skill-dir>/scripts/scan.sh <date>` (zero-LLM session\n   walk → `.scan-output.jsonl`). `<skill-dir>` is this skill's own\n   directory — the one holding this `references/`, resolved per\n   SKILL.md (the plugin cache on Claude Code,\n   `${PLUGIN_ROOT}/skills/linggen/` on Codex). Never hard-code an\n   absolute path: the skill is not installed under `~/.linggen/skills`\n   on these hosts.\n2. **Skip covered sessions.** `list` the day's existing rows\n   (`tier=episodic` + that `day`, and note promoted twins may live in\n   semantic) and collect their `source_session` ids. Drop every\n   scanned session already in that set — live capture or a prior scan\n   contributed it. This is what makes re-scanning safe on\n   partially-captured days.\n3. Judge the remaining candidates per `extractor-prompt.md` +\n   `routing-rules.md`; write keepers to episodic with `occurred_at`\n   set to the session time.\n4. Stamp scanned — `harvest_day` verb (CLI:\n   `ling-mem harvest-day <date>`). This does **not** mark the day\n   remembered: new rows clear its `dreamed` flag, and the next dream (nightly\n   or the day's dream button) judges them. Nothing new staged → still\n   stamp, report `CLEAN`.\n\n## Remember one day\n\n1. **Re-pend check.** From the `days` rollup, note the day's\n   `remembered_at`. If set, judge **only** rows created after it —\n   earlier rows were already judged.\n2. **Worklist.** List the day's episodic rows:\n   `{\"verb\":\"list\",\"tier\":\"episodic\",\"day\":\"<date>\",\"limit\":25,\"sort\":\"oldest\"}`\n   (CLI: `ling-mem list --tier episodic --day <date> --sort oldest`).\n   Page with `offset` until every row is seen. Never pass\n   `type`/`from`/`outcome` — they narrow the list to zero.\n3. **Cluster.** Group near-duplicate rows on the same subject (per-turn\n   capture restates facts acr\n\nArchive v2.3.1: 17 files, 67600 bytes\n\nFiles: doc/shared-memory-design.md (12653b), LICENSE (906b), README.md (4928b), references/condense-flow.md (4756b), references/dream-flow.md (11023b), references/extractor-prompt.md (7522b), references/routing-rules.md (11146b), scripts/bootstrap.sh (2796b), scripts/collect_sessions.sh (11508b), scripts/collect.sh (3018b), scripts/extract_session.sh (13114b), scripts/install-bin.sh (9783b), scripts/install-engine.sh (16315b), scripts/scan.sh (8430b), skill-card.md (2624b), SKILL.md (32292b), _meta.json (126b)\n\nArchive v2.3.0: 15 files, 55943 bytes\n\nFiles: doc/shared-memory-design.md (12653b), LICENSE (906b), README.md (4928b), references/condense-flow.md (4756b), references/dream-flow.md (10015b), references/extractor-prompt.md (7522b), references/routing-rules.md (11146b), scripts/bootstrap.sh (2235b), scripts/collect_sessions.sh (10715b), scripts/collect.sh (3018b), scripts/extract_session.sh (12678b), scripts/scan.sh (8430b), skill-card.md (2658b), SKILL.md (32092b), _meta.json (126b)\n\nArchive v2.2.0: 14 files, 55371 bytes\n\nFiles: doc/shared-memory-design.md (12653b), LICENSE (11358b), README.md (4803b), references/condense-flow.md (4684b), references/dream-flow.md (9516b), references/extractor-prompt.md (7522b), references/routing-rules.md (11146b), scripts/collect_sessions.sh (10047b), scripts/collect.sh (3018b), scripts/extract_session.sh (8390b), scripts/scan.sh (8430b), skill-card.md (2607b), SKILL.md (31672b), _meta.json (126b)\n\nArchive v1.5.4: 14 files, 55491 bytes\n\nFiles: doc/shared-memory-design.md (12653b), LICENSE (11358b), README.md (4803b), references/condense-flow.md (4684b), references/dream-flow.md (9516b), references/extractor-prompt.md (7522b), references/routing-rules.md (11146b), scripts/collect_sessions.sh (10047b), scripts/collect.sh (3018b), scripts/extract_session.sh (8390b), scripts/scan.sh (8430b), skill-card.md (2829b), SKILL.md (31672b), _meta.json (126b)\n\nArchive v1.5.3: 14 files, 55404 bytes\n\nFiles: doc/shared-memory-design.md (12653b), LICENSE (11358b), README.md (4803b), references/condense-flow.md (4684b), references/dream-flow.md (9516b), references/extractor-prompt.md (7522b), references/routing-rules.md (11146b), scripts/collect_sessions.sh (10047b), scripts/collect.sh (3018b), scripts/extract_session.sh (8390b), scripts/scan.sh (8430b), skill-card.md (2663b), SKILL.md (31672b), _meta.json (126b)\n\nArchive v2.1.0: 14 files, 53538 bytes\n\nFiles: doc/shared-memory-design.md (12653b), LICENSE (11358b), README.md (4803b), references/condense-flow.md (4684b), references/dream-flow.md (7825b), references/extractor-prompt.md (7522b), references/routing-rules.md (11146b), scripts/collect_sessions.sh (10047b), scripts/collect.sh (3018b), scripts/extract_session.sh (8390b), scripts/scan.sh (8430b), skill-card.md (2891b), SKILL.md (28725b), _meta.json (126b)\n\nArchive v2.0.1: 14 files, 53402 bytes\n\nFiles: doc/shared-memory-design.md (12653b), LICENSE (11358b), README.md (4803b), references/condense-flow.md (4684b), references/dream-flow.md (7825b), references/extractor-prompt.md (7522b), references/routing-rules.md (11146b), scripts/collect_sessions.sh (10047b), scripts/collect.sh (3018b), scripts/extract_session.sh (8390b), scripts/scan.sh (8430b), skill-card.md (2904b), SKILL.md (28216b), _meta.json (126b)\n\nArchive v2.0.0: 14 files, 53250 bytes\n\nFiles: doc/shared-memory-design.md (12653b), LICENSE (11358b), README.md (4803b), references/condense-flow.md (4684b), references/dream-flow.md (7825b), references/extractor-prompt.md (7522b), references/routing-rules.md (11146b), scripts/collect_sessions.sh (10047b), scripts/collect.sh (3018b), scripts/extract_session.sh (8390b), scripts/scan.sh (8430b), skill-card.md (2983b), SKILL.md (27845b), _meta.json (126b)","readmeExcerpt":"Skill: linggen Owner: linggen Summary: Linggen — durable cross-host memory plus browser control, over two local MCP servers: ling-mem for memory, the Linggen engine for browser, X and agents. Memory: three-tier model (core + long-term + episodic staging) of who the user is, not a log of what was done; same ling-mem daemon and store in Claude Code, Codex, and OpenClaw, and reachable over the LAN from a second machine ","codeSnippets":[],"executableExamples":[{"language":"bash","snippet":"bash scripts/bootstrap.sh"},{"language":"bash","snippet":"ling-mem search \"node 22 quirk\" --limit 5 --format json | jq -c 'del(.vector)'"},{"language":"bash","snippet":"ling-mem add \"<content>\" --type fact --from user --tier core\nling-mem list --tier core --limit 100 | jq -c 'del(.vector)'"},{"language":"text","snippet":"Claude Code   /plugin marketplace add linggen/linggen-memory\n              /plugin install linggen@linggen-memory\nCodex         codex plugin marketplace add linggen/linggen-memory\n              codex plugin add linggen@linggen-memory\nOpenClaw      clawhub install linggen\nAny agent     npx skills add linggen/linggen-memory@linggen\nLinggen       Settings → Skills → linggen   (in-app)"},{"language":"bash","snippet":"bash scripts/install-bin.sh --version '^1'"},{"language":"bash","snippet":"# Add a fact\nling-mem add \"prefers concise replies, no hedging\" --type preference --from user\n\n# Semantic search\nling-mem search \"how do I format logs\" --limit 5 --format json\n\n# List by filter\nling-mem list --type preference --limit 20\n\n# Forget a specific row\nling-mem delete <id>"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: linggen\ndescription: >-\n  Linggen — durable cross-host memory plus browser control, over two\n  local MCP servers: `ling-mem` for memory, the Linggen engine for\n  browser, X and agents. Memory: three-tier model (core + long-term +\n  episodic staging) of who the user is, not a log of what was done;\n  same `ling-mem` daemon and store in Claude Code, Codex, and\n  OpenClaw, and reachable over the LAN from a second machine\n  (`/linggen:config`). Browser: agent control of the user's own Chrome\n  with per-site permission prompts, and logged-in X session reads.\nlicense: MIT-0\nhomepage: https://linggen.dev\nallowed-tools:\n  - Read\n  - Write\n  - Edit\n  - Bash\n  - Glob\n  - Grep\nuser-invocable: true\n\n# ClawHub clawdis metadata — declares dependency on the ling-mem CLI binary.\n# v0.4.0 will add `install: [{kind: brew, formula: ling-mem, tap: linggen/tap}]`\n# once the Homebrew tap exists; for now users install the CLI manually via the\n# install.sh one-liner shown in the body. Other hosts ignore this block.\nmetadata:\n  clawdis:\n    homepage: https://linggen.dev\n    primaryEnv: cli\n    emoji: 🧠\n    os: [darwin, linux]\n    requires:\n      bins: [ling-mem]\n---\n\nYou are **Ling**, operating inside the linggen skill — the user's\ndurable cross-session memory (plus browser control, below). Memory is\nyour surface: you read and write the user's permanent biography.\n\n**Interface order:** prefer the `memory_*` MCP tools from the `linggen`\nserver (`memory_search`, `memory_add`, `memory_get`, `memory_update`,\n`memory_delete`, `memory_list`) — they proxy the same ling-mem daemon\nwith the same semantics. When the MCP server is unavailable (daemon\ndown, headless host), fall back to the **`ling-mem` CLI** via `Bash`;\nevery command in this document works on both paths. Same daemon, same\nstore, same semantics across every host that loads this skill.\n\n*Part of the [Linggen](https://linggen.dev) agent platform.*\n\n**Skill resources** live alongside this `SKILL.md`. When the instructions\nbelow say `Read references/X.md` or `Bash scripts/X.sh`, resolve those\npaths relative to this skill's directory — `${CLAUDE_PLUGIN_ROOT}/skills/linggen/`\non Claude Code, `${PLUGIN_ROOT}/skills/linggen/` on Codex.\n\n> **Memory is how the agent grows up.** Not a log of what was done — a\n> deepening model of *who the user is*. A fact earns its place only if\n> a future session, on any project months from now, would make better\n> predictions about this user because the fact exists. Focus on the\n> user, not the task.\n\n## First use — ensure the Linggen binaries are installed\n\nThis skill has two required binaries: **`ling-mem`** (the memory daemon —\nserves `memory_*` on `127.0.0.1:9528/mcp`, and the CLI every Bash-only\nchannel shells out to) and **`ling`** (the Linggen engine — serves\n`browser_*`, `x_*`, `agent_run` and the dream tools on\n`127.0.0.1:9527/mcp`). Each tool is served in exactly one place: the\nengine does not proxy memory. The Claude Code / Codex plugin's\nsession-start hook installs both "},{"path":"README.md","content":"# linggen (skill)\n\n**Persistent memory for AI assistants. Local, semantic, typed.**\n\nA single-binary memory layer that remembers useful facts about you and your work across every session, every tool, every project. Works in Claude Code, OpenClaw, Linggen, or any agent that can shell out to a CLI.\n\n## What it does\n\n- **Auto-recall on every prompt.** A `UserPromptSubmit` hook runs a semantic search over your stored facts and injects the top matches as context — no manual tool call required. Relevant preferences and past decisions land in the agent's view automatically.\n- **Semantic retrieval.** 1024-dim embeddings via `Qwen3-Embedding-0.6B` (multilingual). Find \"berth calibration\" by asking about \"dock alignment.\"\n- **Typed facts.** `fact`, `preference`, `decision`, `learned`, plus trajectory-level `tried`, `fixed`, `built`. Searches and filters operate on these types.\n- **Forgetting is first-class.** Delete by id, forget by filter — refuses empty filters as a guardrail.\n- **Local-first storage.** The memory store is on disk in `~/.linggen/memory/` (LanceDB) — no cloud sync, no telemetry. Retrieved facts do enter your agent's prompt context on each turn, so they reach whichever LLM you've configured.\n- **Self-updating.** `ling-mem upgrade --check` reports the latest release; `--yes` swaps the binary atomically. (`self-update` still works as an alias.)\n\n## Quick start\n\nInstall from your agent's marketplace (pick one per host): Claude Code\n`/plugin install linggen@linggen-memory`, Codex `codex plugin add\nlinggen@linggen-memory`, OpenClaw `clawhub install linggen`, any agent\n`npx skills add linggen/linggen-memory@linggen`. The `ling-mem` binary\nauto-installs on first use.\n\n```bash\n# Add a fact\nling-mem add \"prefers concise replies, no hedging\" --type preference --from user\n\n# Semantic search\nling-mem search \"how do I format logs\" --limit 5 --format json\n\n# List by filter\nling-mem list --type preference --limit 20\n\n# Forget a specific row\nling-mem delete <id>\n```\n\n## How each host uses it\n\n| Host | Integration |\n|:-----|:------------|\n| Claude Code | SKILL.md + a `UserPromptSubmit` hook (`hooks/recall.sh`). Hook auto-injects relevant memories every prompt; agent calls the CLI for ad-hoc lookups. |\n| Codex / OpenClaw | Standard SKILL.md skill. Agent shells out via the CLI for every memory operation. |\n| Linggen | This skill is loaded the same way (CLI via `Bash`). Separately, the Linggen engine ships built-in `Memory_query` / `Memory_write` tools wired to the same daemon for its own auto-recall + dream paths — same store, same semantics, no skill round-trip needed inside the engine. |\n| Standalone | Any script shells out: `ling-mem search \"query\" --format json` |\n\nThe auto-detect installer (`install.sh`) places the skill into whichever host runtimes are present (`~/.claude/skills/`, `~/.openclaw/skills/`, `~/.linggen/skills/`).\n\n## Platforms\n\n- macOS Apple Silicon (M1+) — prebuilt binary\n- Linux x86_64 / aarch64 — prebuilt binary\n\nIntel Mac: prebuilt bi"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7b596ysh0br4s8zw8ebknrq5866248\",\n  \"slug\": \"linggen\",\n  \"version\": \"2.4.0\",\n  \"publishedAt\": 1791227298450\n}"},{"path":"references/condense-flow.md","content":"# Condense flow — collapse stale chains (canonical runbook)\n\nStage 4 of the memory pipeline: **semantic-at-rest maintenance**, the\nonly pass whose input is old long-term rows. Every other merge point\ngates entry (write-time dedup, the dream's promotion judgment) or works\na recall window; condense cures what no recall ever touches.\n\n- **Linggen** — the built-in `condense` mission under the `memory`\n  agent (ships cron-disabled, monthly once enabled). Trigger from the\n  memory app / mission API.\n- **Claude Code / Codex / OpenClaw** — no mission runtime; the host\n  agent runs the steps below via the `ling-mem` CLI (or the\n  `memory_chains` / `memory_add` MCP tools), on demand.\n\n## Before the first run — back up\n\n```bash\nling-mem export ~/condense-backup-$(date +%F).ndjson\n```\n\nCondense retires rows (atomically, via `replace_ids`), and the first\nruns should be supervised: watch the `MERGE` lines, spot-check a few\nsurvivors, keep the export until you trust the pass.\n\n## The scan — `chains`\n\n```bash\nling-mem chains --derived-only --limit 3                  # cited chains\nling-mem chains --kind marker --derived-only --limit 5    # marker candidates\nling-mem chains --kind subject --derived-only --limit 2   # subject clusters (v2)\n```\n\n(MCP: `memory_chains {\"kind\":\"cited\",\"derived_only\":true,\"limit\":3}`.)\n\nThree kinds, one law:\n\n- **`cited`** — rows citing another row's id verbatim, grouped into\n  chains. Pre-confirmed: an id citation is proof of reference;\n  collapse without re-litigating.\n- **`marker`** — rows with provisional-state language (\"OPEN:\",\n  \"uncommitted\", …) plus nearest-neighbor rows. Guesses: collapse only\n  after confirming a neighbor is the same subject AND one row\n  completes or obsoletes the other; otherwise skip.\n- **`subject`** (v2 digests) — same-subject vector clusters, 3+ rows.\n  Parallel notes on one subject, not a newest-wins chain: write one\n  focused per-subject **digest** row. Vector neighbors carry boundary\n  noise — digest the largest genuinely-one-subject subset\n  (`replace_ids` only its ids), leave outliers untouched; never one\n  mega state row.\n\n**Always pass `derived_only`** on an unattended or semi-attended pass —\nit filters to clusters that are entirely the agent's own notes\n(`from=derived`, `tier=semantic`), which the merge law allows merging\nwithout the user. A user-voice cluster is the user's to resolve\n(surface it in chat; never auto-merge).\n\n## The collapse — one current-truth row per chain\n\nOne atomic write per chain (MCP/HTTP):\n\n```json\nmemory_add {\n  \"content\": \"<current state first; history as a short dated span; keep lessons, drop dead provisional markers>\",\n  \"type\": \"<most current member's type>\",\n  \"tier\": \"semantic\",\n  \"indexed\": true,\n  \"summary\": \"<one line — only when a member was indexed>\",\n  \"replace_ids\": [\"<every member id>\"]\n}\n```\n\nNo `cwd` / `scope`: with `replace_ids` the daemon files the survivor\nunder the members' common directory (none when any member has none).\nPass `indexed` and `summary`"},{"path":"references/dream-flow.md","content":"# Dream flow — remember + forget (canonical runbook)\n\nTwo user-facing functions: **scan** (stage a day's session logs) and\n**dream** (= remember + forget). This file is the canonical procedure\nevery trigger runs:\n\n- **Linggen** — the built-in `dream` mission under the `memory` agent\n  runs every dream: the nightly cron, the memory app's Run-dream\n  button, and the calendar day buttons (day-scoped trigger). The\n  skill session runs only **scan** (`/linggen scan <date>`) and\n  explicit chat requests.\n- **Claude Code / Codex / OpenClaw** — no mission runtime; the host\n  agent runs the same steps via the `ling-mem` CLI (or the `memory_*`\n  MCP tools).\n\nDay-granular: the unit of work is one **local calendar day** of\nepisodic staging. Pending days drain **oldest first**.\n\n## Interface\n\nOn **Linggen**, use the built-in `Memory_query` / `Memory_write` tools\n(Chat-tier, ungated — zero permission prompts across a pass full of\nwrites): verbs `days`, `list` (+`day`), `add`, `remember_day`,\n`harvest_day` (the scan stamp), `sweep`.\nOn **other hosts**, the CLI is 1:1: `ling-mem days [--undreamed]`,\n`ling-mem list --tier episodic --day <date>`, `ling-mem add`,\n`ling-mem remember-day <date>`, `ling-mem harvest-day <date>`,\n`ling-mem sweep`. Always pipe CLI list/search output through\n`jq -c 'del(.vector)'`.\n\nState lives in the daemon (`.days.json` sidecar + the two tables) —\nthe old `.dream-state.json` / `.dream-history.jsonl` files are retired;\nnever write them.\n\n## Ground rules\n\n- **Unattended-safe.** Never call AskUser in a dream pass. When in\n  doubt about durability, **promote** — a redundant semantic row is\n  recoverable; lost signal isn't.\n- **Remembering never deletes.** Episodic is short-term memory; judged\n  rows stay until the sweep ages them out. One exception: a credential\n  / API key / password found in staging is deleted on sight.\n- **Only a tool_error is a failure.** `\"action\":\"merged\"` on add, a\n  promoted row vanishing from episodic (the daemon's cross-tier dedup\n  removed the twin during the add), `removed:false`, an empty list —\n  all normal. Never retry those, never re-verify.\n- **A failed write doesn't end the day.** An error on `add` may still\n  have saved the row — a timeout says so. Search its gist once: there →\n  carry on; absent → retry the add once, then carry on either way.\n  Finish the day and stamp it.\n- **Status lines, not prose:** `DAY <date> rows=<n>` → `PROMOTE <id>\n  \"<gist>\"` per promotion (`MERGE <new-id> replaces=<k> \"<gist>\"` per\n  derived merge) → `DAY <date> done judged=<n> promoted=<k>` →\n  `SWEEP removed=<n>` → `FIX <id> <field>=<new> (was <old>) \"<why>\"`\n  per scope/index/summary fix → one final totals sentence. Never print a\n  status line for a call you didn't make.\n\n## `dream` (no argument) — remember all undreamed days\n\n0. **Snapshot + in-flight check.** `ling-mem export` once (a store\n   backup before any judged writes — the engine does the same before\n   its mission runs). If the `memory_dream_status` MCP tool is\n "}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":2454,"uniquenessScore":42,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-09T13:02:39.661Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-09T13:02:39.661Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T01:13:41.539Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}