Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,16 @@ One command deploys the mnemon skill, prompt files, and Cursor lifecycle hooks
to `.cursor/`. The integration primes new agent sessions with Mnemon guidance
and memory status, then nudges for durable-memory writeback after responses.

### [Trae](https://www.trae.ai/)

```bash
mnemon setup --target trae --yes
```

One command deploys the mnemon skill, prompt files, and Trae native hooks to
`.trae/`. The integration uses `SessionStart`, `UserPromptSubmit`, and `Stop`
hooks in `.trae/hooks.json`.

### [OpenClaw](https://github.com/openclaw/openclaw)

```bash
Expand Down
101 changes: 94 additions & 7 deletions cmd/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,18 @@ 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/, .codex/, .cursor/, .openclaw/, .nanobot/, .pi/).
Use --global to install to user-wide config (~/.claude/, ~/.codex/, ~/.cursor/, ~/.openclaw/, ~/.nanobot/workspace/, ~/.pi/agent/).
By default, installs to project-local config (.claude/, .codex/, .cursor/, .trae/, .openclaw/, .nanobot/, .pi/).
Use --global to install to user-wide config (~/.claude/, ~/.codex/, ~/.cursor/, ~/.trae/, ~/.openclaw/, ~/.nanobot/workspace/, ~/.pi/agent/).
Hermes Agent uses its native user config at ~/.hermes/.

Supported environments: Claude Code, Codex, Cursor, OpenClaw, Nanobot, Pi, Hermes Agent.
Supported environments: Claude Code, Codex, Cursor, Trae, OpenClaw, Nanobot, Pi, Hermes Agent.

Examples:
mnemon setup # Interactive: project-local install
mnemon setup --global # Interactive: user-wide install
mnemon setup --target claude-code # Non-interactive: Claude Code only
mnemon setup --target cursor # Non-interactive: Cursor skill only
mnemon setup --target trae # Non-interactive: Trae skill and hooks
mnemon setup --target hermes # Non-interactive: Hermes Agent only
mnemon setup --eject # Interactive: remove integrations
mnemon setup --eject --target claude-code # Non-interactive: remove Claude Code only
Expand All @@ -41,16 +42,16 @@ Examples:
}

func init() {
setupCmd.Flags().StringVar(&setupTarget, "target", "", "target environment (claude-code, codex, cursor, openclaw, nanobot, pi, hermes)")
setupCmd.Flags().StringVar(&setupTarget, "target", "", "target environment (claude-code, codex, cursor, trae, openclaw, nanobot, pi, hermes)")
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 instead of project-local config")
rootCmd.AddCommand(setupCmd)
}

func runSetup(cmd *cobra.Command, args []string) error {
if setupTarget != "" && setupTarget != "claude-code" && setupTarget != "codex" && setupTarget != "cursor" && setupTarget != "openclaw" && setupTarget != "nanobot" && setupTarget != "pi" && setupTarget != "hermes" {
return fmt.Errorf("invalid target %q (must be claude-code, codex, cursor, openclaw, nanobot, pi, or hermes)", setupTarget)
if setupTarget != "" && setupTarget != "claude-code" && setupTarget != "codex" && setupTarget != "cursor" && setupTarget != "trae" && setupTarget != "openclaw" && setupTarget != "nanobot" && setupTarget != "pi" && setupTarget != "hermes" {
return fmt.Errorf("invalid target %q (must be claude-code, codex, cursor, trae, openclaw, nanobot, pi, or hermes)", setupTarget)
}

envs := setup.DetectEnvironments(setupGlobal)
Expand Down Expand Up @@ -86,7 +87,7 @@ func runInstallFlow(envs []setup.Environment) error {

if len(detected) == 0 {
fmt.Println("\nNo supported LLM CLI environments detected.")
fmt.Println("Install Claude Code, Codex, Cursor, OpenClaw, Nanobot, Pi, or Hermes Agent, then run 'mnemon setup' again.")
fmt.Println("Install Claude Code, Codex, Cursor, Trae, OpenClaw, Nanobot, Pi, or Hermes Agent, then run 'mnemon setup' again.")
return nil
}

Expand Down Expand Up @@ -132,6 +133,8 @@ func installEnv(env *setup.Environment) error {
err = installCodex(env)
case "cursor":
err = installCursor(env)
case "trae":
err = installTrae(env)
case "openclaw":
err = installOpenClaw(env)
case "nanobot":
Expand Down Expand Up @@ -489,6 +492,83 @@ func selectCursorOptionalHooks() setup.HookSelection {
return sel
}

// ─── Trae ───────────────────────────────────────────────────────────

func installTrae(env *setup.Environment) error {
configDir := env.ConfigDir

if !setupGlobal && !setupYes && setup.IsInteractive() {
home := setup.HomeDir()
localDir := ".trae"
globalDir := home + "/.trae"
idx := setup.SelectOne("Install scope",
[]string{
fmt.Sprintf("Local — this project only (%s/)", localDir),
fmt.Sprintf("Global — all projects (%s/)", globalDir),
}, 0)
if idx == 1 {
configDir = globalDir
} else {
configDir = localDir
}
}

fmt.Printf("\nSetting up Trae (%s)...\n", configDir)

fmt.Println("\n[1/3] Skill")
if path, err := setup.TraeWriteSkill(configDir); err != nil {
setup.StatusError(0, 0, "Skill", err)
return err
} else {
setup.StatusOK(0, 0, "Skill", path)
}

fmt.Println("\n[2/3] Prompts")
var promptPath string
if path, err := setup.WritePromptFiles(); err != nil {
setup.StatusError(0, 0, "Prompts", err)
return err
} else {
setup.StatusOK(0, 0, "Prompts", path)
promptPath = path
}

fmt.Println("\n[3/3] Hooks")
for _, hook := range []struct {
label string
filename string
content []byte
}{
{"Hook: prime", "prime.sh", assets.TraePrimeHook},
{"Hook: remind", "user_prompt.sh", assets.TraeUserPromptHook},
{"Hook: nudge", "stop.sh", assets.TraeStopHook},
} {
if path, err := setup.TraeWriteHook(configDir, hook.filename, hook.content); err != nil {
setup.StatusError(0, 0, hook.label, err)
return err
} else {
setup.StatusOK(0, 0, hook.label, path)
}
}
if path, err := setup.TraeRegisterHooks(configDir); err != nil {
setup.StatusError(0, 0, "Hooks config", err)
return err
} else {
setup.StatusUpdated(0, 0, "Hooks config", path)
}

fmt.Println()
fmt.Println("Setup complete!")
fmt.Printf(" Skill %s/skills/mnemon/SKILL.md\n", configDir)
fmt.Printf(" Hooks %s/hooks.json (SessionStart, UserPromptSubmit, Stop)\n", configDir)
fmt.Printf(" Prompts %s/ (guide.md, skill.md)\n", promptPath)
fmt.Println()
fmt.Println("Restart Trae to activate the mnemon skill and hooks.")
fmt.Println("Run 'mnemon setup --eject --target trae' to remove.")

return nil
}

// ─── OpenClaw ───────────────────────────────────────────────────────

func installOpenClaw(env *setup.Environment) error {
Expand Down Expand Up @@ -923,6 +1003,13 @@ func ejectEnv(env *setup.Environment) error {
return errs[0]
}

case "trae":
errs := setup.TraeEject(env.ConfigDir)
ejectMarkdown("AGENTS.md", "Remove memory guidance from ./AGENTS.md?")
if len(errs) > 0 {
return errs[0]
}

case "openclaw":
errs := setup.OpenClawEject(env.ConfigDir)
ejectMarkdown("AGENTS.md", "Remove memory guidance from ./AGENTS.md?")
Expand Down
5 changes: 4 additions & 1 deletion docs/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ mnemon setup --global

# Non-interactive: specific target only
mnemon setup --target claude-code
mnemon setup --target codex
mnemon setup --target cursor
mnemon setup --target trae
mnemon setup --target openclaw
mnemon setup --target pi
mnemon setup --target nanobot --global
Expand All @@ -45,7 +48,7 @@ mnemon setup --eject --target claude-code
| Flag | Default | Description |
|---|---|---|
| `--global` | `false` | Install to user-wide config instead of project-local (recommended for Nanobot: installs to `~/.nanobot/workspace/`; Pi installs to `~/.pi/agent/`; Hermes installs to `~/.hermes/`) |
| `--target <name>` | (auto-detect) | Target environment: `claude-code`, `codex`, `openclaw`, `nanobot`, `pi`, or `hermes` |
| `--target <name>` | (auto-detect) | Target environment: `claude-code`, `codex`, `cursor`, `trae`, `openclaw`, `nanobot`, `pi`, or `hermes` |
| `--eject` | `false` | Remove mnemon integrations |
| `--yes` | `false` | Auto-confirm all prompts |

Expand Down
10 changes: 10 additions & 0 deletions docs/zh/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,16 @@ mnemon setup

`mnemon setup` 自动检测 Claude Code,交互式部署技能文件、钩子和行为引导。启动新会话 — 记忆自动运作。

### [Trae](https://www.trae.ai/)

```bash
mnemon setup --target trae --yes
```

一条命令将 mnemon skill、prompt 文件和 Trae 原生 hooks 部署到 `.trae/`。
该集成使用 `.trae/hooks.json` 中的 `SessionStart`、`UserPromptSubmit` 和
`Stop` hooks。

### [OpenClaw](https://github.com/openclaw/openclaw)

```bash
Expand Down
4 changes: 3 additions & 1 deletion docs/zh/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ mnemon setup --global
# 非交互式:仅指定目标
mnemon setup --target claude-code
mnemon setup --target codex
mnemon setup --target cursor
mnemon setup --target trae
mnemon setup --target openclaw
mnemon setup --target pi
mnemon setup --target nanobot --global
Expand All @@ -46,7 +48,7 @@ mnemon setup --eject --target claude-code
| 标志 | 默认值 | 说明 |
|---|---|---|
| `--global` | `false` | 安装到用户级配置而非项目本地(Nanobot 推荐安装到 `~/.nanobot/workspace/`;Pi 安装到 `~/.pi/agent/`;Hermes 安装到 `~/.hermes/`) |
| `--target <name>` | (自动检测) | 目标环境:`claude-code`、`codex`、`openclaw`、`nanobot`、`pi` 或 `hermes` |
| `--target <name>` | (自动检测) | 目标环境:`claude-code`、`codex`、`cursor`、`trae`、`openclaw`、`nanobot`、`pi` 或 `hermes` |
| `--eject` | `false` | 移除 mnemon 集成 |
| `--yes` | `false` | 自动确认所有提示 |

