From 1e11a0dae9c079e8e9d7ef564e907633ffaac5e4 Mon Sep 17 00:00:00 2001 From: Nanobot Date: Thu, 30 Apr 2026 04:41:18 +0000 Subject: [PATCH 1/3] feat: Add Nanobot support --- cmd/setup.go | 78 +++++++++++++++++++++++--- internal/setup/assets/assets.go | 5 +- internal/setup/assets/nanobot/SKILL.md | 53 +++++++++++++++++ internal/setup/detect.go | 42 ++++++++++++++ internal/setup/nanobot.go | 44 +++++++++++++++ 5 files changed, 214 insertions(+), 8 deletions(-) create mode 100644 internal/setup/assets/nanobot/SKILL.md create mode 100644 internal/setup/nanobot.go diff --git a/cmd/setup.go b/cmd/setup.go index 15de6f8..a016325 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -22,10 +22,10 @@ var setupCmd = &cobra.Command{ Short: "Deploy mnemon into LLM CLI environments", Long: `Detect installed LLM CLIs and deploy mnemon integration. -By default, installs to project-local config (.claude/, .openclaw/). -Use --global to install to user-wide config (~/.claude/, ~/.openclaw/). +By default, installs to project-local config (.claude/, .openclaw/, .nanobot/). +Use --global to install to user-wide config (~/.claude/, ~/.openclaw/, ~/.nanobot/workspace/). -Supported environments: Claude Code, OpenClaw. +Supported environments: Claude Code, OpenClaw, Nanobot. Examples: mnemon setup # Interactive: project-local install @@ -38,7 +38,7 @@ Examples: } func init() { - setupCmd.Flags().StringVar(&setupTarget, "target", "", "target environment (claude-code, openclaw)") + setupCmd.Flags().StringVar(&setupTarget, "target", "", "target environment (claude-code, openclaw, nanobot)") setupCmd.Flags().BoolVar(&setupEject, "eject", false, "remove mnemon integrations") setupCmd.Flags().BoolVar(&setupYes, "yes", false, "auto-confirm all prompts (CI-friendly)") setupCmd.Flags().BoolVar(&setupGlobal, "global", false, "install to user-wide config (~/.claude/) instead of project-local (.claude/)") @@ -46,8 +46,8 @@ func init() { } func runSetup(cmd *cobra.Command, args []string) error { - if setupTarget != "" && setupTarget != "claude-code" && setupTarget != "openclaw" { - return fmt.Errorf("invalid target %q (must be claude-code or openclaw)", setupTarget) + if setupTarget != "" && setupTarget != "claude-code" && setupTarget != "openclaw" && setupTarget != "nanobot" { + return fmt.Errorf("invalid target %q (must be claude-code, openclaw, or nanobot)", setupTarget) } envs := setup.DetectEnvironments(setupGlobal) @@ -83,7 +83,7 @@ func runInstallFlow(envs []setup.Environment) error { if len(detected) == 0 { fmt.Println("\nNo supported LLM CLI environments detected.") - fmt.Println("Install Claude Code or OpenClaw, then run 'mnemon setup' again.") + fmt.Println("Install Claude Code, OpenClaw, or Nanobot, then run 'mnemon setup' again.") return nil } @@ -127,6 +127,8 @@ func installEnv(env *setup.Environment) error { err = installClaudeCode(env) case "openclaw": err = installOpenClaw(env) + case "nanobot": + err = installNanobot(env) } if err != nil { return err @@ -397,6 +399,61 @@ func selectOpenClawOptionalHooks() setup.HookSelection { return sel } +// ─── Nanobot ──────────────────────────────────────────────────────── + +func installNanobot(env *setup.Environment) error { + configDir := env.ConfigDir + + // Scope selection + if !setupGlobal && !setupYes && setup.IsInteractive() { + home := setup.HomeDir() + localDir := ".nanobot" + globalDir := home + "/.nanobot/workspace" + idx := setup.SelectOne("Install scope", + []string{ + fmt.Sprintf("Global — all projects (%s/)", globalDir), + fmt.Sprintf("Local — this project only (%s/)", localDir), + }, 0) // default: Global + if idx == 1 { + configDir = localDir + } else { + configDir = globalDir + } + } + + fmt.Printf("\nSetting up Nanobot (%s)...\n", configDir) + + // Phase 1: Skill + fmt.Println("\n[1/2] Skill") + if path, err := setup.NanobotWriteSkill(configDir); err != nil { + setup.StatusError(0, 0, "Skill", err) + return err + } else { + setup.StatusOK(0, 0, "Skill", path) + } + + // Phase 2: Prompt files (guide.md + skill.md → ~/.mnemon/prompt/) + fmt.Println("\n[2/2] Prompts") + if path, err := setup.WritePromptFiles(); err != nil { + setup.StatusError(0, 0, "Prompts", err) + return err + } else { + setup.StatusOK(0, 0, "Prompts", path) + } + + // Summary + fmt.Println() + fmt.Println("Setup complete!") + fmt.Printf(" Skill %s/skills/mnemon/SKILL.md\n", configDir) + fmt.Printf(" Prompts ~/.mnemon/prompt/ (guide.md, skill.md)\n") + fmt.Println() + fmt.Println("Restart Nanobot to activate the mnemon skill.") + fmt.Println("Edit ~/.mnemon/prompt/guide.md to customize behavior.") + fmt.Println("Run 'mnemon setup --eject' to remove.") + + return nil +} + // ─── Eject ────────────────────────────────────────────────────────── func runEjectFlow(envs []setup.Environment) error { @@ -475,6 +532,13 @@ func ejectEnv(env *setup.Environment) error { if len(errs) > 0 { return errs[0] } + + case "nanobot": + errs := setup.NanobotEject(env.ConfigDir) + ejectMarkdown("AGENTS.md", "Remove memory guidance from ./AGENTS.md?") + if len(errs) > 0 { + return errs[0] + } } return nil } diff --git a/internal/setup/assets/assets.go b/internal/setup/assets/assets.go index ca6ad54..9df0d2d 100644 --- a/internal/setup/assets/assets.go +++ b/internal/setup/assets/assets.go @@ -44,7 +44,10 @@ var NanoClawSkill []byte //go:embed nanoclaw/container-skill.md var NanoClawContainerSkill []byte +//go:embed nanobot/SKILL.md +var NanobotSkill []byte + // All returns the embedded filesystem for inspection. // -//go:embed claude openclaw nanoclaw +//go:embed claude openclaw nanoclaw nanobot var All embed.FS diff --git a/internal/setup/assets/nanobot/SKILL.md b/internal/setup/assets/nanobot/SKILL.md new file mode 100644 index 0000000..73bcc93 --- /dev/null +++ b/internal/setup/assets/nanobot/SKILL.md @@ -0,0 +1,53 @@ +--- +name: mnemon +description: Persistent memory CLI for LLM agents. Store facts, recall past knowledge, link related memories, manage lifecycle. +--- + +# mnemon + +## Workflow + +1. **Remember**: `mnemon remember "" --cat --imp <1-5> --entities "e1,e2" --source agent` + - Diff is built-in: duplicates skipped, conflicts auto-replaced. + - Output includes `action` (added/updated/skipped), `semantic_candidates`, `causal_candidates`. +2. **Link** (evaluate candidates from step 1 — use judgment, not mechanical rules): + - Review `causal_candidates`: does a genuine cause-effect relationship exist? `causal_signal` is regex-based and prone to false positives — only link if the memories are truly causally related. + - Review `semantic_candidates`: are these memories meaningfully related? High `similarity` alone is not sufficient — skip candidates that share keywords but discuss unrelated topics. + - Syntax: `mnemon link --type --weight <0-1> [--meta '']` +3. **Recall**: `mnemon recall "" --limit 10` + +## Commands + +```bash +mnemon remember "" --cat --imp <1-5> --entities "e1,e2" --source agent +mnemon link --type --weight <0-1> [--meta ''] +mnemon recall "" --limit 10 +mnemon search "" --limit 10 +mnemon forget +mnemon related --edge causal +mnemon gc --threshold 0.4 +mnemon gc --keep +mnemon status +mnemon log +mnemon store list +mnemon store create +mnemon store set +mnemon store remove +``` + +## Usage with nanobot + +Use the `exec` tool to run mnemon commands: + +``` +exec(command="mnemon recall 'user preferences'") +exec(command="mnemon remember 'User prefers dark mode' --cat preference --imp 3 --source agent") +``` + +## Guardrails + +- Never run `remember` or `link` in the main conversation — always delegate to a sub-agent via `spawn`. +- Do not store secrets, passwords, or tokens. +- Categories: `preference` · `decision` · `insight` · `fact` · `context` +- Edge types: `temporal` · `semantic` · `causal` · `entity` +- Max 8,000 chars per insight. diff --git a/internal/setup/detect.go b/internal/setup/detect.go index 24d3bb0..dea468b 100644 --- a/internal/setup/detect.go +++ b/internal/setup/detect.go @@ -31,6 +31,7 @@ func DetectEnvironments(global bool) []Environment { return []Environment{ detectClaude(global), detectOpenClaw(global), + detectNanobot(global), } } @@ -123,3 +124,44 @@ func cleanVersion(v string) string { } return v } + +func detectNanobot(global bool) Environment { + home := HomeDir() + globalDir := filepath.Join(home, ".nanobot", "workspace") + localDir := ".nanobot" + + configDir := localDir + if global { + configDir = globalDir + } + + env := Environment{ + Name: "nanobot", + Display: "Nanobot", + ConfigDir: configDir, + } + + // CLI detection is always global + if binPath, err := exec.LookPath("nanobot"); err == nil { + env.Detected = true + env.BinPath = binPath + } + if _, err := os.Stat(globalDir); err == nil { + env.Detected = true + } + + // Check if mnemon integration is already installed at the target location + skillPath := filepath.Join(configDir, "skills", "mnemon", "SKILL.md") + if _, err := os.Stat(skillPath); err == nil { + env.Installed = true + } + + // Get version + if env.BinPath != "" { + if out, err := exec.Command(env.BinPath, "--version").Output(); err == nil { + env.Version = cleanVersion(strings.TrimSpace(string(out))) + } + } + + return env +} diff --git a/internal/setup/nanobot.go b/internal/setup/nanobot.go new file mode 100644 index 0000000..727f66a --- /dev/null +++ b/internal/setup/nanobot.go @@ -0,0 +1,44 @@ +package setup + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/mnemon-dev/mnemon/internal/setup/assets" +) + +// NanobotWriteSkill writes the SKILL.md to the nanobot skills directory. +func NanobotWriteSkill(configDir string) (string, error) { + skillDir := filepath.Join(configDir, "skills", "mnemon") + if err := os.MkdirAll(skillDir, 0755); err != nil { + return "", err + } + skillPath := filepath.Join(skillDir, "SKILL.md") + if err := os.WriteFile(skillPath, assets.NanobotSkill, 0644); err != nil { + return "", err + } + return skillPath, nil +} + +// NanobotEject removes mnemon skill from the given nanobot config dir. +func NanobotEject(configDir string) []error { + var errs []error + + fmt.Printf("\nRemoving nanobot integration (%s)...\n", configDir) + + // Remove skill directory + skillDir := filepath.Join(configDir, "skills", "mnemon") + if err := os.RemoveAll(skillDir); err != nil { + StatusError(1, 1, "Skill", err) + errs = append(errs, err) + } else { + StatusOK(1, 1, "Skill", skillDir+" removed") + } + removeIfEmpty(filepath.Join(configDir, "skills")) + + // Clean up configDir itself if empty + removeIfEmpty(configDir) + + return errs +} From 8489329f9c961a907e02ca6f4237c7271eea1e13 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 17 May 2026 07:15:26 +0000 Subject: [PATCH 2/3] docs: add Nanobot integration to README, USAGE, and CHANGELOG --- CHANGELOG.md | 6 ++++++ README.md | 17 ++++++++++++++--- docs/USAGE.md | 5 +++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fa8994..b4bef70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] +### Added +- Nanobot integration: `mnemon setup --target nanobot` deploys a skill file to + `.nanobot/skills/mnemon/SKILL.md` (local) or `~/.nanobot/workspace/skills/mnemon/SKILL.md` + (global, recommended). `mnemon setup --eject` removes it. Detection is automatic + when the `nanobot` binary or `~/.nanobot/workspace/` directory is present. + ## [0.1.5] - 2026-05-17 ### Fixed diff --git a/README.md b/README.md index fe154de..267d92c 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,14 @@ NanoClaw runs agents inside Linux containers. Use the `/add-mnemon` skill to int The skill is available at `.claude/skills/add-mnemon/` in the NanoClaw repo. +### [Nanobot](https://github.com/dream-aim/nanobot) + +```bash +mnemon setup --target nanobot --global --yes +``` + +One command writes a skill file to `~/.nanobot/workspace/skills/mnemon/SKILL.md`. Memory is shared across all Nanobot sessions and projects. Use `--global` (recommended) because Nanobot discovers skills from the global workspace directory. + ### Uninstall ```bash @@ -156,6 +164,7 @@ memory is useful. - **Zero user-side operation** — install once; supported runtimes can use hooks, minimal runtimes can use persistent rules - **LLM-supervised** — the host LLM decides what to remember, update, and forget; no embedded LLM, no API keys +- **Multi-framework support** — Claude Code (hooks), OpenClaw (plugins), Nanobot (skills), and more - **Markdown-installable harness** — `SKILL.md`, `INSTALL.md`, `GUIDELINE.md`, and four lifecycle reminders - **Four-graph architecture** — temporal, entity, causal, and semantic edges, not just vector similarity - **Intent-native protocol** — three primitives (`remember`, `link`, `recall`) map to the LLM's cognitive vocabulary, not database syntax; structured JSON output with signal transparency @@ -173,6 +182,8 @@ All your local agentic AIs — across sessions and frameworks — sharing one po │ OpenClaw ─────┤ │ + Nanobot ──────┤ + │ NanoClaw ─────┤ ├──▶ ~/.mnemon ◀── shared memory OpenCode ─────┤ @@ -182,9 +193,9 @@ All your local agentic AIs — across sessions and frameworks — sharing one po The foundation is in place: a single `~/.mnemon` database that any agent can read and write. Claude Code setup automates hook installation; OpenClaw can use -plugin hooks; NanoClaw integrates via container skills and volume mounts. The -same harness can be installed in any LLM CLI that supports skills, rules, -system prompts, or event hooks. +plugin hooks; Nanobot integrates via skill files; NanoClaw integrates via +container skills and volume mounts. The same harness can be installed in any +LLM CLI that supports skills, rules, system prompts, or event hooks. The longer-term direction is a **memory gateway**: protocol decoupled from storage engine. The current SQLite backend is the first adapter; the protocol surface (`remember / link / recall`) can sit on top of PostgreSQL, Neo4j, or any graph database. Agent-side optimization (when to recall, what to remember) and storage-side optimization (indexing, graph algorithms) evolve independently. See [Future Direction](docs/design/08-decisions.md#82-future-direction) for details. diff --git a/docs/USAGE.md b/docs/USAGE.md index 0ebff12..49898bf 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -30,6 +30,7 @@ mnemon setup --global # Non-interactive: specific target only mnemon setup --target claude-code mnemon setup --target openclaw +mnemon setup --target nanobot --global # Auto-confirm all prompts (CI-friendly) mnemon setup --yes @@ -41,8 +42,8 @@ mnemon setup --eject --target claude-code | Flag | Default | Description | |---|---|---| -| `--global` | `false` | Install to user-wide config (`~/.claude/`) instead of project-local (`.claude/`) | -| `--target ` | (auto-detect) | Target environment: `claude-code` or `openclaw` | +| `--global` | `false` | Install to user-wide config instead of project-local (required for Nanobot: installs to `~/.nanobot/workspace/`) | +| `--target ` | (auto-detect) | Target environment: `claude-code`, `openclaw`, or `nanobot` | | `--eject` | `false` | Remove mnemon integrations | | `--yes` | `false` | Auto-confirm all prompts | From 623426d056650885432ea58c14a318266b9f9941 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 17 May 2026 23:48:50 +0000 Subject: [PATCH 3/3] fix: address nanobot PR review feedback - Update Nanobot README link to https://github.com/HKUDS/nanobot - Make SKILL.md usage examples recall-only to match the guardrail; soften guardrail from "never" to "prefer delegating" so the intent is clear without being overly prescriptive --- README.md | 2 +- internal/setup/assets/nanobot/SKILL.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 267d92c..9424eed 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ NanoClaw runs agents inside Linux containers. Use the `/add-mnemon` skill to int The skill is available at `.claude/skills/add-mnemon/` in the NanoClaw repo. -### [Nanobot](https://github.com/dream-aim/nanobot) +### [Nanobot](https://github.com/HKUDS/nanobot) ```bash mnemon setup --target nanobot --global --yes diff --git a/internal/setup/assets/nanobot/SKILL.md b/internal/setup/assets/nanobot/SKILL.md index 73bcc93..39e6bc7 100644 --- a/internal/setup/assets/nanobot/SKILL.md +++ b/internal/setup/assets/nanobot/SKILL.md @@ -37,16 +37,16 @@ mnemon store remove ## Usage with nanobot -Use the `exec` tool to run mnemon commands: +Use the `exec` tool to run mnemon commands. Recall can run in the main conversation; delegate `remember` and `link` to a sub-agent via `spawn` to keep the main conversation clean. ``` exec(command="mnemon recall 'user preferences'") -exec(command="mnemon remember 'User prefers dark mode' --cat preference --imp 3 --source agent") +exec(command="mnemon recall 'past decisions about auth'") ``` ## Guardrails -- Never run `remember` or `link` in the main conversation — always delegate to a sub-agent via `spawn`. +- Prefer delegating `remember` and `link` to a sub-agent via `spawn` rather than running them in the main conversation. - Do not store secrets, passwords, or tokens. - Categories: `preference` · `decision` · `insight` · `fact` · `context` - Edge types: `temporal` · `semantic` · `causal` · `entity`