@hiveai/mcp 0.2.7 → 0.2.9
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/README.md +362 -0
- package/dist/index.js +170 -12
- package/dist/index.js.map +1 -1
- package/dist/server.js +170 -12
- package/dist/server.js.map +1 -1
- package/package.json +13 -24
package/README.md
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# @hiveai/mcp
|
|
2
|
+
|
|
3
|
+
> **hAIve MCP server** — exposes shared team memory and project context to any MCP-compatible AI client (Claude Code, Cursor, GitHub Copilot, VS Code, etc.)
|
|
4
|
+
|
|
5
|
+
Connect your AI coding tools to a shared, version-controlled knowledge base. Every convention, architectural decision, and gotcha your team has discovered is surfaced automatically when relevant — no more re-explaining the same things in every session.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @hiveai/mcp
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Also install the CLI to manage memories:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g @hiveai/cli
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# 1. Initialize hAIve in your project
|
|
27
|
+
haive init
|
|
28
|
+
|
|
29
|
+
# 2. Add your AI client config (see below)
|
|
30
|
+
# 3. Ask your AI to call get_briefing before starting any task
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Client configuration
|
|
36
|
+
|
|
37
|
+
### Claude Code
|
|
38
|
+
|
|
39
|
+
Add to `~/.claude.json` (global) or `.claude/settings.json` (per-project):
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"mcpServers": {
|
|
44
|
+
"haive": {
|
|
45
|
+
"command": "haive-mcp",
|
|
46
|
+
"args": ["--root", "/absolute/path/to/your/project"]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Cursor
|
|
53
|
+
|
|
54
|
+
Add to `~/.cursor/mcp.json`:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"haive": {
|
|
60
|
+
"command": "haive-mcp",
|
|
61
|
+
"args": ["--root", "/absolute/path/to/your/project"]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### VS Code
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
code --add-mcp '{"name":"haive","command":"haive-mcp","args":["--root","/absolute/path/to/project"]}'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Project-scoped (auto-detected)
|
|
74
|
+
|
|
75
|
+
Add a `.mcp.json` at the project root:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"mcpServers": {
|
|
80
|
+
"haive": {
|
|
81
|
+
"command": "haive-mcp",
|
|
82
|
+
"args": ["--root", "."]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The project root can also be set via the `HAIVE_PROJECT_ROOT` environment variable, or auto-detected from the nearest `.ai/`, `.git/`, or `package.json`.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## MCP Tools
|
|
93
|
+
|
|
94
|
+
### `get_briefing` ⭐ Start every task with this
|
|
95
|
+
|
|
96
|
+
One-shot onboarding: returns project context + module contexts + ranked relevant memories under a token budget. Replaces 4–5 separate calls at the start of a session.
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"task": "add a Stripe payment integration",
|
|
101
|
+
"files": ["src/payments/PaymentService.ts"],
|
|
102
|
+
"max_tokens": 8000,
|
|
103
|
+
"max_memories": 8,
|
|
104
|
+
"format": "full",
|
|
105
|
+
"semantic": true
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Parameters:**
|
|
110
|
+
|
|
111
|
+
| Parameter | Default | Description |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| `task` | — | What you're about to do. Used to rank memories by relevance. |
|
|
114
|
+
| `files` | `[]` | Files you're editing. Surfaces memories anchored to these files. |
|
|
115
|
+
| `max_tokens` | `8000` | Token budget for the entire response. Sections are truncated to fit. |
|
|
116
|
+
| `max_memories` | `8` | Max memories to include. |
|
|
117
|
+
| `format` | `"full"` | `"full"` = complete bodies · `"compact"` = 1-line summaries (call `mem_get` for details) |
|
|
118
|
+
| `semantic` | `true` | Use embedding-based ranking if `@hiveai/embeddings` is indexed. |
|
|
119
|
+
| `include_stale` | `false` | Include stale memories (may be outdated). |
|
|
120
|
+
| `track` | `true` | Increment read_count for returned memories. |
|
|
121
|
+
|
|
122
|
+
**Response includes:**
|
|
123
|
+
- `project_context` — the contents of `.ai/project-context.md`
|
|
124
|
+
- `module_contexts` — relevant `.ai/modules/<name>/context.md` files
|
|
125
|
+
- `memories` — ranked list of relevant memories with body, confidence, and match reason
|
|
126
|
+
- `decay_warnings` — memory IDs not read in >90 days (review or deprecate)
|
|
127
|
+
- `search_mode` — `"semantic"` | `"literal_fallback"` | `"literal"`
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### `mem_save`
|
|
132
|
+
|
|
133
|
+
Save a new memory. For failed approaches, use `mem_tried` instead — it enforces better structure.
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"type": "gotcha",
|
|
138
|
+
"slug": "open-in-view-false",
|
|
139
|
+
"scope": "team",
|
|
140
|
+
"body": "spring.jpa.open-in-view=false is intentional — do not re-enable. Lazy loading outside transactions causes N+1 queries.",
|
|
141
|
+
"paths": ["src/main/resources/application.properties"],
|
|
142
|
+
"tags": ["spring", "jpa", "performance"]
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
| Parameter | Required | Description |
|
|
147
|
+
|---|---|---|
|
|
148
|
+
| `type` | ✅ | `convention` · `decision` · `gotcha` · `architecture` · `glossary` |
|
|
149
|
+
| `slug` | ✅ | Short kebab-case identifier |
|
|
150
|
+
| `scope` | — | `personal` (default) · `team` · `module` |
|
|
151
|
+
| `body` | ✅ | Markdown content |
|
|
152
|
+
| `paths` | — | File paths to anchor to (enables staleness detection) |
|
|
153
|
+
| `symbols` | — | Function/class names to anchor to |
|
|
154
|
+
| `tags` | — | Tags for filtering |
|
|
155
|
+
| `domain` | — | Business domain (e.g. `payments`) |
|
|
156
|
+
| `author` | — | Author handle |
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### `mem_tried` ⭐ Record failures immediately
|
|
161
|
+
|
|
162
|
+
Record a failed approach. Automatically surfaces first in future `get_briefing` calls so agents don't repeat the same mistake.
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"what": "using require() to import gray-matter in an ESM package",
|
|
167
|
+
"why_failed": "The package is ESM-only — require() throws ERR_REQUIRE_ESM",
|
|
168
|
+
"instead": "Use import matter from 'gray-matter' (named default import)",
|
|
169
|
+
"scope": "team",
|
|
170
|
+
"paths": ["src/parser.ts"]
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Auto-validated — no approval cycle needed.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
### `mem_search`
|
|
179
|
+
|
|
180
|
+
Search memories by substring or semantic similarity.
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"query": "flyway migration",
|
|
185
|
+
"scope": "team",
|
|
186
|
+
"semantic": true,
|
|
187
|
+
"limit": 10
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Falls back to literal search if embeddings are not indexed.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
### `mem_get`
|
|
196
|
+
|
|
197
|
+
Fetch a single memory with full body, anchor, confidence, and usage stats.
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{ "id": "2025-01-15-gotcha-flyway-strict" }
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
### `mem_list`
|
|
206
|
+
|
|
207
|
+
List memories with optional filters.
|
|
208
|
+
|
|
209
|
+
```json
|
|
210
|
+
{
|
|
211
|
+
"scope": "team",
|
|
212
|
+
"type": "gotcha",
|
|
213
|
+
"status": "validated",
|
|
214
|
+
"tags": ["payments"]
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
### `mem_for_files`
|
|
221
|
+
|
|
222
|
+
Given the files you're editing, return relevant memories grouped by reason (anchor overlap, module, domain).
|
|
223
|
+
|
|
224
|
+
```json
|
|
225
|
+
{
|
|
226
|
+
"files": ["src/payments/PaymentService.java", "src/payments/WaveProvider.java"]
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
### `mem_update`
|
|
233
|
+
|
|
234
|
+
Update a memory's body, tags, or anchor without changing its id or usage history.
|
|
235
|
+
|
|
236
|
+
```json
|
|
237
|
+
{
|
|
238
|
+
"id": "2025-01-15-gotcha-flyway-strict",
|
|
239
|
+
"body": "Updated explanation...",
|
|
240
|
+
"paths": ["src/main/resources/db/migration"]
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
### `mem_verify`
|
|
247
|
+
|
|
248
|
+
Check if anchor paths and symbols still exist in the current code. Detects stale memories and suggests possible renames when files have moved.
|
|
249
|
+
|
|
250
|
+
```json
|
|
251
|
+
{ "id": "2025-01-15-gotcha-flyway-strict", "update": true }
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
### `mem_diff`
|
|
257
|
+
|
|
258
|
+
Compare two memories side-by-side: shows frontmatter fields that differ and lines unique to each body. Useful before merging duplicates.
|
|
259
|
+
|
|
260
|
+
```json
|
|
261
|
+
{ "id_a": "2025-01-15-gotcha-flyway-strict", "id_b": "2025-02-01-decision-flyway-naming" }
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
### `mem_approve` / `mem_reject` / `mem_pending` / `mem_delete`
|
|
267
|
+
|
|
268
|
+
Lifecycle operations:
|
|
269
|
+
|
|
270
|
+
```json
|
|
271
|
+
{ "id": "2025-01-15-gotcha-flyway-strict" } // mem_approve
|
|
272
|
+
{ "id": "2025-01-15-gotcha-old", "reason": "Outdated" } // mem_reject
|
|
273
|
+
{} // mem_pending (list all)
|
|
274
|
+
{ "id": "2025-01-15-gotcha-old" } // mem_delete
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
### `get_project_context`
|
|
280
|
+
|
|
281
|
+
Read `.ai/project-context.md` directly (without token budgeting).
|
|
282
|
+
|
|
283
|
+
```json
|
|
284
|
+
{ "module": "payments" } // Also loads .ai/modules/payments/context.md
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
### `bootstrap_project_save`
|
|
290
|
+
|
|
291
|
+
Persist a project (or module) context document generated by the AI.
|
|
292
|
+
|
|
293
|
+
```json
|
|
294
|
+
{
|
|
295
|
+
"content": "# Project context\n\n## Architecture\n...",
|
|
296
|
+
"module": "payments" // Optional: save as .ai/modules/payments/context.md
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
### `code_map`
|
|
303
|
+
|
|
304
|
+
Browse the pre-computed code map (file → exports + descriptions) instead of grepping.
|
|
305
|
+
|
|
306
|
+
```json
|
|
307
|
+
{ "query": "payment" } // Filter by keyword
|
|
308
|
+
{ "file": "src/payments" } // Filter by file prefix
|
|
309
|
+
{ "symbol": "PaymentService" } // Find a specific export
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Requires `haive index code` to be run first.
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## MCP Prompts
|
|
317
|
+
|
|
318
|
+
### `post_task` ⭐ Run before closing every session
|
|
319
|
+
|
|
320
|
+
Post-task reflection checklist. Guides the AI through capturing failed approaches, conventions, decisions, and gotchas before the session ends.
|
|
321
|
+
|
|
322
|
+
```
|
|
323
|
+
Use the post_task prompt with:
|
|
324
|
+
task_summary: "Added Stripe payment integration"
|
|
325
|
+
files_touched: ["src/payments/StripeService.ts", "src/payments/PaymentController.ts"]
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### `bootstrap_project`
|
|
329
|
+
|
|
330
|
+
Instructions for the AI to analyze the current project and save a structured context document to `.ai/project-context.md`. Run once after `haive init`.
|
|
331
|
+
|
|
332
|
+
### `import_docs`
|
|
333
|
+
|
|
334
|
+
Analyze documentation (README, ADR, wiki page, API spec) and save actionable knowledge as hAIve memories.
|
|
335
|
+
|
|
336
|
+
```
|
|
337
|
+
Use the import_docs prompt with:
|
|
338
|
+
content: "<full document text>"
|
|
339
|
+
source: "docs/architecture.md"
|
|
340
|
+
scope: "team"
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
The AI extracts up to 10 memories (conventions, decisions, gotchas, architecture) and calls `mem_save` for each.
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Memory lifecycle
|
|
348
|
+
|
|
349
|
+
```
|
|
350
|
+
mem_save / mem_tried → draft (personal) or proposed (team)
|
|
351
|
+
mem_approve → validated
|
|
352
|
+
mem_verify → stale (if anchors broken)
|
|
353
|
+
mem_reject → rejected
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Validated team memories appear in `get_briefing`. Stale memories are excluded by default (pass `include_stale: true` to override).
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## License
|
|
361
|
+
|
|
362
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -397,6 +397,7 @@ async function memVerify(input, ctx) {
|
|
|
397
397
|
file_path: filePath,
|
|
398
398
|
stale: result.stale,
|
|
399
399
|
reason: result.reason,
|
|
400
|
+
...result.possibleRenames.length > 0 ? { possible_renames: result.possibleRenames } : {},
|
|
400
401
|
status_after: statusAfter
|
|
401
402
|
});
|
|
402
403
|
}
|
|
@@ -859,6 +860,7 @@ import {
|
|
|
859
860
|
estimateTokens,
|
|
860
861
|
getUsage as getUsage5,
|
|
861
862
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
863
|
+
isDecaying,
|
|
862
864
|
literalMatchesAllTokens as literalMatchesAllTokens2,
|
|
863
865
|
loadMemoriesFromDir as loadMemoriesFromDir12,
|
|
864
866
|
loadUsageIndex as loadUsageIndex7,
|
|
@@ -883,12 +885,17 @@ var GetBriefingInputSchema = {
|
|
|
883
885
|
"Use semantic ranking when a task is provided (requires `haive embeddings index`)."
|
|
884
886
|
),
|
|
885
887
|
include_stale: z15.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
|
|
886
|
-
track: z15.boolean().default(true).describe("Increment read_count on returned memories")
|
|
888
|
+
track: z15.boolean().default(true).describe("Increment read_count on returned memories"),
|
|
889
|
+
format: z15.enum(["full", "compact"]).default("full").describe(
|
|
890
|
+
"Output format: 'full' returns complete memory bodies; 'compact' returns id + 1-line summary only (call mem_get for details)."
|
|
891
|
+
)
|
|
887
892
|
};
|
|
888
893
|
async function getBriefing(input, ctx) {
|
|
889
894
|
const inferred = inferModulesFromPaths2(input.files);
|
|
890
895
|
const memories = [];
|
|
891
896
|
let searchMode = "literal";
|
|
897
|
+
let usage = { version: 1, updated_at: "", by_id: {} };
|
|
898
|
+
let byId = /* @__PURE__ */ new Map();
|
|
892
899
|
if (existsSync15(ctx.paths.memoriesDir)) {
|
|
893
900
|
const allLoaded = await loadMemoriesFromDir12(ctx.paths.memoriesDir);
|
|
894
901
|
const allMemories = allLoaded.filter(({ memory }) => {
|
|
@@ -897,7 +904,7 @@ async function getBriefing(input, ctx) {
|
|
|
897
904
|
if (!input.include_stale && s === "stale") return false;
|
|
898
905
|
return true;
|
|
899
906
|
});
|
|
900
|
-
|
|
907
|
+
usage = await loadUsageIndex7(ctx.paths);
|
|
901
908
|
const semanticHits = input.task && input.semantic ? await trySemanticHits(ctx, input.task, allMemories.length * 2) : null;
|
|
902
909
|
if (input.task && input.semantic) {
|
|
903
910
|
searchMode = semanticHits ? "semantic" : "literal_fallback";
|
|
@@ -954,7 +961,6 @@ async function getBriefing(input, ctx) {
|
|
|
954
961
|
}
|
|
955
962
|
}
|
|
956
963
|
if (semanticHits) {
|
|
957
|
-
const byId = new Map(allMemories.map((m) => [m.memory.frontmatter.id, m]));
|
|
958
964
|
for (const hit of semanticHits) {
|
|
959
965
|
const loaded = byId.get(hit.id);
|
|
960
966
|
if (loaded) addOrUpdate(loaded, "semantic", hit.score, "semantic");
|
|
@@ -969,6 +975,17 @@ async function getBriefing(input, ctx) {
|
|
|
969
975
|
const sb = reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
|
|
970
976
|
return sb - sa;
|
|
971
977
|
});
|
|
978
|
+
byId = new Map(allMemories.map((m) => [m.memory.frontmatter.id, m]));
|
|
979
|
+
for (const mem of ranked.slice(0, input.max_memories)) {
|
|
980
|
+
if (seen.size >= input.max_memories * 2) break;
|
|
981
|
+
const loaded = byId.get(mem.id);
|
|
982
|
+
if (!loaded) continue;
|
|
983
|
+
for (const relId of loaded.memory.frontmatter.related_ids ?? []) {
|
|
984
|
+
if (seen.has(relId)) continue;
|
|
985
|
+
const related = byId.get(relId);
|
|
986
|
+
if (related) addOrUpdate(related, "anchor", void 0, "partial");
|
|
987
|
+
}
|
|
988
|
+
}
|
|
972
989
|
memories.push(...ranked.slice(0, input.max_memories));
|
|
973
990
|
if (input.track && memories.length > 0) {
|
|
974
991
|
await trackReads3(ctx.paths, memories.map((m) => m.id));
|
|
@@ -1009,7 +1026,6 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1009
1026
|
trimmedModules.push({ name: m.name, content: sub.text, truncated: sub.truncated });
|
|
1010
1027
|
}
|
|
1011
1028
|
}
|
|
1012
|
-
const trimmedMemoriesText = memoriesSlice.text;
|
|
1013
1029
|
const trimmedMemories = [];
|
|
1014
1030
|
if (!memoriesSlice.truncated) {
|
|
1015
1031
|
trimmedMemories.push(...memories);
|
|
@@ -1029,13 +1045,22 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1029
1045
|
}
|
|
1030
1046
|
}
|
|
1031
1047
|
const totalTokens = projectSlice.estimatedTokens + modulesSlice.estimatedTokens + memoriesSlice.estimatedTokens;
|
|
1048
|
+
const decayWarnings = [];
|
|
1049
|
+
for (const m of trimmedMemories) {
|
|
1050
|
+
const u = getUsage5(usage, m.id);
|
|
1051
|
+
const loaded = byId.get(m.id);
|
|
1052
|
+
const createdAt = loaded?.memory.frontmatter.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1053
|
+
if (isDecaying(u, createdAt)) decayWarnings.push(m.id);
|
|
1054
|
+
}
|
|
1055
|
+
const outputMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : trimmedMemories;
|
|
1032
1056
|
return {
|
|
1033
1057
|
...input.task ? { task: input.task } : {},
|
|
1034
1058
|
search_mode: searchMode,
|
|
1035
1059
|
inferred_modules: inferred,
|
|
1036
1060
|
project_context: projectContext ? { content: projectSlice.text, truncated: projectSlice.truncated } : null,
|
|
1037
1061
|
module_contexts: trimmedModules,
|
|
1038
|
-
memories:
|
|
1062
|
+
memories: outputMemories,
|
|
1063
|
+
decay_warnings: decayWarnings,
|
|
1039
1064
|
estimated_tokens: totalTokens,
|
|
1040
1065
|
budget: {
|
|
1041
1066
|
max_tokens: input.max_tokens,
|
|
@@ -1047,6 +1072,13 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1047
1072
|
}
|
|
1048
1073
|
};
|
|
1049
1074
|
}
|
|
1075
|
+
function compactSummary(body) {
|
|
1076
|
+
for (const line of body.split("\n")) {
|
|
1077
|
+
const trimmed = line.replace(/^#+\s*/, "").trim();
|
|
1078
|
+
if (trimmed.length > 0) return trimmed.slice(0, 120);
|
|
1079
|
+
}
|
|
1080
|
+
return body.slice(0, 120);
|
|
1081
|
+
}
|
|
1050
1082
|
async function trySemanticHits(ctx, task, limit) {
|
|
1051
1083
|
let mod;
|
|
1052
1084
|
try {
|
|
@@ -1106,13 +1138,58 @@ async function codeMapTool(input, ctx) {
|
|
|
1106
1138
|
};
|
|
1107
1139
|
}
|
|
1108
1140
|
|
|
1109
|
-
// src/
|
|
1141
|
+
// src/tools/mem-diff.ts
|
|
1142
|
+
import { existsSync as existsSync16 } from "fs";
|
|
1143
|
+
import { loadMemoriesFromDir as loadMemoriesFromDir13 } from "@hiveai/core";
|
|
1110
1144
|
import { z as z17 } from "zod";
|
|
1145
|
+
var MemDiffInputSchema = {
|
|
1146
|
+
id_a: z17.string().min(1).describe("First memory id"),
|
|
1147
|
+
id_b: z17.string().min(1).describe("Second memory id")
|
|
1148
|
+
};
|
|
1149
|
+
async function memDiff(input, ctx) {
|
|
1150
|
+
if (!existsSync16(ctx.paths.memoriesDir)) {
|
|
1151
|
+
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
1152
|
+
}
|
|
1153
|
+
const all = await loadMemoriesFromDir13(ctx.paths.memoriesDir);
|
|
1154
|
+
const foundA = all.find((m) => m.memory.frontmatter.id === input.id_a);
|
|
1155
|
+
const foundB = all.find((m) => m.memory.frontmatter.id === input.id_b);
|
|
1156
|
+
if (!foundA) throw new Error(`No memory with id "${input.id_a}".`);
|
|
1157
|
+
if (!foundB) throw new Error(`No memory with id "${input.id_b}".`);
|
|
1158
|
+
const fmA = foundA.memory.frontmatter;
|
|
1159
|
+
const fmB = foundB.memory.frontmatter;
|
|
1160
|
+
const frontmatterDiff = {};
|
|
1161
|
+
const allKeys = /* @__PURE__ */ new Set([...Object.keys(fmA), ...Object.keys(fmB)]);
|
|
1162
|
+
for (const key of allKeys) {
|
|
1163
|
+
const va = fmA[key];
|
|
1164
|
+
const vb = fmB[key];
|
|
1165
|
+
if (JSON.stringify(va) !== JSON.stringify(vb)) {
|
|
1166
|
+
frontmatterDiff[key] = { a: va, b: vb };
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
const linesA = new Set(foundA.memory.body.split("\n").map((l) => l.trim()).filter(Boolean));
|
|
1170
|
+
const linesB = new Set(foundB.memory.body.split("\n").map((l) => l.trim()).filter(Boolean));
|
|
1171
|
+
const onlyA = [...linesA].filter((l) => !linesB.has(l));
|
|
1172
|
+
const onlyB = [...linesB].filter((l) => !linesA.has(l));
|
|
1173
|
+
const common = [...linesA].filter((l) => linesB.has(l)).length;
|
|
1174
|
+
return {
|
|
1175
|
+
id_a: input.id_a,
|
|
1176
|
+
id_b: input.id_b,
|
|
1177
|
+
frontmatter_diff: frontmatterDiff,
|
|
1178
|
+
body_diff: {
|
|
1179
|
+
lines_only_in_a: onlyA,
|
|
1180
|
+
lines_only_in_b: onlyB,
|
|
1181
|
+
common_lines: common
|
|
1182
|
+
}
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
// src/prompts/bootstrap-project.ts
|
|
1187
|
+
import { z as z18 } from "zod";
|
|
1111
1188
|
var BootstrapProjectArgsSchema = {
|
|
1112
|
-
module:
|
|
1189
|
+
module: z18.string().optional().describe(
|
|
1113
1190
|
"Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
|
|
1114
1191
|
),
|
|
1115
|
-
focus:
|
|
1192
|
+
focus: z18.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
|
|
1116
1193
|
};
|
|
1117
1194
|
var ROOT_TEMPLATE = `# Project context
|
|
1118
1195
|
|
|
@@ -1194,10 +1271,10 @@ ${template}\`\`\`
|
|
|
1194
1271
|
}
|
|
1195
1272
|
|
|
1196
1273
|
// src/prompts/post-task.ts
|
|
1197
|
-
import { z as
|
|
1274
|
+
import { z as z19 } from "zod";
|
|
1198
1275
|
var PostTaskArgsSchema = {
|
|
1199
|
-
task_summary:
|
|
1200
|
-
files_touched:
|
|
1276
|
+
task_summary: z19.string().optional().describe("One sentence describing what you just did"),
|
|
1277
|
+
files_touched: z19.array(z19.string()).optional().describe("Files you created or modified during the task")
|
|
1201
1278
|
};
|
|
1202
1279
|
function postTaskPrompt(args, ctx) {
|
|
1203
1280
|
const taskLine = args.task_summary ? `
|
|
@@ -1253,9 +1330,78 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]" or "N
|
|
|
1253
1330
|
};
|
|
1254
1331
|
}
|
|
1255
1332
|
|
|
1333
|
+
// src/prompts/import-docs.ts
|
|
1334
|
+
import { z as z20 } from "zod";
|
|
1335
|
+
var ImportDocsArgsSchema = {
|
|
1336
|
+
content: z20.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
|
|
1337
|
+
source: z20.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
|
|
1338
|
+
scope: z20.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
|
|
1339
|
+
dry_run: z20.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
|
|
1340
|
+
};
|
|
1341
|
+
function importDocsPrompt(args, ctx) {
|
|
1342
|
+
const sourceLine = args.source ? `
|
|
1343
|
+
Source: **${args.source}**` : "";
|
|
1344
|
+
const dryRunNote = args.dry_run ? "\n> **DRY RUN** \u2014 describe what you would save but do not call any tools." : "";
|
|
1345
|
+
const text = `You are given documentation to analyze and import into the hAIve memory system.
|
|
1346
|
+
${sourceLine}
|
|
1347
|
+
Scope: **${args.scope}**
|
|
1348
|
+
Project root: \`${ctx.paths.root}\`
|
|
1349
|
+
${dryRunNote}
|
|
1350
|
+
|
|
1351
|
+
## Your task
|
|
1352
|
+
|
|
1353
|
+
Read the documentation below and extract actionable memories. For each distinct piece of knowledge:
|
|
1354
|
+
|
|
1355
|
+
1. **Identify the memory type** \u2014 which category fits best?
|
|
1356
|
+
- \`convention\` \u2014 how things are done here (naming, patterns, workflow)
|
|
1357
|
+
- \`decision\` \u2014 a choice that was made and why (tradeoffs, constraints)
|
|
1358
|
+
- \`gotcha\` \u2014 non-obvious behavior, traps, things that surprise newcomers
|
|
1359
|
+
- \`architecture\` \u2014 structural overview of a system or module
|
|
1360
|
+
- \`glossary\` \u2014 domain terms and their meaning in this project
|
|
1361
|
+
|
|
1362
|
+
2. **Determine the anchor** \u2014 which files or symbols does this knowledge apply to? List them in \`paths\`.
|
|
1363
|
+
|
|
1364
|
+
3. **Write a focused body** \u2014 one memory = one insight. Do not combine multiple unrelated facts.
|
|
1365
|
+
- Start with the key fact or rule
|
|
1366
|
+
- Add context: why it matters, when it applies
|
|
1367
|
+
- Add examples if helpful
|
|
1368
|
+
|
|
1369
|
+
4. **Call \`mem_save\`** for each memory (unless dry_run).
|
|
1370
|
+
- Set \`scope="${args.scope}"\`
|
|
1371
|
+
- Set \`slug\` to a short kebab-case identifier
|
|
1372
|
+
- Set \`paths\` to the relevant file paths (extracted from the doc if present)
|
|
1373
|
+
|
|
1374
|
+
## Rules
|
|
1375
|
+
|
|
1376
|
+
- Skip generic documentation that applies to any project (e.g., "install with npm install").
|
|
1377
|
+
- Prioritize gotchas, non-obvious decisions, and domain-specific conventions.
|
|
1378
|
+
- If the same knowledge is repeated in different sections, save it once.
|
|
1379
|
+
- Maximum 10 memories per import \u2014 select the most actionable ones.
|
|
1380
|
+
|
|
1381
|
+
## Documentation to import
|
|
1382
|
+
|
|
1383
|
+
---
|
|
1384
|
+
|
|
1385
|
+
${args.content}
|
|
1386
|
+
|
|
1387
|
+
---
|
|
1388
|
+
|
|
1389
|
+
When done, respond with: "Imported N memories: [list of IDs]" or "Nothing actionable found."
|
|
1390
|
+
`;
|
|
1391
|
+
return {
|
|
1392
|
+
description: "Import documentation as hAIve memories",
|
|
1393
|
+
messages: [
|
|
1394
|
+
{
|
|
1395
|
+
role: "user",
|
|
1396
|
+
content: { type: "text", text }
|
|
1397
|
+
}
|
|
1398
|
+
]
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1256
1402
|
// src/server.ts
|
|
1257
1403
|
var SERVER_NAME = "haive";
|
|
1258
|
-
var SERVER_VERSION = "0.2.
|
|
1404
|
+
var SERVER_VERSION = "0.2.9";
|
|
1259
1405
|
function jsonResult(data) {
|
|
1260
1406
|
return {
|
|
1261
1407
|
content: [
|
|
@@ -1368,6 +1514,12 @@ function createHaiveServer(options = {}) {
|
|
|
1368
1514
|
MemTriedInputSchema,
|
|
1369
1515
|
async (input) => jsonResult(await memTried(input, context))
|
|
1370
1516
|
);
|
|
1517
|
+
server.tool(
|
|
1518
|
+
"mem_diff",
|
|
1519
|
+
"Compare two memories side-by-side: shows frontmatter fields that differ and lines unique to each body. Useful before merging or deduplicating memories.",
|
|
1520
|
+
MemDiffInputSchema,
|
|
1521
|
+
async (input) => jsonResult(await memDiff(input, context))
|
|
1522
|
+
);
|
|
1371
1523
|
server.prompt(
|
|
1372
1524
|
"bootstrap_project",
|
|
1373
1525
|
"Instructions for the AI client to analyze the project and save the context.",
|
|
@@ -1380,6 +1532,12 @@ function createHaiveServer(options = {}) {
|
|
|
1380
1532
|
PostTaskArgsSchema,
|
|
1381
1533
|
(args) => postTaskPrompt(args, context)
|
|
1382
1534
|
);
|
|
1535
|
+
server.prompt(
|
|
1536
|
+
"import_docs",
|
|
1537
|
+
"Analyze documentation (README, ADR, wiki page, etc.) and save the actionable knowledge as hAIve memories. Pass the content and an optional source/scope.",
|
|
1538
|
+
ImportDocsArgsSchema,
|
|
1539
|
+
(args) => importDocsPrompt(args, context)
|
|
1540
|
+
);
|
|
1383
1541
|
return { server, context };
|
|
1384
1542
|
}
|
|
1385
1543
|
|