Expand Down
14 changes: 13 additions & 1 deletion internal/setup/assets/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ var CursorStopHook []byte
//go:embed cursor/compact.sh
var CursorCompactHook []byte

//go:embed trae/SKILL.md
var TraeSkill []byte

//go:embed trae/prime.sh
var TraePrimeHook []byte

//go:embed trae/user_prompt.sh
var TraeUserPromptHook []byte

//go:embed trae/stop.sh
var TraeStopHook []byte

//go:embed openclaw/SKILL.md
var OpenClawSkill []byte

Expand Down Expand Up @@ -94,5 +106,5 @@ var HermesCompactHook []byte

// All returns the embedded filesystem for inspection.
//
//go:embed claude codex cursor openclaw nanoclaw nanobot pi hermes
//go:embed claude codex cursor trae openclaw nanoclaw nanobot pi hermes
var All embed.FS
46 changes: 46 additions & 0 deletions internal/setup/assets/trae/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
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 "<fact>" --cat <cat> --imp <1-5> --entities "e1,e2" --source agent`
- Diff is built in: duplicates are skipped, conflicts are auto-replaced.
- Output includes `action` (added/updated/skipped), `semantic_candidates`, and `causal_candidates`.
2. **Link** (evaluate candidates from step 1 using judgment):
- Review `causal_candidates`: link only when the memories are genuinely causally related.
- Review `semantic_candidates`: high `similarity` alone is not enough; skip unrelated keyword matches.
- Syntax: `mnemon link <id> <candidate> --type <causal|semantic> --weight <0-1> [--meta '<json>']`
3. **Recall**: `mnemon recall "<query>" --limit 10`

## Commands

```bash
mnemon remember "<fact>" --cat <cat> --imp <1-5> --entities "e1,e2" --source agent
mnemon link <id1> <id2> --type <type> --weight <0-1> [--meta '<json>']
mnemon recall "<query>" --limit 10
mnemon search "<query>" --limit 10
mnemon import --dry-run <file>
mnemon import <file>
mnemon forget <id>
mnemon related <id> --edge causal
mnemon gc --threshold 0.4
mnemon gc --keep <id>
mnemon status
mnemon log
mnemon store list
mnemon store create <name>
mnemon store set <name>
mnemon store remove <name>
```

## Guardrails

- Use memory only when it can materially improve continuity or task quality.
- Do not store secrets, passwords, tokens, private keys, or short-lived operational noise.
- Categories: `preference`, `decision`, `insight`, `fact`, `context`
- Edge types: `temporal`, `semantic`, `causal`, `entity`
- Max 8,000 chars per insight.
22 changes: 22 additions & 0 deletions internal/setup/assets/trae/prime.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
PROMPT_DIR="${MNEMON_DATA_DIR:-$HOME/.mnemon}/prompt"
if [ ! -f "${PROMPT_DIR}/guide.md" ] && [ -f "${HOME}/.mnemon/prompt/guide.md" ]; then
PROMPT_DIR="${HOME}/.mnemon/prompt"
fi

if ! command -v mnemon >/dev/null 2>&1; then
echo "[mnemon] Warning: mnemon not found in PATH."
[ -f "${PROMPT_DIR}/guide.md" ] && cat "${PROMPT_DIR}/guide.md"
exit 0
fi

STATS=$(mnemon status 2>/dev/null)
if [ -n "$STATS" ]; then
INSIGHTS=$(echo "$STATS" | sed -n 's/.*"total_insights": *\([0-9]*\).*/\1/p' | head -1)
EDGES=$(echo "$STATS" | sed -n 's/.*"edge_count": *\([0-9]*\).*/\1/p' | head -1)
echo "[mnemon] Memory active (${INSIGHTS:-0} insights, ${EDGES:-0} edges)."
else
echo "[mnemon] Memory active."
fi

[ -f "${PROMPT_DIR}/guide.md" ] && cat "${PROMPT_DIR}/guide.md"
14 changes: 14 additions & 0 deletions internal/setup/assets/trae/stop.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
INPUT=$(cat)

MSG=$(echo "$INPUT" | jq -r '.last_assistant_message // ""' 2>/dev/null)
if echo "$MSG" | grep -qiE "mnemon remember|mnemon recall|mnemon link|Stored.*imp="; then
exit 0
fi

cat <<'JSON'
{
"decision": "block",
"reason": "[mnemon] Before stopping, evaluate whether this exchange contains durable preferences, decisions, insights, facts, or context worth remembering. If yes, run mnemon remember/link; if no, state that no memory update is needed, then finish."
}
JSON
3 changes: 3 additions & 0 deletions internal/setup/assets/trae/user_prompt.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash
cat >/dev/null || true
echo "[mnemon] Evaluate: recall needed? After responding, evaluate: remember needed?"
Loading
Loading