@hippo-memo/cli 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +174 -66
- package/dist/template/README.md +42 -2
- package/dist/template/skill/hippo-memory/SKILL.md +257 -0
- package/package.json +2 -1
- package/src/commands/index.ts +1 -0
- package/src/commands/init.ts +151 -17
- package/src/commands/memory/actions.ts +268 -0
- package/src/commands/memory/index.ts +64 -0
- package/src/index.ts +3 -0
package/dist/template/README.md
CHANGED
|
@@ -15,8 +15,14 @@ Hippo Memory 的预设内存模板集合。
|
|
|
15
15
|
```
|
|
16
16
|
template/
|
|
17
17
|
├── mcp/ # MCP 服务器相关模板
|
|
18
|
-
│ └──
|
|
18
|
+
│ └── AGENTS.md # MCP 协议配置
|
|
19
|
+
├── skill/ # Claude Code Skill 模板
|
|
20
|
+
│ ├── hippo-memory/ # 记忆管理技能
|
|
21
|
+
│ └── hippo-init/ # 初始化引导技能
|
|
19
22
|
└── system/ # 通用系统模板
|
|
23
|
+
├── agent.md # AI 能力边界
|
|
24
|
+
├── soul.md # AI 身份和行为模式
|
|
25
|
+
└── user.md # 用户信息和偏好
|
|
20
26
|
```
|
|
21
27
|
|
|
22
28
|
## 系统模板
|
|
@@ -50,4 +56,38 @@ AI 能力边界定义
|
|
|
50
56
|
|
|
51
57
|
## 使用方式
|
|
52
58
|
|
|
53
|
-
|
|
59
|
+
### CLI 初始化
|
|
60
|
+
|
|
61
|
+
运行 `hippo init` 时会自动:
|
|
62
|
+
|
|
63
|
+
1. 创建 `.hippo` 记忆存储目录
|
|
64
|
+
2. 初始化系统记忆(system://boot, system://soul, system://user)
|
|
65
|
+
3. 提示选择 Claude Code 集成方式:
|
|
66
|
+
- **MCP 模式**:将协议注入到 `AGENTS.md` 或 `CLAUDE.md`
|
|
67
|
+
- **Skill 模式**:复制技能模板到 `.claude/skills/`
|
|
68
|
+
- **混合模式**:同时配置 MCP 和 Skill
|
|
69
|
+
|
|
70
|
+
### MCP vs Skill
|
|
71
|
+
|
|
72
|
+
| 特性 | MCP | Skill |
|
|
73
|
+
|------|-----|-------|
|
|
74
|
+
| **作用域** | 全局/用户级 | 项目级 |
|
|
75
|
+
| **存储位置** | Claude 配置目录 | `.claude/skills/` |
|
|
76
|
+
| **版本控制** | 不推荐 | 推荐 |
|
|
77
|
+
| **适用场景** | 跨项目共享 | 项目特定流程 |
|
|
78
|
+
|
|
79
|
+
### 技能说明
|
|
80
|
+
|
|
81
|
+
#### hippo-memory
|
|
82
|
+
提供完整的 Hippo Memory 操作指南,包括:
|
|
83
|
+
- 创建、读取、更新、删除记忆
|
|
84
|
+
- 搜索记忆内容
|
|
85
|
+
- 管理记忆别名
|
|
86
|
+
- URI 设计规范和最佳实践
|
|
87
|
+
|
|
88
|
+
#### hippo-init
|
|
89
|
+
项目初始化引导技能,包括:
|
|
90
|
+
- 记忆目录创建
|
|
91
|
+
- MCP vs Skill 选择指导
|
|
92
|
+
- 配置文件说明
|
|
93
|
+
- 故障排查
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hippo-memory
|
|
3
|
+
description: 【必须主动判定触发】每当用户透露偏好、约定、决策时立即存储。主动触发场景:①用户说"记住"、"记录"、"我习惯" ②发现项目决策/架构约定 ③用户表达偏好"我喜欢/我不用/我不喜欢" ④重复出现的信息应缓存。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Hippo Memory Skill
|
|
7
|
+
|
|
8
|
+
## 概述
|
|
9
|
+
|
|
10
|
+
此技能提供 Hippo Memory 存储系统的管理功能,通过 `bunx @hippo-memo/cli@latest` 命令进行记忆的增删改查、搜索和别名操作。
|
|
11
|
+
|
|
12
|
+
## ⚡ 会话启动协议
|
|
13
|
+
|
|
14
|
+
**每次新会话必须且只能执行:**
|
|
15
|
+
```bash
|
|
16
|
+
bunx @hippo-memo/cli@latest memory read "system://boot"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## 🎯 "三思而后答"强制检查
|
|
20
|
+
|
|
21
|
+
**回复前必须完成以下思维链:**
|
|
22
|
+
|
|
23
|
+
| 检查项 | 判定 | 动作 |
|
|
24
|
+
|--------|------|------|
|
|
25
|
+
| **A. 领域** | 用户询问私有记忆/历史/偏好? | → 立即检索 |
|
|
26
|
+
| **B. 完备性** | 我有确凿证据支撑回答? | → 不确定则先验证 |
|
|
27
|
+
| **C. 决策** | A=B=是 | → **调用 CLI 后再输出** |
|
|
28
|
+
|
|
29
|
+
**违规 = 任务失败**
|
|
30
|
+
|
|
31
|
+
## 🏗️ 系统节点
|
|
32
|
+
|
|
33
|
+
推荐创建以下系统级记忆节点:
|
|
34
|
+
|
|
35
|
+
| URI | 作用 |
|
|
36
|
+
|-----|------|
|
|
37
|
+
| `system://boot` | 会话入口,合成核心摘要 |
|
|
38
|
+
| `system://soul` | 身份/行为模式 |
|
|
39
|
+
| `system://user` | 用户信息/偏好 |
|
|
40
|
+
| `system://project` | 项目核心信息 |
|
|
41
|
+
| `system://recent` | 近期事件 |
|
|
42
|
+
|
|
43
|
+
## 📖 读取协议:检索优先
|
|
44
|
+
|
|
45
|
+
### 必须检索的触发条件(任一)
|
|
46
|
+
1. 用户提到:项目名、人名、过往约定
|
|
47
|
+
2. 问题包含:"我记得"、"之前说过"、"我的配置"
|
|
48
|
+
3. 涉及时间线:"上周"、"上次"
|
|
49
|
+
4. disclosure 被触发
|
|
50
|
+
5. 回答依赖"可能"、"大概"
|
|
51
|
+
|
|
52
|
+
### 检索策略
|
|
53
|
+
```bash
|
|
54
|
+
# 确切 URI
|
|
55
|
+
bunx @hippo-memo/cli@latest memory read "system://user"
|
|
56
|
+
|
|
57
|
+
# 大致位置
|
|
58
|
+
bunx @hippo-memo/cli@latest memory search "数据库"
|
|
59
|
+
|
|
60
|
+
# 不确定 - 多个关键词尝试
|
|
61
|
+
bunx @hippo-memo/cli@latest memory search "上周 项目 数据库"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### ❌ 禁止
|
|
65
|
+
- 基于通用知识回答私有信息
|
|
66
|
+
- 使用"可能"、"大概"而不验证
|
|
67
|
+
- 未检索时说"我记得..."
|
|
68
|
+
|
|
69
|
+
### ✅ 正确
|
|
70
|
+
调用 CLI → "根据您的记录..."
|
|
71
|
+
|
|
72
|
+
## ✍️ 写入协议
|
|
73
|
+
|
|
74
|
+
### create_memory 触发条件
|
|
75
|
+
- 新的重要认知/感悟
|
|
76
|
+
- 用户透露重要信息
|
|
77
|
+
- 发生重大事件
|
|
78
|
+
- 跨会话复用的结论
|
|
79
|
+
|
|
80
|
+
### update_memory 触发条件
|
|
81
|
+
- **必须先 `memory read`**,再修正
|
|
82
|
+
- 发现过去认知错误
|
|
83
|
+
- 用户纠正你
|
|
84
|
+
- 信息过时
|
|
85
|
+
|
|
86
|
+
**铁律:改记忆前,先读记忆。**
|
|
87
|
+
|
|
88
|
+
## URI 设计规范
|
|
89
|
+
|
|
90
|
+
### 域名分类
|
|
91
|
+
- `system://` - 系统级记忆(身份、配置)
|
|
92
|
+
- `project://` - 项目级记忆(架构、决策)
|
|
93
|
+
- `user://` - 用户级记忆(偏好、习惯)
|
|
94
|
+
- `knowledge://` - 知识库(技术文档、最佳实践)
|
|
95
|
+
- `temp://` - 临时记忆(会话缓存)
|
|
96
|
+
|
|
97
|
+
### 路径设计
|
|
98
|
+
```
|
|
99
|
+
system://boot
|
|
100
|
+
system://soul
|
|
101
|
+
system://user/preferences
|
|
102
|
+
system://project/{project-name}
|
|
103
|
+
project://myapp/architecture/database
|
|
104
|
+
user://preferences/pkg-manager
|
|
105
|
+
knowledge://tech/auth-jwt
|
|
106
|
+
temp://session/cache
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## CLI 命令
|
|
110
|
+
|
|
111
|
+
### 1. 创建记忆
|
|
112
|
+
```bash
|
|
113
|
+
bunx @hippo-memo/cli@latest memory create "user://note/test" "# 测试笔记\n这是内容"
|
|
114
|
+
```
|
|
115
|
+
选项:
|
|
116
|
+
- `-p, --priority <1-10>` - 优先级(默认: 5)
|
|
117
|
+
- `-d, --disclosure <string>` - 触发条件
|
|
118
|
+
- `--dir <directory>` - 记忆目录(默认: .memory)
|
|
119
|
+
|
|
120
|
+
### 2. 读取记忆
|
|
121
|
+
```bash
|
|
122
|
+
bunx @hippo-memo/cli@latest memory read "user://note/test"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 3. 更新记忆
|
|
126
|
+
```bash
|
|
127
|
+
bunx @hippo-memo/cli@latest memory update "user://note/test" "更新内容"
|
|
128
|
+
```
|
|
129
|
+
选项:
|
|
130
|
+
- `-p, --priority <1-10>` - 更新优先级
|
|
131
|
+
- `-d, --disclosure <string>` - 更新触发条件
|
|
132
|
+
|
|
133
|
+
### 4. 删除记忆
|
|
134
|
+
```bash
|
|
135
|
+
bunx @hippo-memo/cli@latest memory delete "user://note/test"
|
|
136
|
+
```
|
|
137
|
+
选项:
|
|
138
|
+
- `-r, --recursive` - 递归删除子记忆
|
|
139
|
+
- `--permanent` - 永久删除(不进入回收站)
|
|
140
|
+
|
|
141
|
+
### 5. 搜索记忆
|
|
142
|
+
```bash
|
|
143
|
+
bunx @hippo-memo/cli@latest memory search "测试"
|
|
144
|
+
```
|
|
145
|
+
选项:
|
|
146
|
+
- `--domain <string>` - 限定域名
|
|
147
|
+
- `-l, --limit <number>` - 结果数量(默认: 20)
|
|
148
|
+
|
|
149
|
+
### 6. 列出所有记忆
|
|
150
|
+
```bash
|
|
151
|
+
bunx @hippo-memo/cli@latest memory list
|
|
152
|
+
```
|
|
153
|
+
选项:
|
|
154
|
+
- `--domain <string>` - 限定域名
|
|
155
|
+
|
|
156
|
+
### 7. 添加别名
|
|
157
|
+
```bash
|
|
158
|
+
bunx @hippo-memo/cli@latest memory alias "alias://test" "user://note/test"
|
|
159
|
+
```
|
|
160
|
+
选项:
|
|
161
|
+
- `-p, --priority <1-10>` - 别名优先级(继承目标默认)
|
|
162
|
+
- `-d, --disclosure <string>` - 别名触发条件
|
|
163
|
+
|
|
164
|
+
## 🔄 结构操作
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# 移动/重命名:先建后删
|
|
168
|
+
bunx @hippo-memo/cli@latest memory alias "new" "old"
|
|
169
|
+
bunx @hippo-memo/cli@latest memory delete "old"
|
|
170
|
+
|
|
171
|
+
# 删除前:先读确认
|
|
172
|
+
bunx @hippo-memo/cli@latest memory read "to_delete"
|
|
173
|
+
bunx @hippo-memo/cli@latest memory delete "to_delete"
|
|
174
|
+
|
|
175
|
+
# 多重入口
|
|
176
|
+
bunx @hippo-memo/cli@latest memory alias "path/a" "path/b"
|
|
177
|
+
bunx @hippo-memo/cli@latest memory alias "path/c" "path/b"
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## 最佳实践
|
|
181
|
+
|
|
182
|
+
### 优先级指南
|
|
183
|
+
| 级别 | 含义 | 上限 | 示例 |
|
|
184
|
+
|------|------|------|------|
|
|
185
|
+
| **10** | 核心身份 | 5条 | `system://boot`, `system://soul` |
|
|
186
|
+
| **8-9** | 关键事实/高频模式 | 15条 | 用户偏好、项目架构决策 |
|
|
187
|
+
| **5-7** | 一般记忆 | 无限制 | 技术文档、会议记录 |
|
|
188
|
+
| **1-4** | 冷门信息 | 无限制 | 临时缓存、调试信息 |
|
|
189
|
+
|
|
190
|
+
### Disclosure 规范
|
|
191
|
+
Disclosure 定义了记忆的自动触发条件,帮助 LLM 主动检索:
|
|
192
|
+
|
|
193
|
+
✅ **好的 disclosure**:
|
|
194
|
+
```
|
|
195
|
+
"当用户提到项目 hippo 时"
|
|
196
|
+
"当讨论包管理器选择时"
|
|
197
|
+
"当周五或周末安排任务时"
|
|
198
|
+
"当实现认证功能时"
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
❌ **不好的 disclosure**:
|
|
202
|
+
```
|
|
203
|
+
"重要"
|
|
204
|
+
"记住"
|
|
205
|
+
"用户信息"
|
|
206
|
+
"配置"
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### 主动触发场景
|
|
210
|
+
|
|
211
|
+
| 场景 | 示例输入 | 存储动作 |
|
|
212
|
+
|------|----------|----------|
|
|
213
|
+
| **显性请求** | "记住我周末不工作" | `memory create "user://availability/weekend" ...` |
|
|
214
|
+
| **偏好表达** | "我喜欢用 pnpm" | `memory create "user://pref/pkg-manager" "pnpm" --disclosure "当选择包管理器时"` |
|
|
215
|
+
| **架构决策** | "这个项目用 PostgreSQL" | `memory create "project://myapp/database" "PostgreSQL 15+" --disclosure "当讨论数据库时"` |
|
|
216
|
+
| **技术约定** | "组件都用 shadcn/ui" | `memory create "project://myapp/ui-lib" "shadcn/ui" --disclosure "当创建 UI 组件时"` |
|
|
217
|
+
|
|
218
|
+
## 🎭 Few-Shot 示例
|
|
219
|
+
|
|
220
|
+
### 检索优先
|
|
221
|
+
```
|
|
222
|
+
用户:"我上周项目用的什么数据库?"
|
|
223
|
+
|
|
224
|
+
❌ "可能是 PostgreSQL..."
|
|
225
|
+
✅ bunx @hippo-memo/cli@latest memory search "上周 项目 数据库"
|
|
226
|
+
→ bunx @hippo-memo/cli@latest memory read "..."
|
|
227
|
+
→ "根据记录,用的是 MongoDB"
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Disclosure 触发
|
|
231
|
+
```
|
|
232
|
+
用户:"我要实现登录功能"
|
|
233
|
+
|
|
234
|
+
✅ 检测到"登录"
|
|
235
|
+
→ bunx @hippo-memo/cli@latest memory read "knowledge/auth"
|
|
236
|
+
→ "您之前确定用 JWT 方案,对吗?"
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### 写入新记忆
|
|
240
|
+
```
|
|
241
|
+
用户:"我周末不工作"
|
|
242
|
+
|
|
243
|
+
✅ bunx @hippo-memo/cli@latest memory create \
|
|
244
|
+
"user://availability/weekend" \
|
|
245
|
+
"用户周末不工作,周一处理" \
|
|
246
|
+
--priority 8 \
|
|
247
|
+
--disclosure "当周五/周末安排任务时"
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## 🔚 回复前自检
|
|
251
|
+
|
|
252
|
+
- [ ] 涉及私有信息?→ 调用 `memory read`/`memory search`
|
|
253
|
+
- [ ] 不确定?→ 先验证而非猜测
|
|
254
|
+
- [ ] 用户新信息?→ 立即 `memory create`
|
|
255
|
+
- [ ] 引用记忆?→ 标注来源 URI
|
|
256
|
+
|
|
257
|
+
**全部通过 → 输出 | 任一未通过 → 先执行 CLI**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hippo-memo/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"hippo": "./dist/index.js"
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@types/node": "^20",
|
|
19
|
+
"@types/prompts": "^2.4.9",
|
|
19
20
|
"concurrently": "^9.2.1",
|
|
20
21
|
"typescript": "^5"
|
|
21
22
|
}
|
package/src/commands/index.ts
CHANGED
package/src/commands/init.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
access,
|
|
3
3
|
constants,
|
|
4
|
+
cp,
|
|
4
5
|
mkdir,
|
|
5
6
|
readFile,
|
|
6
7
|
writeFile
|
|
@@ -9,10 +10,12 @@ import { EOL } from "node:os";
|
|
|
9
10
|
import { join } from "node:path";
|
|
10
11
|
import { createConfig, createStore } from "@hippo-memo/core";
|
|
11
12
|
import { MEMORY_DIR_NAME } from "@hippo-memo/shared";
|
|
13
|
+
import prompts from "prompts";
|
|
12
14
|
|
|
13
15
|
const __dirname = import.meta.dirname;
|
|
14
16
|
const TEMPLATE_DIR = join(__dirname, "../../template/system");
|
|
15
17
|
const AGENTS_TEMPLATE_PATH = join(__dirname, "../../template/mcp/AGENTS.md");
|
|
18
|
+
const SKILL_TEMPLATE_DIR = join(__dirname, "../../template/skill");
|
|
16
19
|
|
|
17
20
|
const TEMPLATE_URIS = {
|
|
18
21
|
agent: "system://agent",
|
|
@@ -20,6 +23,8 @@ const TEMPLATE_URIS = {
|
|
|
20
23
|
user: "system://user"
|
|
21
24
|
};
|
|
22
25
|
|
|
26
|
+
type InitMode = "mcp" | "skill";
|
|
27
|
+
|
|
23
28
|
async function readTemplateContent(filename: string): Promise<string> {
|
|
24
29
|
return readFile(join(TEMPLATE_DIR, filename), "utf-8");
|
|
25
30
|
}
|
|
@@ -38,15 +43,12 @@ function getInjectionMarker(content: string): string {
|
|
|
38
43
|
*/
|
|
39
44
|
function isAlreadyInjected(content: string, marker: string): boolean {
|
|
40
45
|
const lines = content.split("\n");
|
|
41
|
-
// 跳过开头的空行
|
|
42
46
|
const firstNonEmptyLine = lines.find((line) => line.trim() !== "");
|
|
43
47
|
return firstNonEmptyLine?.trim() === marker;
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
/**
|
|
47
51
|
* 安全地将内容注入到文件顶部
|
|
48
|
-
* @param filePath 目标文件路径
|
|
49
|
-
* @param contentToInject 要注入的内容
|
|
50
52
|
*/
|
|
51
53
|
async function injectToTopOfFile(
|
|
52
54
|
filePath: string,
|
|
@@ -61,10 +63,8 @@ async function injectToTopOfFile(
|
|
|
61
63
|
originalContent = "";
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
// 从注入内容中提取第一行作为标识符
|
|
65
66
|
const injectionMarker = getInjectionMarker(contentToInject);
|
|
66
67
|
|
|
67
|
-
// 幂等性检查:通过第一行判断是否已注入
|
|
68
68
|
if (
|
|
69
69
|
originalContent &&
|
|
70
70
|
isAlreadyInjected(originalContent, injectionMarker)
|
|
@@ -73,7 +73,6 @@ async function injectToTopOfFile(
|
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
// 拼接内容,注意处理换行符
|
|
77
76
|
const separator = originalContent ? `${EOL}${EOL}` : "";
|
|
78
77
|
const updatedContent = `${contentToInject}${separator}${originalContent}`;
|
|
79
78
|
|
|
@@ -109,24 +108,138 @@ async function handleAgentsAndClaudeFiles(directory: string): Promise<void> {
|
|
|
109
108
|
// CLAUDE.md 不存在
|
|
110
109
|
}
|
|
111
110
|
|
|
112
|
-
// 读取 AGENTS.md 模板内容
|
|
113
111
|
const agentsContent = await readFile(AGENTS_TEMPLATE_PATH, "utf-8");
|
|
114
112
|
|
|
115
113
|
if (hasClaude) {
|
|
116
|
-
// CLAUDE.md 存在:优先注入到 CLAUDE.md 顶部
|
|
117
114
|
console.log("› Found: CLAUDE.md");
|
|
118
|
-
|
|
115
|
+
|
|
116
|
+
// 检查是否已注入
|
|
117
|
+
const existingContent = await readFile(claudePath, "utf-8");
|
|
118
|
+
const injectionMarker = getInjectionMarker(agentsContent);
|
|
119
|
+
|
|
120
|
+
if (!isAlreadyInjected(existingContent, injectionMarker)) {
|
|
121
|
+
const response = await prompts({
|
|
122
|
+
type: "confirm",
|
|
123
|
+
name: "inject",
|
|
124
|
+
message: "Inject MCP configuration to CLAUDE.md?",
|
|
125
|
+
initial: true
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (response.inject) {
|
|
129
|
+
await injectToTopOfFile(claudePath, agentsContent);
|
|
130
|
+
} else {
|
|
131
|
+
console.log("› Skipped: CLAUDE.md");
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
console.log("› Skipped: content already exists");
|
|
135
|
+
}
|
|
119
136
|
} else if (hasAgents) {
|
|
120
|
-
// 只有 AGENTS.md 存在:注入到 AGENTS.md 顶部
|
|
121
137
|
console.log("› Found: AGENTS.md");
|
|
122
|
-
|
|
138
|
+
|
|
139
|
+
const existingContent = await readFile(agentsPath, "utf-8");
|
|
140
|
+
const injectionMarker = getInjectionMarker(agentsContent);
|
|
141
|
+
|
|
142
|
+
if (!isAlreadyInjected(existingContent, injectionMarker)) {
|
|
143
|
+
const response = await prompts({
|
|
144
|
+
type: "confirm",
|
|
145
|
+
name: "inject",
|
|
146
|
+
message: "Inject MCP configuration to AGENTS.md?",
|
|
147
|
+
initial: true
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (response.inject) {
|
|
151
|
+
await injectToTopOfFile(agentsPath, agentsContent);
|
|
152
|
+
} else {
|
|
153
|
+
console.log("› Skipped: AGENTS.md");
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
console.log("› Skipped: content already exists");
|
|
157
|
+
}
|
|
123
158
|
} else {
|
|
124
|
-
// 两者都不存在:创建 AGENTS.md
|
|
125
159
|
console.log("✔ Created: AGENTS.md");
|
|
126
160
|
await writeFile(agentsPath, agentsContent, "utf-8");
|
|
127
161
|
}
|
|
128
162
|
}
|
|
129
163
|
|
|
164
|
+
/**
|
|
165
|
+
* 复制 Skill 模板到项目目录
|
|
166
|
+
*/
|
|
167
|
+
async function copySkillTemplates(directory: string): Promise<void> {
|
|
168
|
+
const targetSkillDir = join(directory, ".claude", "skills");
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
await mkdir(targetSkillDir, { recursive: true });
|
|
172
|
+
} catch {
|
|
173
|
+
// 目录可能已存在
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const skills = ["hippo-memory"];
|
|
177
|
+
|
|
178
|
+
for (const skill of skills) {
|
|
179
|
+
const sourceDir = join(SKILL_TEMPLATE_DIR, skill);
|
|
180
|
+
const targetDir = join(targetSkillDir, skill);
|
|
181
|
+
const targetFile = join(targetDir, "SKILL.md");
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
await mkdir(targetDir, { recursive: true });
|
|
185
|
+
|
|
186
|
+
// 检查文件是否已存在
|
|
187
|
+
let fileExists = false;
|
|
188
|
+
try {
|
|
189
|
+
await access(targetFile, constants.F_OK);
|
|
190
|
+
fileExists = true;
|
|
191
|
+
} catch {
|
|
192
|
+
// 文件不存在
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (fileExists) {
|
|
196
|
+
const response = await prompts({
|
|
197
|
+
type: "confirm",
|
|
198
|
+
name: "overwrite",
|
|
199
|
+
message: `Skill ${skill} already exists. Overwrite?`,
|
|
200
|
+
initial: false
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
if (!response.overwrite) {
|
|
204
|
+
console.log(`› Skipped: ${skill}`);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
await cp(join(sourceDir, "SKILL.md"), targetFile);
|
|
210
|
+
console.log(`✔ Installed skill: ${skill}`);
|
|
211
|
+
} catch (error: unknown) {
|
|
212
|
+
console.error(`✖ Failed to install skill: ${skill}`, error);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* 询问用户初始化模式
|
|
219
|
+
*/
|
|
220
|
+
async function askInitMode(): Promise<InitMode> {
|
|
221
|
+
const response = await prompts({
|
|
222
|
+
type: "select",
|
|
223
|
+
name: "mode",
|
|
224
|
+
message: "选择 Claude Code 集成方式:",
|
|
225
|
+
choices: [
|
|
226
|
+
{
|
|
227
|
+
title: "🔌 MCP 模式 (推荐全局配置)",
|
|
228
|
+
description: "适用于跨项目共享记忆,需要单独配置 MCP 服务器",
|
|
229
|
+
value: "mcp"
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
title: "📜 Skill 模式 (推荐项目配置)",
|
|
233
|
+
description: "项目级技能,可提交到 Git 与团队共享",
|
|
234
|
+
value: "skill"
|
|
235
|
+
}
|
|
236
|
+
],
|
|
237
|
+
initial: 1
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
return (response.mode as InitMode) || "skill";
|
|
241
|
+
}
|
|
242
|
+
|
|
130
243
|
export async function init(directory: string): Promise<void> {
|
|
131
244
|
const memoryRoot = join(directory, MEMORY_DIR_NAME);
|
|
132
245
|
|
|
@@ -134,7 +247,6 @@ export async function init(directory: string): Promise<void> {
|
|
|
134
247
|
await access(memoryRoot, constants.F_OK);
|
|
135
248
|
console.log(`Memory directory already exists at ${memoryRoot}`);
|
|
136
249
|
} catch {
|
|
137
|
-
// Directory doesn't exist, proceed with creation
|
|
138
250
|
await mkdir(memoryRoot, { recursive: true });
|
|
139
251
|
|
|
140
252
|
const store = createStore({
|
|
@@ -149,10 +261,32 @@ export async function init(directory: string): Promise<void> {
|
|
|
149
261
|
})
|
|
150
262
|
);
|
|
151
263
|
|
|
152
|
-
console.log(
|
|
264
|
+
console.log(`✔ Initialized hippo memory at ${memoryRoot}`);
|
|
153
265
|
}
|
|
154
266
|
|
|
155
|
-
//
|
|
156
|
-
|
|
157
|
-
|
|
267
|
+
// 询问初始化模式
|
|
268
|
+
const mode = await askInitMode();
|
|
269
|
+
|
|
270
|
+
// 处理 MCP 配置 (注入到 AGENTS.md/CLAUDE.md)
|
|
271
|
+
if (mode === "mcp") {
|
|
272
|
+
console.log(`${EOL}◇ Setting up MCP integration...`);
|
|
273
|
+
await handleAgentsAndClaudeFiles(directory);
|
|
274
|
+
console.log(`${EOL}✨ Setup complete!${EOL}`);
|
|
275
|
+
console.log("📌 Next steps:");
|
|
276
|
+
console.log(" 1. Configure MCP server in Claude Code settings");
|
|
277
|
+
console.log(" 2. Run: hippo web -p 3000");
|
|
278
|
+
console.log("");
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// 处理 Skill 配置
|
|
282
|
+
if (mode === "skill") {
|
|
283
|
+
console.log(`${EOL}◇ Installing Claude Code Skills...`);
|
|
284
|
+
await copySkillTemplates(directory);
|
|
285
|
+
console.log(`${EOL}✨ Setup complete!${EOL}`);
|
|
286
|
+
console.log("📌 Next steps:");
|
|
287
|
+
console.log(" 1. Skills installed in .claude/skills/");
|
|
288
|
+
console.log(" 2. LLM will automatically trigger when needed");
|
|
289
|
+
console.log(" 3. Commit to Git to share with your team");
|
|
290
|
+
console.log("");
|
|
291
|
+
}
|
|
158
292
|
}
|