@hiveai/cli 0.9.3 → 0.9.5
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 +427 -149
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command44 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/briefing.ts
|
|
7
7
|
import { existsSync } from "fs";
|
|
@@ -197,7 +197,7 @@ async function getHotFiles(root, daysBack, maxHotFiles, filePaths) {
|
|
|
197
197
|
if (!f) continue;
|
|
198
198
|
counts.set(f, (counts.get(f) ?? 0) + 1);
|
|
199
199
|
}
|
|
200
|
-
let entries = [...counts.entries()].map(([
|
|
200
|
+
let entries = [...counts.entries()].map(([path41, changes]) => ({ path: path41, changes }));
|
|
201
201
|
const lowerPaths = filePaths.map((p) => p.toLowerCase());
|
|
202
202
|
if (lowerPaths.length > 0) {
|
|
203
203
|
entries = entries.filter((e) => lowerPaths.some((p) => e.path.toLowerCase().includes(p)));
|
|
@@ -1882,6 +1882,7 @@ var PROJECT_CONTEXT_TEMPLATE = `# Project context
|
|
|
1882
1882
|
|
|
1883
1883
|
> Generated by \`haive init\`. Run \`haive init --bootstrap\` to auto-fill from your codebase,
|
|
1884
1884
|
> or invoke the MCP prompt \`bootstrap_project\` in your AI client for a richer AI-generated version.
|
|
1885
|
+
> **AI agents**: use the **haive** MCP (\`get_briefing\` / \`mem_relevant_to\`) before substantive edits \u2014 see bridge files and \`.cursor/rules/haive-mcp-required.mdc\`.
|
|
1885
1886
|
|
|
1886
1887
|
## Architecture
|
|
1887
1888
|
TODO \u2014 high-level overview of the codebase.
|
|
@@ -1906,16 +1907,47 @@ Memories live under \`.ai/memories/\` (personal/team/module).
|
|
|
1906
1907
|
|
|
1907
1908
|
## hAIve \u2014 mandatory rules for AI agents
|
|
1908
1909
|
|
|
1910
|
+
Installing or initializing hAIve in this repository means **the team expects AI agents to drive work through hAIve**, not bypass it.
|
|
1911
|
+
|
|
1912
|
+
### MCP first (default \u2014 never optional unless the developer says so)
|
|
1913
|
+
The **haive** MCP server must be enabled in your client. Until it is unavailable (misconfiguration report to the developer), you **must** use it for substantive work:
|
|
1914
|
+
|
|
1915
|
+
- **Before** you edit project source files, configs (\`package.json\`, tooling), or workflows for the current goal, invoke \`get_briefing\` with a short **task** string (and **files** / **symbols** when you know them). If project context was already refreshed in this session and you only need ranked memories, you may call \`mem_relevant_to\` instead\u2014but **still** consult hAIve before trusting ungrounded guesses.
|
|
1916
|
+
- **Before** declaring a coding task finished, invoke the MCP prompt \`post_task\` when the session produced learnings or substantive changes worth capturing.
|
|
1917
|
+
|
|
1918
|
+
Do **not** treat these as nice-to-have: they are **default-required** for coding agents here.
|
|
1919
|
+
|
|
1909
1920
|
### Session rules
|
|
1910
|
-
- **Before** marking a task as done ("Done", "Finished", "C'est fait"), call the MCP prompt \`post_task\`.
|
|
1911
1921
|
- **Immediately** when an approach fails (wrong API, wrong pattern, wrong assumption), call \`mem_tried\` \u2014 do not wait until the end of the session.
|
|
1912
|
-
- When starting a new task, call \`get_briefing\` with the task description to load relevant memories and avoid repeating past mistakes.
|
|
1913
1922
|
|
|
1914
1923
|
### Safety rules \u2014 NEVER violate these
|
|
1915
1924
|
- If \`get_briefing\` returns an \`action_required\` list, **stop and show each item to the developer** before doing anything. Use the exact \`developer_message\` provided. Wait for explicit confirmation.
|
|
1916
1925
|
- **Never modify code autonomously** because of a breaking change detected in another project (dependency version bump, API contract change, removed field). Always ask first.
|
|
1917
1926
|
- When in doubt about a cross-repo change: ask, don't act.
|
|
1918
1927
|
`;
|
|
1928
|
+
var CURSOR_HAIVE_RULE_MDC = `---
|
|
1929
|
+
description: Require hAIve MCP (get_briefing / mem_relevant_to) before substantive repo edits
|
|
1930
|
+
alwaysApply: true
|
|
1931
|
+
---
|
|
1932
|
+
|
|
1933
|
+
# hAIve \u2014 required for agents
|
|
1934
|
+
|
|
1935
|
+
This repository uses **hAIve**. Running \`haive init\` means the team expects agents to **use the haive MCP server by default**, not skip it.
|
|
1936
|
+
|
|
1937
|
+
## Before you change the repo
|
|
1938
|
+
|
|
1939
|
+
1. Call **\`get_briefing\`** with \`task\` (and \`files\` / \`symbols\` when known), **or** \`mem_relevant_to\` if full project context is already loaded and you only need ranked memories.
|
|
1940
|
+
2. Then read/search the codebase as needed. Do not invert this order for non-trivial work.
|
|
1941
|
+
|
|
1942
|
+
## During / after the task
|
|
1943
|
+
|
|
1944
|
+
- On failure: **\`mem_tried\`** immediately.
|
|
1945
|
+
- Before closing a substantive session: MCP prompt **\`post_task\`** when there is something worth capturing.
|
|
1946
|
+
|
|
1947
|
+
## If haive MCP is missing
|
|
1948
|
+
|
|
1949
|
+
Tell the developer to enable the **haive** server (e.g. \`haive mcp --stdio\` in client config) and restart the client. Do not silently ignore hAIve.
|
|
1950
|
+
`;
|
|
1919
1951
|
var CI_WORKFLOW = `name: haive-sync
|
|
1920
1952
|
|
|
1921
1953
|
on:
|
|
@@ -2036,7 +2068,7 @@ jobs:
|
|
|
2036
2068
|
function registerInit(program2) {
|
|
2037
2069
|
program2.command("init").description(
|
|
2038
2070
|
"Initialize a hAIve project \u2014 autopilot mode ON by default (zero human intervention).\n Auto-bootstraps project-context.md from local files and seeds detected stack packs.\n Add --manual to control memory approval and session recaps yourself.\n Add --no-bootstrap and --stack none to disable the auto-features."
|
|
2039
|
-
).option("-d, --dir <dir>", "project root", process.cwd()).option("--no-bridges", "do not generate CLAUDE.md / .cursorrules / copilot-instructions.md").option("--with-ci", "write a GitHub Actions workflow (.github/workflows/haive-sync.yml) \u2014 included automatically in autopilot mode").option(
|
|
2071
|
+
).option("-d, --dir <dir>", "project root", process.cwd()).option("--no-bridges", "do not generate CLAUDE.md / .cursorrules / copilot-instructions.md / .cursor/rules/haive-mcp-required.mdc").option("--with-ci", "write a GitHub Actions workflow (.github/workflows/haive-sync.yml) \u2014 included automatically in autopilot mode").option(
|
|
2040
2072
|
"--manual",
|
|
2041
2073
|
"opt out of autopilot: memories require manual approval, no auto-session recap, no auto-context"
|
|
2042
2074
|
).option(
|
|
@@ -2066,6 +2098,7 @@ function registerInit(program2) {
|
|
|
2066
2098
|
await mkdir3(paths.teamDir, { recursive: true });
|
|
2067
2099
|
await mkdir3(paths.moduleDir, { recursive: true });
|
|
2068
2100
|
await mkdir3(paths.modulesContextDir, { recursive: true });
|
|
2101
|
+
await ensureAiRuntimeLayout(paths.runtimeDir);
|
|
2069
2102
|
if (!existsSync6(paths.projectContext)) {
|
|
2070
2103
|
if (wantBootstrap) {
|
|
2071
2104
|
ui.info("Bootstrapping project context from local files\u2026");
|
|
@@ -2095,6 +2128,7 @@ function registerInit(program2) {
|
|
|
2095
2128
|
await writeBridge(root, "CLAUDE.md");
|
|
2096
2129
|
await writeBridge(root, ".cursorrules");
|
|
2097
2130
|
await writeBridge(root, path7.join(".github", "copilot-instructions.md"));
|
|
2131
|
+
await writeCursorHaiveRule(root);
|
|
2098
2132
|
}
|
|
2099
2133
|
const stacksToSeed = await resolveStacksToSeed(root, wantStack);
|
|
2100
2134
|
if (stacksToSeed.length > 0) {
|
|
@@ -2237,6 +2271,17 @@ async function resolveStacksToSeed(root, stackOpt) {
|
|
|
2237
2271
|
}
|
|
2238
2272
|
return stackOpt.split(",").map((s) => s.trim().toLowerCase()).filter(isValidStack);
|
|
2239
2273
|
}
|
|
2274
|
+
async function writeCursorHaiveRule(root) {
|
|
2275
|
+
const relPath = ".cursor/rules/haive-mcp-required.mdc";
|
|
2276
|
+
const target = path7.join(root, relPath);
|
|
2277
|
+
if (existsSync6(target)) {
|
|
2278
|
+
ui.info(`Cursor rule ${relPath} already exists \u2014 skipped`);
|
|
2279
|
+
return;
|
|
2280
|
+
}
|
|
2281
|
+
await mkdir3(path7.dirname(target), { recursive: true });
|
|
2282
|
+
await writeFile3(target, CURSOR_HAIVE_RULE_MDC, "utf8");
|
|
2283
|
+
ui.success(`Created Cursor rule ${relPath}`);
|
|
2284
|
+
}
|
|
2240
2285
|
async function writeBridge(root, relPath) {
|
|
2241
2286
|
const target = path7.join(root, relPath);
|
|
2242
2287
|
if (existsSync6(target)) {
|
|
@@ -2247,6 +2292,27 @@ async function writeBridge(root, relPath) {
|
|
|
2247
2292
|
await writeFile3(target, BRIDGE_BODY, "utf8");
|
|
2248
2293
|
ui.success(`Created bridge ${relPath}`);
|
|
2249
2294
|
}
|
|
2295
|
+
var RUNTIME_README_BODY = `# .ai/.runtime \u2014 disposable local layer
|
|
2296
|
+
|
|
2297
|
+
Not team truth. Use for machine-local session notes or tooling scratch files.
|
|
2298
|
+
Official memories belong in .ai/memories/ (versioned in Git).
|
|
2299
|
+
Only .gitignore and this README are meant to commit; everything else stays untracked.
|
|
2300
|
+
`;
|
|
2301
|
+
var RUNTIME_GITIGNORE_BODY = `*
|
|
2302
|
+
!.gitignore
|
|
2303
|
+
!README.md
|
|
2304
|
+
`;
|
|
2305
|
+
async function ensureAiRuntimeLayout(runtimeDir) {
|
|
2306
|
+
await mkdir3(runtimeDir, { recursive: true });
|
|
2307
|
+
const gi = path7.join(runtimeDir, ".gitignore");
|
|
2308
|
+
if (!existsSync6(gi)) {
|
|
2309
|
+
await writeFile3(gi, RUNTIME_GITIGNORE_BODY, "utf8");
|
|
2310
|
+
}
|
|
2311
|
+
const readme = path7.join(runtimeDir, "README.md");
|
|
2312
|
+
if (!existsSync6(readme)) {
|
|
2313
|
+
await writeFile3(readme, RUNTIME_README_BODY, "utf8");
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2250
2316
|
async function ensureGitignoreEntries(root, patterns) {
|
|
2251
2317
|
try {
|
|
2252
2318
|
const gitignorePath = path7.join(root, ".gitignore");
|
|
@@ -2631,6 +2697,7 @@ import {
|
|
|
2631
2697
|
loadMemoriesFromDir as loadMemoriesFromDir3,
|
|
2632
2698
|
loadUsageIndex as loadUsageIndex2,
|
|
2633
2699
|
pickSnippetNeedle,
|
|
2700
|
+
rankMemoriesLexical,
|
|
2634
2701
|
tokenizeQuery as tokenizeQuery2,
|
|
2635
2702
|
trackReads as trackReads2
|
|
2636
2703
|
} from "@hiveai/core";
|
|
@@ -2832,9 +2899,19 @@ import {
|
|
|
2832
2899
|
serializeMemory as serializeMemory10
|
|
2833
2900
|
} from "@hiveai/core";
|
|
2834
2901
|
import { z as z29 } from "zod";
|
|
2902
|
+
import { existsSync as existsSync27 } from "fs";
|
|
2903
|
+
import { findLexicalConflictPairs, loadMemoriesFromDir as loadMemoriesFromDir21 } from "@hiveai/core";
|
|
2835
2904
|
import { z as z30 } from "zod";
|
|
2905
|
+
import { resolveProjectInfo } from "@hiveai/core";
|
|
2836
2906
|
import { z as z31 } from "zod";
|
|
2907
|
+
import { MemoryTypeSchema, suggestTopicKey } from "@hiveai/core";
|
|
2837
2908
|
import { z as z32 } from "zod";
|
|
2909
|
+
import { existsSync as existsSync28 } from "fs";
|
|
2910
|
+
import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir222 } from "@hiveai/core";
|
|
2911
|
+
import { z as z33 } from "zod";
|
|
2912
|
+
import { z as z34 } from "zod";
|
|
2913
|
+
import { z as z35 } from "zod";
|
|
2914
|
+
import { z as z36 } from "zod";
|
|
2838
2915
|
function createContext(options = {}) {
|
|
2839
2916
|
const env = options.env ?? process.env;
|
|
2840
2917
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -3119,6 +3196,9 @@ var MemSearchInputSchema = {
|
|
|
3119
3196
|
semantic: z5.boolean().default(false).describe(
|
|
3120
3197
|
"Use semantic similarity from the embeddings index (requires `haive embeddings index`)."
|
|
3121
3198
|
),
|
|
3199
|
+
lexical_rank: z5.boolean().default(false).describe(
|
|
3200
|
+
"When true (and semantic is false), rank the filtered corpus with Okapi-BM25-style lexical scoring instead of literal AND/OR. Helps phrase-like queries without embeddings."
|
|
3201
|
+
),
|
|
3122
3202
|
min_score: z5.number().min(0).max(1).default(0).describe("Minimum cosine similarity (semantic mode only)"),
|
|
3123
3203
|
track: z5.boolean().default(true).describe("Increment read_count on returned memories (used for passive validation)")
|
|
3124
3204
|
};
|
|
@@ -3144,6 +3224,27 @@ async function memSearch(input, ctx) {
|
|
|
3144
3224
|
notice: "Semantic search unavailable (embeddings index missing or @hiveai/embeddings not installed). Falling back to literal search."
|
|
3145
3225
|
};
|
|
3146
3226
|
}
|
|
3227
|
+
} else if (input.lexical_rank && input.query.trim()) {
|
|
3228
|
+
const { ranked, scores } = rankMemoriesLexical(
|
|
3229
|
+
filtered,
|
|
3230
|
+
input.query,
|
|
3231
|
+
input.limit
|
|
3232
|
+
);
|
|
3233
|
+
if (ranked.length > 0) {
|
|
3234
|
+
const snippetNeedle = pickSnippetNeedle(input.query);
|
|
3235
|
+
result = {
|
|
3236
|
+
matches: ranked.map(
|
|
3237
|
+
(loaded, i) => lexicalHit(loaded, snippetNeedle, usage, scores[i])
|
|
3238
|
+
),
|
|
3239
|
+
total: ranked.length,
|
|
3240
|
+
mode: "lexical_ranked"
|
|
3241
|
+
};
|
|
3242
|
+
} else {
|
|
3243
|
+
result = {
|
|
3244
|
+
...buildLiteralResult(input, filtered, usage),
|
|
3245
|
+
notice: "Lexical ranking found no BM25-positive hits \u2014 showing literal matches instead."
|
|
3246
|
+
};
|
|
3247
|
+
}
|
|
3147
3248
|
} else {
|
|
3148
3249
|
result = buildLiteralResult(input, filtered, usage);
|
|
3149
3250
|
}
|
|
@@ -3241,6 +3342,9 @@ function toHit(loaded, needle, usage) {
|
|
|
3241
3342
|
file_path: loaded.filePath
|
|
3242
3343
|
};
|
|
3243
3344
|
}
|
|
3345
|
+
function lexicalHit(loaded, needle, usage, lexicalScore) {
|
|
3346
|
+
return { ...toHit(loaded, needle, usage), score: lexicalScore };
|
|
3347
|
+
}
|
|
3244
3348
|
var MemVerifyInputSchema = {
|
|
3245
3349
|
id: z6.string().optional().describe("If set, verify only this memory id"),
|
|
3246
3350
|
update: z6.boolean().default(false).describe("Write the resulting status back to disk (status=stale or validated)")
|
|
@@ -5572,11 +5676,76 @@ function gitFileDiff(root, file, sinceDays) {
|
|
|
5572
5676
|
return null;
|
|
5573
5677
|
}
|
|
5574
5678
|
}
|
|
5679
|
+
var MemConflictCandidatesInputSchema = {
|
|
5680
|
+
since_days: z30.number().int().positive().max(3650).default(365).describe("Only memories created since N days ago"),
|
|
5681
|
+
types: z30.array(z30.enum(["decision", "architecture", "convention", "gotcha"])).default(["decision", "architecture"]).describe("Memory types scanned for pairwise lexical overlap"),
|
|
5682
|
+
min_jaccard: z30.number().min(0).max(1).default(0.45).describe("Minimum Jaccard token similarity to surface as a candidate pair"),
|
|
5683
|
+
max_pairs: z30.number().int().positive().max(100).default(20).describe("Cap pairs returned"),
|
|
5684
|
+
max_scan: z30.number().int().positive().max(2e3).default(500).describe("Maximum memories sampled for O(n\xB2) scan \u2014 excess dropped after chronological sort.")
|
|
5685
|
+
};
|
|
5686
|
+
async function memConflictCandidates(input, ctx) {
|
|
5687
|
+
if (!existsSync27(ctx.paths.memoriesDir)) {
|
|
5688
|
+
return {
|
|
5689
|
+
pairs: [],
|
|
5690
|
+
scanned: 0,
|
|
5691
|
+
truncated: false,
|
|
5692
|
+
notice: "No .ai/memories directory."
|
|
5693
|
+
};
|
|
5694
|
+
}
|
|
5695
|
+
const all = await loadMemoriesFromDir21(ctx.paths.memoriesDir);
|
|
5696
|
+
const { pairs, scanned, truncated } = findLexicalConflictPairs(all, {
|
|
5697
|
+
sinceDays: input.since_days,
|
|
5698
|
+
types: input.types,
|
|
5699
|
+
minJaccard: input.min_jaccard,
|
|
5700
|
+
maxPairs: input.max_pairs,
|
|
5701
|
+
maxScan: input.max_scan
|
|
5702
|
+
});
|
|
5703
|
+
const notice = pairs.length === 0 ? "No lexical candidate pairs \u2265 threshold \u2014 try lowering min_jaccard or widen since_days/types." : void 0;
|
|
5704
|
+
return { pairs, scanned, truncated, notice };
|
|
5705
|
+
}
|
|
5706
|
+
var MemResolveProjectInputSchema = {
|
|
5707
|
+
cwd: z31.string().optional().describe("Directory used for root discovery when HAIVE_PROJECT_ROOT is unset.")
|
|
5708
|
+
};
|
|
5709
|
+
async function memResolveProject(input, _ctx) {
|
|
5710
|
+
void _ctx;
|
|
5711
|
+
return {
|
|
5712
|
+
ok: true,
|
|
5713
|
+
info: resolveProjectInfo({
|
|
5714
|
+
cwd: input.cwd
|
|
5715
|
+
})
|
|
5716
|
+
};
|
|
5717
|
+
}
|
|
5718
|
+
var MemSuggestTopicInputSchema = {
|
|
5719
|
+
type: MemoryTypeSchema.describe("Memory kind \u2014 drives the suggested topic family."),
|
|
5720
|
+
title: z32.string().min(1).describe("Short title or phrase (headers, headings) \u2014 turned into slug")
|
|
5721
|
+
};
|
|
5722
|
+
async function memSuggestTopic(input, _ctx) {
|
|
5723
|
+
void _ctx;
|
|
5724
|
+
const suggestion = suggestTopicKey(input.type, input.title);
|
|
5725
|
+
return { topic_key: suggestion.topic_key, family: suggestion.family, type: input.type };
|
|
5726
|
+
}
|
|
5727
|
+
var MemTimelineInputSchema = {
|
|
5728
|
+
memory_id: z33.string().optional().describe("Seed id \u2014 expands via related_ids, topic, anchors"),
|
|
5729
|
+
topic: z33.string().optional().describe("Frontmatter.topic value \u2014 chronological list when memory_id omitted"),
|
|
5730
|
+
limit: z33.number().int().positive().max(100).default(30).describe("Max timeline entries returned")
|
|
5731
|
+
};
|
|
5732
|
+
async function memTimeline(input, ctx) {
|
|
5733
|
+
if (!existsSync28(ctx.paths.memoriesDir)) {
|
|
5734
|
+
return { entries: [], total: 0, notice: "No .ai/memories directory." };
|
|
5735
|
+
}
|
|
5736
|
+
const all = await loadMemoriesFromDir222(ctx.paths.memoriesDir);
|
|
5737
|
+
const { entries, notice } = collectTimelineEntries(all, {
|
|
5738
|
+
memoryId: input.memory_id,
|
|
5739
|
+
topic: input.topic,
|
|
5740
|
+
limit: input.limit
|
|
5741
|
+
});
|
|
5742
|
+
return { entries, total: entries.length, notice };
|
|
5743
|
+
}
|
|
5575
5744
|
var BootstrapProjectArgsSchema = {
|
|
5576
|
-
module:
|
|
5745
|
+
module: z34.string().optional().describe(
|
|
5577
5746
|
"Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
|
|
5578
5747
|
),
|
|
5579
|
-
focus:
|
|
5748
|
+
focus: z34.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
|
|
5580
5749
|
};
|
|
5581
5750
|
var ROOT_TEMPLATE = `# Project context
|
|
5582
5751
|
|
|
@@ -5657,8 +5826,8 @@ ${template}\`\`\`
|
|
|
5657
5826
|
};
|
|
5658
5827
|
}
|
|
5659
5828
|
var PostTaskArgsSchema = {
|
|
5660
|
-
task_summary:
|
|
5661
|
-
files_touched:
|
|
5829
|
+
task_summary: z35.string().optional().describe("One sentence describing what you just did"),
|
|
5830
|
+
files_touched: z35.array(z35.string()).optional().describe("Files you created or modified during the task")
|
|
5662
5831
|
};
|
|
5663
5832
|
function postTaskPrompt(args, ctx) {
|
|
5664
5833
|
const taskLine = args.task_summary ? `
|
|
@@ -5741,10 +5910,10 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]. Sessi
|
|
|
5741
5910
|
};
|
|
5742
5911
|
}
|
|
5743
5912
|
var ImportDocsArgsSchema = {
|
|
5744
|
-
content:
|
|
5745
|
-
source:
|
|
5746
|
-
scope:
|
|
5747
|
-
dry_run:
|
|
5913
|
+
content: z36.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
|
|
5914
|
+
source: z36.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
|
|
5915
|
+
scope: z36.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
|
|
5916
|
+
dry_run: z36.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
|
|
5748
5917
|
};
|
|
5749
5918
|
function importDocsPrompt(args, ctx) {
|
|
5750
5919
|
const sourceLine = args.source ? `
|
|
@@ -5807,7 +5976,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
5807
5976
|
};
|
|
5808
5977
|
}
|
|
5809
5978
|
var SERVER_NAME = "haive";
|
|
5810
|
-
var SERVER_VERSION = "0.9.
|
|
5979
|
+
var SERVER_VERSION = "0.9.5";
|
|
5811
5980
|
function jsonResult(data) {
|
|
5812
5981
|
return {
|
|
5813
5982
|
content: [
|
|
@@ -5858,6 +6027,23 @@ function createHaiveServer(options = {}) {
|
|
|
5858
6027
|
return jsonResult(await memSave(input, context));
|
|
5859
6028
|
}
|
|
5860
6029
|
);
|
|
6030
|
+
server.tool(
|
|
6031
|
+
"mem_suggest_topic",
|
|
6032
|
+
[
|
|
6033
|
+
"Propose a stable `topic` key (topic-upsert) from type + short title.",
|
|
6034
|
+
"",
|
|
6035
|
+
"USE BEFORE mem_save when you want deterministic updates to the same memory over time;",
|
|
6036
|
+
"families mimic Engram-style grouping (architecture/*, bug/*, decision/*, \u2026).",
|
|
6037
|
+
"",
|
|
6038
|
+
"PARAMETERS:",
|
|
6039
|
+
" type \u2014 convention | decision | gotcha | architecture | glossary | attempt | session_recap",
|
|
6040
|
+
" title \u2014 phrase to slugify under the suggested family prefix",
|
|
6041
|
+
"",
|
|
6042
|
+
"RETURNS: { topic_key, family, type }"
|
|
6043
|
+
].join("\n"),
|
|
6044
|
+
MemSuggestTopicInputSchema,
|
|
6045
|
+
async (input) => jsonResult(await memSuggestTopic(input, context))
|
|
6046
|
+
);
|
|
5861
6047
|
server.tool(
|
|
5862
6048
|
"mem_tried",
|
|
5863
6049
|
[
|
|
@@ -5949,8 +6135,12 @@ function createHaiveServer(options = {}) {
|
|
|
5949
6135
|
server.tool(
|
|
5950
6136
|
"get_briefing",
|
|
5951
6137
|
[
|
|
5952
|
-
"\u2B50
|
|
5953
|
-
"
|
|
6138
|
+
"\u2B50 DEFAULT-FIRST for coding agents on any repo where `haive init` ran: call this BEFORE",
|
|
6139
|
+
"changing source or project config for the current goal (unless the developer explicitly opts out).",
|
|
6140
|
+
"One-shot onboarding: everything relevant in a single call under a token budget.",
|
|
6141
|
+
"",
|
|
6142
|
+
"PROGRESSIVE DISCLOSURE \u2014 after this, drill down only if needed:",
|
|
6143
|
+
" mem_relevant_to / mem_search (compact lists) \u2192 mem_get (full body + anchors).",
|
|
5954
6144
|
"",
|
|
5955
6145
|
"RETURNS (in order of priority):",
|
|
5956
6146
|
" 0. action_required \u2014 \u26A0\uFE0F HANDLE THIS FIRST if non-empty (see protocol below)",
|
|
@@ -5985,7 +6175,7 @@ function createHaiveServer(options = {}) {
|
|
|
5985
6175
|
" low \u2014 proposed, few reads (take with caution)",
|
|
5986
6176
|
" unverified \u2014 draft (unverified: true flag set)",
|
|
5987
6177
|
"",
|
|
5988
|
-
"Replaces 4\u20135 separate tool calls.
|
|
6178
|
+
"Replaces 4\u20135 separate tool calls. Prefer this first; use mem_search / mem_get only for follow-up."
|
|
5989
6179
|
].join("\n"),
|
|
5990
6180
|
GetBriefingInputSchema,
|
|
5991
6181
|
async (input) => {
|
|
@@ -6004,6 +6194,8 @@ function createHaiveServer(options = {}) {
|
|
|
6004
6194
|
"SEARCH MODES:",
|
|
6005
6195
|
" Literal (default): AND search across id, tags, and body \u2014 all tokens must match.",
|
|
6006
6196
|
" Falls back to OR automatically if no AND results (partial match).",
|
|
6197
|
+
" Lexical rank (lexical_rank: true, semantic: false): Okapi-BM25-style scoring on the",
|
|
6198
|
+
" filtered corpus \u2014 good for phrase-like queries without embeddings.",
|
|
6007
6199
|
" Semantic (semantic: true): embedding-based similarity \u2014 finds related memories",
|
|
6008
6200
|
" even with different wording. Requires haive embeddings index to be built.",
|
|
6009
6201
|
"",
|
|
@@ -6012,6 +6204,7 @@ function createHaiveServer(options = {}) {
|
|
|
6012
6204
|
" scope \u2014 filter by personal | team | module",
|
|
6013
6205
|
" type \u2014 filter by convention | decision | gotcha | architecture | glossary",
|
|
6014
6206
|
" semantic \u2014 true for embedding-based search (requires @hiveai/embeddings)",
|
|
6207
|
+
" lexical_rank \u2014 BM25-style ranking (ignored when semantic is true)",
|
|
6015
6208
|
" limit \u2014 max results (default 10)",
|
|
6016
6209
|
"",
|
|
6017
6210
|
"RETURNS: array of { id, type, scope, status, confidence, body, match_quality }"
|
|
@@ -6022,6 +6215,22 @@ function createHaiveServer(options = {}) {
|
|
|
6022
6215
|
return jsonResult(await memSearch(input, context));
|
|
6023
6216
|
}
|
|
6024
6217
|
);
|
|
6218
|
+
server.tool(
|
|
6219
|
+
"mem_timeline",
|
|
6220
|
+
[
|
|
6221
|
+
"Chronological view of related memories: by shared frontmatter.topic OR expanded from a seed id",
|
|
6222
|
+
"(related_ids, same topic, overlapping anchor paths \u2014 one extra hop on related_ids).",
|
|
6223
|
+
"",
|
|
6224
|
+
"PARAMETERS:",
|
|
6225
|
+
" memory_id \u2014 optional seed memory id",
|
|
6226
|
+
" topic \u2014 optional topic key (required if memory_id omitted)",
|
|
6227
|
+
" limit \u2014 max entries (default 30)",
|
|
6228
|
+
"",
|
|
6229
|
+
"RETURNS: { entries: [{ id, type, scope, created_at, one_line, topic? }], total, notice? }"
|
|
6230
|
+
].join("\n"),
|
|
6231
|
+
MemTimelineInputSchema,
|
|
6232
|
+
async (input) => jsonResult(await memTimeline(input, context))
|
|
6233
|
+
);
|
|
6025
6234
|
server.tool(
|
|
6026
6235
|
"mem_for_files",
|
|
6027
6236
|
[
|
|
@@ -6051,7 +6260,7 @@ function createHaiveServer(options = {}) {
|
|
|
6051
6260
|
[
|
|
6052
6261
|
"Fetch a single memory by its full id with all details.",
|
|
6053
6262
|
"",
|
|
6054
|
-
"USE WHEN get_briefing returned a
|
|
6263
|
+
"USE WHEN get_briefing / mem_relevant_to / mem_search returned a compact hit and you need",
|
|
6055
6264
|
"the full body, or when you know the exact id of a memory.",
|
|
6056
6265
|
"",
|
|
6057
6266
|
"PARAMETERS:",
|
|
@@ -6139,6 +6348,22 @@ function createHaiveServer(options = {}) {
|
|
|
6139
6348
|
CodeMapInputSchema,
|
|
6140
6349
|
async (input) => jsonResult(await codeMapTool(input, context))
|
|
6141
6350
|
);
|
|
6351
|
+
server.tool(
|
|
6352
|
+
"mem_resolve_project",
|
|
6353
|
+
[
|
|
6354
|
+
"Diagnostics: resolve which project root hAIve is using (never throws).",
|
|
6355
|
+
"",
|
|
6356
|
+
"USE IN multi-root workspaces or when the agent CWD may not be the repo root \u2014",
|
|
6357
|
+
"mirrors HAIVE_PROJECT_ROOT, findProjectRoot markers, and presence of .ai/memories.",
|
|
6358
|
+
"",
|
|
6359
|
+
"PARAMETERS:",
|
|
6360
|
+
" cwd \u2014 optional directory used for discovery when HAIVE_PROJECT_ROOT is unset",
|
|
6361
|
+
"",
|
|
6362
|
+
"RETURNS: { ok: true, info: { cwd, resolved_root, haive_project_root_env, \u2026 } }"
|
|
6363
|
+
].join("\n"),
|
|
6364
|
+
MemResolveProjectInputSchema,
|
|
6365
|
+
async (input) => jsonResult(await memResolveProject(input, context))
|
|
6366
|
+
);
|
|
6142
6367
|
server.tool(
|
|
6143
6368
|
"mem_update",
|
|
6144
6369
|
[
|
|
@@ -6272,6 +6497,8 @@ function createHaiveServer(options = {}) {
|
|
|
6272
6497
|
"One-shot ranked memories for a task \u2014 use instead of get_briefing when",
|
|
6273
6498
|
"project context is already loaded and you only want the relevant memory layer.",
|
|
6274
6499
|
"",
|
|
6500
|
+
"Second step in progressive disclosure (after get_briefing): narrow here, then mem_get for full text.",
|
|
6501
|
+
"",
|
|
6275
6502
|
"Reuses the same ranking pipeline (anchor / module / literal / semantic) but",
|
|
6276
6503
|
"skips project_context, modules, action_required, etc.",
|
|
6277
6504
|
"",
|
|
@@ -6429,6 +6656,24 @@ function createHaiveServer(options = {}) {
|
|
|
6429
6656
|
return jsonResult(await memConflicts(input, context));
|
|
6430
6657
|
}
|
|
6431
6658
|
);
|
|
6659
|
+
server.tool(
|
|
6660
|
+
"mem_conflict_candidates",
|
|
6661
|
+
[
|
|
6662
|
+
"Bulk lexical scan for decision/architecture-like pairs that look similar (Jaccard on tokens).",
|
|
6663
|
+
"",
|
|
6664
|
+
"Advisory only \u2014 follow with mem_conflicts_with on specific ids for real contradiction checks.",
|
|
6665
|
+
"",
|
|
6666
|
+
"PARAMETERS:",
|
|
6667
|
+
" since_days, types, min_jaccard, max_pairs, max_scan",
|
|
6668
|
+
"",
|
|
6669
|
+
"RETURNS: { pairs: [{ id_a, id_b, jaccard }], scanned, truncated, notice? }"
|
|
6670
|
+
].join("\n"),
|
|
6671
|
+
MemConflictCandidatesInputSchema,
|
|
6672
|
+
async (input) => {
|
|
6673
|
+
tracker.record("mem_conflict_candidates", `${input.since_days}d`);
|
|
6674
|
+
return jsonResult(await memConflictCandidates(input, context));
|
|
6675
|
+
}
|
|
6676
|
+
);
|
|
6432
6677
|
server.tool(
|
|
6433
6678
|
"pre_commit_check",
|
|
6434
6679
|
[
|
|
@@ -6566,7 +6811,7 @@ function registerMcp(program2) {
|
|
|
6566
6811
|
// src/commands/sync.ts
|
|
6567
6812
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
6568
6813
|
import { readFile as readFile8, writeFile as writeFile13, mkdir as mkdir8 } from "fs/promises";
|
|
6569
|
-
import { existsSync as
|
|
6814
|
+
import { existsSync as existsSync29 } from "fs";
|
|
6570
6815
|
import path12 from "path";
|
|
6571
6816
|
import "commander";
|
|
6572
6817
|
import {
|
|
@@ -6578,7 +6823,7 @@ import {
|
|
|
6578
6823
|
isDecaying as isDecaying2,
|
|
6579
6824
|
loadCodeMap as loadCodeMap4,
|
|
6580
6825
|
loadConfig as loadConfig4,
|
|
6581
|
-
loadMemoriesFromDir as
|
|
6826
|
+
loadMemoriesFromDir as loadMemoriesFromDir23,
|
|
6582
6827
|
loadUsageIndex as loadUsageIndex12,
|
|
6583
6828
|
pullCrossRepoSources,
|
|
6584
6829
|
resolveHaivePaths as resolveHaivePaths7,
|
|
@@ -6602,7 +6847,7 @@ function registerSync(program2) {
|
|
|
6602
6847
|
).option("--bridge-file <path>", "bridge file to inject into (default: CLAUDE.md)").option("--bridge-max-memories <n>", "max memories to inject into bridge file", "5").option("--embed", "rebuild embeddings index after sync (requires @haive/embeddings)").option("--no-cross-repo", "skip cross-repo memory pull even if crossRepoSources is configured").option("--no-deps", "skip dependency version tracking").option("--no-contracts", "skip contract file diff checking").action(async (opts) => {
|
|
6603
6848
|
const root = findProjectRoot10(opts.dir);
|
|
6604
6849
|
const paths = resolveHaivePaths7(root);
|
|
6605
|
-
if (!
|
|
6850
|
+
if (!existsSync29(paths.memoriesDir)) {
|
|
6606
6851
|
if (!opts.quiet) ui.warn(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
6607
6852
|
process.exitCode = 1;
|
|
6608
6853
|
return;
|
|
@@ -6618,7 +6863,7 @@ function registerSync(program2) {
|
|
|
6618
6863
|
let promoted = 0;
|
|
6619
6864
|
let autoApproved = 0;
|
|
6620
6865
|
if (opts.verify !== false) {
|
|
6621
|
-
const memories = await
|
|
6866
|
+
const memories = await loadMemoriesFromDir23(paths.memoriesDir);
|
|
6622
6867
|
for (const { memory: memory2, filePath } of memories) {
|
|
6623
6868
|
if (memory2.frontmatter.type === "session_recap") {
|
|
6624
6869
|
if (memory2.frontmatter.status === "stale") {
|
|
@@ -6679,7 +6924,7 @@ function registerSync(program2) {
|
|
|
6679
6924
|
}
|
|
6680
6925
|
}
|
|
6681
6926
|
if (opts.promote !== false) {
|
|
6682
|
-
const memories = await
|
|
6927
|
+
const memories = await loadMemoriesFromDir23(paths.memoriesDir);
|
|
6683
6928
|
const usage = await loadUsageIndex12(paths);
|
|
6684
6929
|
const nowMs = Date.now();
|
|
6685
6930
|
for (const { memory: memory2, filePath } of memories) {
|
|
@@ -6718,7 +6963,7 @@ function registerSync(program2) {
|
|
|
6718
6963
|
}
|
|
6719
6964
|
}
|
|
6720
6965
|
const sinceReport = opts.since ? collectSinceChanges(root, opts.since) : null;
|
|
6721
|
-
const draftMemories = (await
|
|
6966
|
+
const draftMemories = (await loadMemoriesFromDir23(paths.memoriesDir)).filter(
|
|
6722
6967
|
(m) => m.memory.frontmatter.status === "draft"
|
|
6723
6968
|
);
|
|
6724
6969
|
const draftCount = draftMemories.length;
|
|
@@ -6753,7 +6998,7 @@ function registerSync(program2) {
|
|
|
6753
6998
|
}
|
|
6754
6999
|
}
|
|
6755
7000
|
if (!opts.quiet) {
|
|
6756
|
-
const allForDecay = await
|
|
7001
|
+
const allForDecay = await loadMemoriesFromDir23(paths.memoriesDir);
|
|
6757
7002
|
const usageForDecay = await loadUsageIndex12(paths);
|
|
6758
7003
|
const decaying = allForDecay.filter(({ memory: memory2 }) => {
|
|
6759
7004
|
const fm = memory2.frontmatter;
|
|
@@ -6971,8 +7216,8 @@ Attends une **confirmation explicite** avant d'agir.
|
|
|
6971
7216
|
});
|
|
6972
7217
|
}
|
|
6973
7218
|
async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
|
|
6974
|
-
if (!
|
|
6975
|
-
const all = await
|
|
7219
|
+
if (!existsSync29(memoriesDir)) return;
|
|
7220
|
+
const all = await loadMemoriesFromDir23(memoriesDir);
|
|
6976
7221
|
const top = all.filter(({ memory: memory2 }) => {
|
|
6977
7222
|
const s = memory2.frontmatter.status;
|
|
6978
7223
|
if (memory2.frontmatter.type === "session_recap") return false;
|
|
@@ -6996,7 +7241,7 @@ ${m.memory.body.trim()}`;
|
|
|
6996
7241
|
` + block + `
|
|
6997
7242
|
|
|
6998
7243
|
${BRIDGE_END}`;
|
|
6999
|
-
const fileExists =
|
|
7244
|
+
const fileExists = existsSync29(bridgeFile);
|
|
7000
7245
|
let existing = fileExists ? await readFile8(bridgeFile, "utf8") : "";
|
|
7001
7246
|
existing = existing.replace(/\r\n/g, "\n");
|
|
7002
7247
|
const startIdx = existing.indexOf(BRIDGE_START);
|
|
@@ -7047,14 +7292,14 @@ function collectSinceChanges(root, ref) {
|
|
|
7047
7292
|
// src/commands/memory-add.ts
|
|
7048
7293
|
import { createHash as createHash2 } from "crypto";
|
|
7049
7294
|
import { mkdir as mkdir9, readFile as readFile9, writeFile as writeFile14 } from "fs/promises";
|
|
7050
|
-
import { existsSync as
|
|
7295
|
+
import { existsSync as existsSync30 } from "fs";
|
|
7051
7296
|
import path13 from "path";
|
|
7052
7297
|
import "commander";
|
|
7053
7298
|
import {
|
|
7054
7299
|
buildFrontmatter as buildFrontmatter7,
|
|
7055
7300
|
findProjectRoot as findProjectRoot11,
|
|
7056
7301
|
inferModulesFromPaths as inferModulesFromPaths3,
|
|
7057
|
-
loadMemoriesFromDir as
|
|
7302
|
+
loadMemoriesFromDir as loadMemoriesFromDir24,
|
|
7058
7303
|
memoryFilePath as memoryFilePath6,
|
|
7059
7304
|
resolveHaivePaths as resolveHaivePaths8,
|
|
7060
7305
|
serializeMemory as serializeMemory12
|
|
@@ -7086,7 +7331,7 @@ function registerMemoryAdd(memory2) {
|
|
|
7086
7331
|
).requiredOption("--type <type>", "convention | decision | gotcha | architecture | glossary | attempt").requiredOption("--slug <slug>", "short kebab-case identifier used in the file name").option("--title <text>", "memory title \u2014 becomes the first heading of the body").option("--scope <scope>", "personal | team | module (default: personal, or team in autopilot)", "personal").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags for easier retrieval").option("--domain <domain>", "domain (e.g. transactions)").option("--author <author>", "author email or handle").option("--paths <csv>", "anchor to source files \u2014 used for staleness detection by haive sync").option("--symbols <csv>", "anchor to specific symbols (class/function names)").option("--commit <sha>", "anchor to a specific commit SHA").option("--body <text>", "memory body content (Markdown) \u2014 overrides --title default body").option("--body-file <path>", "read memory body from a Markdown file \u2014 for long content").option("--no-auto-tag", "disable automatic tag suggestions inferred from anchor paths").option("--topic <key>", "stable key for upsert: if a memory with this topic+scope already exists, update it in-place (revision_count++)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
7087
7332
|
const root = findProjectRoot11(opts.dir);
|
|
7088
7333
|
const paths = resolveHaivePaths8(root);
|
|
7089
|
-
if (!
|
|
7334
|
+
if (!existsSync30(paths.haiveDir)) {
|
|
7090
7335
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
7091
7336
|
process.exitCode = 1;
|
|
7092
7337
|
return;
|
|
@@ -7097,7 +7342,7 @@ function registerMemoryAdd(memory2) {
|
|
|
7097
7342
|
const inferredTags = autoTagsEnabled ? inferModulesFromPaths3(anchorPaths) : [];
|
|
7098
7343
|
const mergedTags = Array.from(/* @__PURE__ */ new Set([...userTags, ...inferredTags]));
|
|
7099
7344
|
if (anchorPaths.length > 0) {
|
|
7100
|
-
const missing = anchorPaths.filter((p) => !
|
|
7345
|
+
const missing = anchorPaths.filter((p) => !existsSync30(path13.resolve(root, p)));
|
|
7101
7346
|
if (missing.length > 0) {
|
|
7102
7347
|
ui.warn(`Anchor path${missing.length > 1 ? "s" : ""} not found in project:`);
|
|
7103
7348
|
for (const p of missing) ui.warn(` \u2717 ${p}`);
|
|
@@ -7109,7 +7354,7 @@ function registerMemoryAdd(memory2) {
|
|
|
7109
7354
|
const title = opts.title ?? opts.slug;
|
|
7110
7355
|
let body;
|
|
7111
7356
|
if (opts.bodyFile !== void 0) {
|
|
7112
|
-
if (!
|
|
7357
|
+
if (!existsSync30(opts.bodyFile)) {
|
|
7113
7358
|
ui.error(`--body-file not found: ${opts.bodyFile}`);
|
|
7114
7359
|
process.exitCode = 1;
|
|
7115
7360
|
return;
|
|
@@ -7130,9 +7375,9 @@ TODO \u2014 write the memory body.
|
|
|
7130
7375
|
`;
|
|
7131
7376
|
}
|
|
7132
7377
|
const scope = opts.scope ?? "personal";
|
|
7133
|
-
if (
|
|
7378
|
+
if (existsSync30(paths.memoriesDir)) {
|
|
7134
7379
|
const incomingHash = createHash2("sha256").update(body.trim()).digest("hex").slice(0, 12);
|
|
7135
|
-
const allForHash = await
|
|
7380
|
+
const allForHash = await loadMemoriesFromDir24(paths.memoriesDir);
|
|
7136
7381
|
const hashDup = allForHash.find(
|
|
7137
7382
|
({ memory: memory3 }) => createHash2("sha256").update(memory3.body.trim()).digest("hex").slice(0, 12) === incomingHash && memory3.frontmatter.scope === scope
|
|
7138
7383
|
);
|
|
@@ -7143,8 +7388,8 @@ TODO \u2014 write the memory body.
|
|
|
7143
7388
|
return;
|
|
7144
7389
|
}
|
|
7145
7390
|
}
|
|
7146
|
-
if (opts.topic &&
|
|
7147
|
-
const existing = await
|
|
7391
|
+
if (opts.topic && existsSync30(paths.memoriesDir)) {
|
|
7392
|
+
const existing = await loadMemoriesFromDir24(paths.memoriesDir);
|
|
7148
7393
|
const topicMatch = existing.find(
|
|
7149
7394
|
({ memory: memory3 }) => memory3.frontmatter.topic === opts.topic && memory3.frontmatter.scope === scope && (!opts.module || memory3.frontmatter.module === opts.module)
|
|
7150
7395
|
);
|
|
@@ -7182,13 +7427,13 @@ TODO \u2014 write the memory body.
|
|
|
7182
7427
|
});
|
|
7183
7428
|
const file = memoryFilePath6(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
7184
7429
|
await mkdir9(path13.dirname(file), { recursive: true });
|
|
7185
|
-
if (
|
|
7430
|
+
if (existsSync30(file)) {
|
|
7186
7431
|
ui.error(`Memory already exists at ${file}`);
|
|
7187
7432
|
process.exitCode = 1;
|
|
7188
7433
|
return;
|
|
7189
7434
|
}
|
|
7190
|
-
if (
|
|
7191
|
-
const existing = await
|
|
7435
|
+
if (existsSync30(paths.memoriesDir)) {
|
|
7436
|
+
const existing = await loadMemoriesFromDir24(paths.memoriesDir);
|
|
7192
7437
|
const slugTokens = opts.slug.toLowerCase().split(/[-_\s]+/).filter(Boolean);
|
|
7193
7438
|
const similar = existing.filter(({ memory: memory3 }) => {
|
|
7194
7439
|
const id = memory3.frontmatter.id.toLowerCase();
|
|
@@ -7230,14 +7475,14 @@ function parseCsv2(value) {
|
|
|
7230
7475
|
}
|
|
7231
7476
|
|
|
7232
7477
|
// src/commands/memory-list.ts
|
|
7233
|
-
import { existsSync as
|
|
7478
|
+
import { existsSync as existsSync31 } from "fs";
|
|
7234
7479
|
import path14 from "path";
|
|
7235
7480
|
import "commander";
|
|
7236
7481
|
import { findProjectRoot as findProjectRoot12, resolveHaivePaths as resolveHaivePaths9 } from "@hiveai/core";
|
|
7237
7482
|
|
|
7238
7483
|
// src/utils/fs.ts
|
|
7239
7484
|
import {
|
|
7240
|
-
loadMemoriesFromDir as
|
|
7485
|
+
loadMemoriesFromDir as loadMemoriesFromDir25,
|
|
7241
7486
|
loadMemory,
|
|
7242
7487
|
listMarkdownFilesRecursive
|
|
7243
7488
|
} from "@hiveai/core";
|
|
@@ -7247,12 +7492,12 @@ function registerMemoryList(memory2) {
|
|
|
7247
7492
|
memory2.command("list").description("List memories with optional filters").option("--scope <scope>", "personal | team | module").option("--type <type>", "filter by type").option("--tag <tag>", "filter by tag").option("--module <name>", "filter by module name").option("--status <csv>", "filter by status (draft,proposed,validated,stale,rejected,deprecated)").option("--show-rejected", "include rejected memories (hidden by default)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
7248
7493
|
const root = findProjectRoot12(opts.dir);
|
|
7249
7494
|
const paths = resolveHaivePaths9(root);
|
|
7250
|
-
if (!
|
|
7495
|
+
if (!existsSync31(paths.memoriesDir)) {
|
|
7251
7496
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
7252
7497
|
process.exitCode = 1;
|
|
7253
7498
|
return;
|
|
7254
7499
|
}
|
|
7255
|
-
const all = await
|
|
7500
|
+
const all = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
7256
7501
|
const statusFilter = opts.status ? opts.status.split(",").map((s) => s.trim()) : null;
|
|
7257
7502
|
const filtered = all.filter((m) => {
|
|
7258
7503
|
if (!matchesFilters(m, opts)) return false;
|
|
@@ -7315,7 +7560,7 @@ function matchesFilters(loaded, opts) {
|
|
|
7315
7560
|
|
|
7316
7561
|
// src/commands/memory-promote.ts
|
|
7317
7562
|
import { mkdir as mkdir10, unlink as unlink2, writeFile as writeFile15 } from "fs/promises";
|
|
7318
|
-
import { existsSync as
|
|
7563
|
+
import { existsSync as existsSync33 } from "fs";
|
|
7319
7564
|
import path15 from "path";
|
|
7320
7565
|
import "commander";
|
|
7321
7566
|
import {
|
|
@@ -7328,12 +7573,12 @@ function registerMemoryPromote(memory2) {
|
|
|
7328
7573
|
memory2.command("promote <id>").description("Promote a personal memory to team scope (status -> proposed)").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
7329
7574
|
const root = findProjectRoot13(opts.dir);
|
|
7330
7575
|
const paths = resolveHaivePaths10(root);
|
|
7331
|
-
if (!
|
|
7576
|
+
if (!existsSync33(paths.memoriesDir)) {
|
|
7332
7577
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
7333
7578
|
process.exitCode = 1;
|
|
7334
7579
|
return;
|
|
7335
7580
|
}
|
|
7336
|
-
const teamAndModule = await
|
|
7581
|
+
const teamAndModule = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
7337
7582
|
const alreadyShared = teamAndModule.find(
|
|
7338
7583
|
(m) => m.memory.frontmatter.id === id && (m.memory.frontmatter.scope === "team" || m.memory.frontmatter.scope === "module")
|
|
7339
7584
|
);
|
|
@@ -7347,7 +7592,7 @@ function registerMemoryPromote(memory2) {
|
|
|
7347
7592
|
}
|
|
7348
7593
|
return;
|
|
7349
7594
|
}
|
|
7350
|
-
const all = await
|
|
7595
|
+
const all = await loadMemoriesFromDir25(paths.personalDir);
|
|
7351
7596
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
7352
7597
|
if (!found) {
|
|
7353
7598
|
ui.error(`No personal memory with id "${id}". (Promotion only applies to personal scope.)`);
|
|
@@ -7373,7 +7618,7 @@ function registerMemoryPromote(memory2) {
|
|
|
7373
7618
|
}
|
|
7374
7619
|
|
|
7375
7620
|
// src/commands/memory-approve.ts
|
|
7376
|
-
import { existsSync as
|
|
7621
|
+
import { existsSync as existsSync34 } from "fs";
|
|
7377
7622
|
import { writeFile as writeFile16 } from "fs/promises";
|
|
7378
7623
|
import path16 from "path";
|
|
7379
7624
|
import "commander";
|
|
@@ -7386,12 +7631,12 @@ function registerMemoryApprove(memory2) {
|
|
|
7386
7631
|
memory2.command("approve [id]").description("Mark a memory as 'validated'. Use --all to bulk-approve all proposed/draft memories.").option("--all", "approve all proposed and draft memories at once").option("--pending", "approve all memories with status 'proposed'").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
7387
7632
|
const root = findProjectRoot14(opts.dir);
|
|
7388
7633
|
const paths = resolveHaivePaths11(root);
|
|
7389
|
-
if (!
|
|
7634
|
+
if (!existsSync34(paths.memoriesDir)) {
|
|
7390
7635
|
ui.error(`No .ai/memories at ${root}.`);
|
|
7391
7636
|
process.exitCode = 1;
|
|
7392
7637
|
return;
|
|
7393
7638
|
}
|
|
7394
|
-
const all = await
|
|
7639
|
+
const all = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
7395
7640
|
if (opts.all || opts.pending) {
|
|
7396
7641
|
const candidates = all.filter((m) => {
|
|
7397
7642
|
const s = m.memory.frontmatter.status;
|
|
@@ -7445,7 +7690,7 @@ function registerMemoryApprove(memory2) {
|
|
|
7445
7690
|
|
|
7446
7691
|
// src/commands/memory-update.ts
|
|
7447
7692
|
import { writeFile as writeFile17 } from "fs/promises";
|
|
7448
|
-
import { existsSync as
|
|
7693
|
+
import { existsSync as existsSync35 } from "fs";
|
|
7449
7694
|
import path17 from "path";
|
|
7450
7695
|
import "commander";
|
|
7451
7696
|
import {
|
|
@@ -7457,12 +7702,12 @@ function registerMemoryUpdate(memory2) {
|
|
|
7457
7702
|
memory2.command("update <id>").description("Update body, tags, or anchor of an existing memory (preserves id and usage history)").option("--title <text>", "new title \u2014 replaces the first heading of the body").option("--body <text>", "new Markdown body \u2014 replaces the existing body").option("--tags <csv>", "new tags, comma-separated \u2014 fully replaces existing tags").option("--paths <csv>", "new anchor paths, comma-separated").option("--symbols <csv>", "new anchor symbols, comma-separated").option("--commit <sha>", "new anchor commit SHA").option("--domain <domain>", "new domain label").option("--author <author>", "new author handle or email").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
7458
7703
|
const root = findProjectRoot15(opts.dir);
|
|
7459
7704
|
const paths = resolveHaivePaths12(root);
|
|
7460
|
-
if (!
|
|
7705
|
+
if (!existsSync35(paths.memoriesDir)) {
|
|
7461
7706
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
7462
7707
|
process.exitCode = 1;
|
|
7463
7708
|
return;
|
|
7464
7709
|
}
|
|
7465
|
-
const memories = await
|
|
7710
|
+
const memories = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
7466
7711
|
const loaded = memories.find((m) => m.memory.frontmatter.id === id);
|
|
7467
7712
|
if (!loaded) {
|
|
7468
7713
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -7529,7 +7774,7 @@ function parseCsv3(value) {
|
|
|
7529
7774
|
|
|
7530
7775
|
// src/commands/memory-auto-promote.ts
|
|
7531
7776
|
import { writeFile as writeFile18 } from "fs/promises";
|
|
7532
|
-
import { existsSync as
|
|
7777
|
+
import { existsSync as existsSync36 } from "fs";
|
|
7533
7778
|
import path18 from "path";
|
|
7534
7779
|
import "commander";
|
|
7535
7780
|
import {
|
|
@@ -7549,7 +7794,7 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
7549
7794
|
).option("--apply", "actually write status=validated to disk (default: dry-run)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
7550
7795
|
const root = findProjectRoot16(opts.dir);
|
|
7551
7796
|
const paths = resolveHaivePaths13(root);
|
|
7552
|
-
if (!
|
|
7797
|
+
if (!existsSync36(paths.memoriesDir)) {
|
|
7553
7798
|
ui.error(`No .ai/memories at ${root}.`);
|
|
7554
7799
|
process.exitCode = 1;
|
|
7555
7800
|
return;
|
|
@@ -7558,7 +7803,7 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
7558
7803
|
minReads: Number(opts.minReads ?? DEFAULT_AUTO_PROMOTE_RULE3.minReads),
|
|
7559
7804
|
maxRejections: Number(opts.maxRejections ?? DEFAULT_AUTO_PROMOTE_RULE3.maxRejections)
|
|
7560
7805
|
};
|
|
7561
|
-
const memories = await
|
|
7806
|
+
const memories = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
7562
7807
|
const usage = await loadUsageIndex13(paths);
|
|
7563
7808
|
const eligible = memories.filter(
|
|
7564
7809
|
({ memory: memory3 }) => isAutoPromoteEligible3(memory3.frontmatter, getUsage11(usage, memory3.frontmatter.id), rule)
|
|
@@ -7592,7 +7837,7 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
7592
7837
|
|
|
7593
7838
|
// src/commands/memory-edit.ts
|
|
7594
7839
|
import { spawn as spawn3 } from "child_process";
|
|
7595
|
-
import { existsSync as
|
|
7840
|
+
import { existsSync as existsSync37 } from "fs";
|
|
7596
7841
|
import { readFile as readFile10 } from "fs/promises";
|
|
7597
7842
|
import path19 from "path";
|
|
7598
7843
|
import "commander";
|
|
@@ -7605,12 +7850,12 @@ function registerMemoryEdit(memory2) {
|
|
|
7605
7850
|
memory2.command("edit <id>").description("Open a memory in $EDITOR and re-validate when you save").option("-e, --editor <cmd>", "editor command (defaults to $EDITOR or 'vi')").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
7606
7851
|
const root = findProjectRoot17(opts.dir);
|
|
7607
7852
|
const paths = resolveHaivePaths14(root);
|
|
7608
|
-
if (!
|
|
7853
|
+
if (!existsSync37(paths.memoriesDir)) {
|
|
7609
7854
|
ui.error(`No .ai/memories at ${root}.`);
|
|
7610
7855
|
process.exitCode = 1;
|
|
7611
7856
|
return;
|
|
7612
7857
|
}
|
|
7613
|
-
const all = await
|
|
7858
|
+
const all = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
7614
7859
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
7615
7860
|
if (!found) {
|
|
7616
7861
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -7645,7 +7890,7 @@ function runEditor(editor, file) {
|
|
|
7645
7890
|
}
|
|
7646
7891
|
|
|
7647
7892
|
// src/commands/memory-for-files.ts
|
|
7648
|
-
import { existsSync as
|
|
7893
|
+
import { existsSync as existsSync38 } from "fs";
|
|
7649
7894
|
import path20 from "path";
|
|
7650
7895
|
import "commander";
|
|
7651
7896
|
import {
|
|
@@ -7661,12 +7906,12 @@ function registerMemoryForFiles(memory2) {
|
|
|
7661
7906
|
memory2.command("for-files <files...>").description("Show memories relevant to the given files (anchor overlap, module, domain)").option("-d, --dir <dir>", "project root").action(async (files, opts) => {
|
|
7662
7907
|
const root = findProjectRoot18(opts.dir);
|
|
7663
7908
|
const paths = resolveHaivePaths15(root);
|
|
7664
|
-
if (!
|
|
7909
|
+
if (!existsSync38(paths.memoriesDir)) {
|
|
7665
7910
|
ui.error(`No .ai/memories at ${root}.`);
|
|
7666
7911
|
process.exitCode = 1;
|
|
7667
7912
|
return;
|
|
7668
7913
|
}
|
|
7669
|
-
const all = await
|
|
7914
|
+
const all = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
7670
7915
|
const usage = await loadUsageIndex14(paths);
|
|
7671
7916
|
const inferred = inferModulesFromPaths4(files);
|
|
7672
7917
|
const byAnchor = [];
|
|
@@ -7773,7 +8018,7 @@ function printGroup(root, label, loaded, usage) {
|
|
|
7773
8018
|
}
|
|
7774
8019
|
|
|
7775
8020
|
// src/commands/memory-hot.ts
|
|
7776
|
-
import { existsSync as
|
|
8021
|
+
import { existsSync as existsSync39 } from "fs";
|
|
7777
8022
|
import path21 from "path";
|
|
7778
8023
|
import "commander";
|
|
7779
8024
|
import {
|
|
@@ -7786,13 +8031,13 @@ function registerMemoryHot(memory2) {
|
|
|
7786
8031
|
memory2.command("hot").description("List memories actively used but not yet validated (good promotion candidates)").option("--threshold <n>", "minimum read_count to qualify", "3").option("--status <status>", "limit to one status (default: draft + proposed)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
7787
8032
|
const root = findProjectRoot19(opts.dir);
|
|
7788
8033
|
const paths = resolveHaivePaths16(root);
|
|
7789
|
-
if (!
|
|
8034
|
+
if (!existsSync39(paths.memoriesDir)) {
|
|
7790
8035
|
ui.error(`No .ai/memories at ${root}.`);
|
|
7791
8036
|
process.exitCode = 1;
|
|
7792
8037
|
return;
|
|
7793
8038
|
}
|
|
7794
8039
|
const threshold = Math.max(1, Number(opts.threshold ?? 3));
|
|
7795
|
-
const all = await
|
|
8040
|
+
const all = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
7796
8041
|
const usage = await loadUsageIndex15(paths);
|
|
7797
8042
|
const candidates = all.filter(({ memory: mem }) => {
|
|
7798
8043
|
const fm = mem.frontmatter;
|
|
@@ -7824,7 +8069,7 @@ function registerMemoryHot(memory2) {
|
|
|
7824
8069
|
|
|
7825
8070
|
// src/commands/memory-tried.ts
|
|
7826
8071
|
import { mkdir as mkdir11, writeFile as writeFile19 } from "fs/promises";
|
|
7827
|
-
import { existsSync as
|
|
8072
|
+
import { existsSync as existsSync40 } from "fs";
|
|
7828
8073
|
import path23 from "path";
|
|
7829
8074
|
import "commander";
|
|
7830
8075
|
import {
|
|
@@ -7853,7 +8098,7 @@ function registerMemoryTried(memory2) {
|
|
|
7853
8098
|
).requiredOption("--what <text>", "what approach was tried (short, descriptive title)").requiredOption("--why-failed <text>", "why it failed or should NOT be used (include the exact error if possible)").option("--instead <text>", "the correct approach to use instead").option("--scope <scope>", "personal | team | module (default: personal)", "personal").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags").option("--paths <csv>", "anchor paths, comma-separated").option("--author <author>", "author email or handle").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
7854
8099
|
const root = findProjectRoot20(opts.dir);
|
|
7855
8100
|
const paths = resolveHaivePaths17(root);
|
|
7856
|
-
if (!
|
|
8101
|
+
if (!existsSync40(paths.haiveDir)) {
|
|
7857
8102
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
7858
8103
|
process.exitCode = 1;
|
|
7859
8104
|
return;
|
|
@@ -7877,7 +8122,7 @@ function registerMemoryTried(memory2) {
|
|
|
7877
8122
|
const body = lines.join("\n") + "\n";
|
|
7878
8123
|
const file = memoryFilePath8(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
7879
8124
|
await mkdir11(path23.dirname(file), { recursive: true });
|
|
7880
|
-
if (
|
|
8125
|
+
if (existsSync40(file)) {
|
|
7881
8126
|
ui.error(`Memory already exists at ${file}`);
|
|
7882
8127
|
process.exitCode = 1;
|
|
7883
8128
|
return;
|
|
@@ -7893,7 +8138,7 @@ function parseCsv4(value) {
|
|
|
7893
8138
|
}
|
|
7894
8139
|
|
|
7895
8140
|
// src/commands/memory-pending.ts
|
|
7896
|
-
import { existsSync as
|
|
8141
|
+
import { existsSync as existsSync41 } from "fs";
|
|
7897
8142
|
import path24 from "path";
|
|
7898
8143
|
import "commander";
|
|
7899
8144
|
import {
|
|
@@ -7906,12 +8151,12 @@ function registerMemoryPending(memory2) {
|
|
|
7906
8151
|
memory2.command("pending").description("List 'proposed' memories awaiting review (sorted by reads desc)").option("--scope <scope>", "filter by scope (personal | team | module)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
7907
8152
|
const root = findProjectRoot21(opts.dir);
|
|
7908
8153
|
const paths = resolveHaivePaths18(root);
|
|
7909
|
-
if (!
|
|
8154
|
+
if (!existsSync41(paths.memoriesDir)) {
|
|
7910
8155
|
ui.error(`No .ai/memories at ${root}.`);
|
|
7911
8156
|
process.exitCode = 1;
|
|
7912
8157
|
return;
|
|
7913
8158
|
}
|
|
7914
|
-
const all = await
|
|
8159
|
+
const all = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
7915
8160
|
const usage = await loadUsageIndex16(paths);
|
|
7916
8161
|
const proposed = all.filter(({ memory: mem }) => {
|
|
7917
8162
|
if (mem.frontmatter.status !== "proposed") return false;
|
|
@@ -7941,7 +8186,7 @@ function registerMemoryPending(memory2) {
|
|
|
7941
8186
|
}
|
|
7942
8187
|
|
|
7943
8188
|
// src/commands/memory-query.ts
|
|
7944
|
-
import { existsSync as
|
|
8189
|
+
import { existsSync as existsSync43 } from "fs";
|
|
7945
8190
|
import path25 from "path";
|
|
7946
8191
|
import "commander";
|
|
7947
8192
|
import {
|
|
@@ -7958,7 +8203,7 @@ function registerMemoryQuery(memory2) {
|
|
|
7958
8203
|
memory2.command("query <text>").alias("search").description("Search memories by id, tag, or substring (AND, OR fallback). Alias: search").option("-d, --dir <dir>", "project root").option("--limit <n>", "max results", "20").option("--scope <scope>", "personal | team | module").option("--status <csv>", "filter by status (draft,proposed,validated,stale,rejected)").option("--show-rejected", "include rejected memories (hidden by default)").action(async (text, opts) => {
|
|
7959
8204
|
const root = findProjectRoot22(opts.dir);
|
|
7960
8205
|
const paths = resolveHaivePaths19(root);
|
|
7961
|
-
if (!
|
|
8206
|
+
if (!existsSync43(paths.memoriesDir)) {
|
|
7962
8207
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
7963
8208
|
process.exitCode = 1;
|
|
7964
8209
|
return;
|
|
@@ -7969,7 +8214,7 @@ function registerMemoryQuery(memory2) {
|
|
|
7969
8214
|
return;
|
|
7970
8215
|
}
|
|
7971
8216
|
const statusFilter = opts.status ? opts.status.split(",").map((s) => s.trim()) : null;
|
|
7972
|
-
const all = await
|
|
8217
|
+
const all = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
7973
8218
|
const passesFilters2 = (mem) => {
|
|
7974
8219
|
const fm = mem.frontmatter;
|
|
7975
8220
|
if (opts.scope && fm.scope !== opts.scope) return false;
|
|
@@ -8017,7 +8262,7 @@ ${top.length} of ${matches.length} match${matches.length === 1 ? "" : "es"}`)
|
|
|
8017
8262
|
|
|
8018
8263
|
// src/commands/memory-reject.ts
|
|
8019
8264
|
import { writeFile as writeFile20 } from "fs/promises";
|
|
8020
|
-
import { existsSync as
|
|
8265
|
+
import { existsSync as existsSync44 } from "fs";
|
|
8021
8266
|
import "commander";
|
|
8022
8267
|
import {
|
|
8023
8268
|
findProjectRoot as findProjectRoot23,
|
|
@@ -8031,12 +8276,12 @@ function registerMemoryReject(memory2) {
|
|
|
8031
8276
|
memory2.command("reject <id>").description("Record a rejection (blocks auto-promotion and lowers confidence)").option("-r, --reason <reason>", "why this memory is being rejected").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
8032
8277
|
const root = findProjectRoot23(opts.dir);
|
|
8033
8278
|
const paths = resolveHaivePaths20(root);
|
|
8034
|
-
if (!
|
|
8279
|
+
if (!existsSync44(paths.memoriesDir)) {
|
|
8035
8280
|
ui.error(`No .ai/memories at ${root}.`);
|
|
8036
8281
|
process.exitCode = 1;
|
|
8037
8282
|
return;
|
|
8038
8283
|
}
|
|
8039
|
-
const memories = await
|
|
8284
|
+
const memories = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
8040
8285
|
const loaded = memories.find((m) => m.memory.frontmatter.id === id);
|
|
8041
8286
|
if (!loaded) {
|
|
8042
8287
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -8067,7 +8312,7 @@ function registerMemoryReject(memory2) {
|
|
|
8067
8312
|
}
|
|
8068
8313
|
|
|
8069
8314
|
// src/commands/memory-rm.ts
|
|
8070
|
-
import { existsSync as
|
|
8315
|
+
import { existsSync as existsSync45 } from "fs";
|
|
8071
8316
|
import { unlink as unlink3 } from "fs/promises";
|
|
8072
8317
|
import path26 from "path";
|
|
8073
8318
|
import { createInterface } from "readline/promises";
|
|
@@ -8082,12 +8327,12 @@ function registerMemoryRm(memory2) {
|
|
|
8082
8327
|
memory2.command("rm <id>").description("Delete a memory file (and its usage entry by default)").option("-y, --yes", "skip the confirmation prompt").option("--keep-usage", "do not remove the usage.json entry").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
8083
8328
|
const root = findProjectRoot24(opts.dir);
|
|
8084
8329
|
const paths = resolveHaivePaths21(root);
|
|
8085
|
-
if (!
|
|
8330
|
+
if (!existsSync45(paths.memoriesDir)) {
|
|
8086
8331
|
ui.error(`No .ai/memories at ${root}.`);
|
|
8087
8332
|
process.exitCode = 1;
|
|
8088
8333
|
return;
|
|
8089
8334
|
}
|
|
8090
|
-
const all = await
|
|
8335
|
+
const all = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
8091
8336
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
8092
8337
|
if (!found) {
|
|
8093
8338
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -8118,7 +8363,7 @@ function registerMemoryRm(memory2) {
|
|
|
8118
8363
|
}
|
|
8119
8364
|
|
|
8120
8365
|
// src/commands/memory-show.ts
|
|
8121
|
-
import { existsSync as
|
|
8366
|
+
import { existsSync as existsSync46 } from "fs";
|
|
8122
8367
|
import { readFile as readFile11 } from "fs/promises";
|
|
8123
8368
|
import path27 from "path";
|
|
8124
8369
|
import "commander";
|
|
@@ -8133,12 +8378,12 @@ function registerMemoryShow(memory2) {
|
|
|
8133
8378
|
memory2.command("show <id>").description("Print a memory's frontmatter, body, and confidence/usage").option("--raw", "print the raw file contents instead of a summary").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
8134
8379
|
const root = findProjectRoot25(opts.dir);
|
|
8135
8380
|
const paths = resolveHaivePaths22(root);
|
|
8136
|
-
if (!
|
|
8381
|
+
if (!existsSync46(paths.memoriesDir)) {
|
|
8137
8382
|
ui.error(`No .ai/memories at ${root}.`);
|
|
8138
8383
|
process.exitCode = 1;
|
|
8139
8384
|
return;
|
|
8140
8385
|
}
|
|
8141
|
-
const all = await
|
|
8386
|
+
const all = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
8142
8387
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
8143
8388
|
if (!found) {
|
|
8144
8389
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -8177,7 +8422,7 @@ function registerMemoryShow(memory2) {
|
|
|
8177
8422
|
}
|
|
8178
8423
|
|
|
8179
8424
|
// src/commands/memory-stats.ts
|
|
8180
|
-
import { existsSync as
|
|
8425
|
+
import { existsSync as existsSync47 } from "fs";
|
|
8181
8426
|
import path28 from "path";
|
|
8182
8427
|
import "commander";
|
|
8183
8428
|
import {
|
|
@@ -8191,12 +8436,12 @@ function registerMemoryStats(memory2) {
|
|
|
8191
8436
|
memory2.command("stats").description("Show usage stats and confidence levels per memory").option("--id <id>", "show stats for a single memory id").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8192
8437
|
const root = findProjectRoot26(opts.dir);
|
|
8193
8438
|
const paths = resolveHaivePaths23(root);
|
|
8194
|
-
if (!
|
|
8439
|
+
if (!existsSync47(paths.memoriesDir)) {
|
|
8195
8440
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
8196
8441
|
process.exitCode = 1;
|
|
8197
8442
|
return;
|
|
8198
8443
|
}
|
|
8199
|
-
const all = await
|
|
8444
|
+
const all = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
8200
8445
|
const usage = await loadUsageIndex20(paths);
|
|
8201
8446
|
const target = opts.id ? all.filter((m) => m.memory.frontmatter.id === opts.id) : all;
|
|
8202
8447
|
if (target.length === 0) {
|
|
@@ -8223,7 +8468,7 @@ function registerMemoryStats(memory2) {
|
|
|
8223
8468
|
|
|
8224
8469
|
// src/commands/memory-verify.ts
|
|
8225
8470
|
import { writeFile as writeFile21 } from "fs/promises";
|
|
8226
|
-
import { existsSync as
|
|
8471
|
+
import { existsSync as existsSync48 } from "fs";
|
|
8227
8472
|
import path29 from "path";
|
|
8228
8473
|
import "commander";
|
|
8229
8474
|
import {
|
|
@@ -8238,12 +8483,12 @@ function registerMemoryVerify(memory2) {
|
|
|
8238
8483
|
).option("--id <id>", "verify a single memory by id").option("--all", "verify every memory (default if --id is omitted)").option("--update", "write status=stale or status=validated back to disk").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8239
8484
|
const root = findProjectRoot27(opts.dir);
|
|
8240
8485
|
const paths = resolveHaivePaths24(root);
|
|
8241
|
-
if (!
|
|
8486
|
+
if (!existsSync48(paths.memoriesDir)) {
|
|
8242
8487
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
8243
8488
|
process.exitCode = 1;
|
|
8244
8489
|
return;
|
|
8245
8490
|
}
|
|
8246
|
-
const all = await
|
|
8491
|
+
const all = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
8247
8492
|
const targets = opts.id ? all.filter((m) => m.memory.frontmatter.id === opts.id) : all;
|
|
8248
8493
|
if (opts.id && targets.length === 0) {
|
|
8249
8494
|
ui.error(`No memory with id "${opts.id}".`);
|
|
@@ -8325,7 +8570,7 @@ function applyVerification2(mem, result) {
|
|
|
8325
8570
|
|
|
8326
8571
|
// src/commands/memory-import.ts
|
|
8327
8572
|
import { readFile as readFile12 } from "fs/promises";
|
|
8328
|
-
import { existsSync as
|
|
8573
|
+
import { existsSync as existsSync49 } from "fs";
|
|
8329
8574
|
import "commander";
|
|
8330
8575
|
import {
|
|
8331
8576
|
findProjectRoot as findProjectRoot28,
|
|
@@ -8337,12 +8582,12 @@ function registerMemoryImport(memory2) {
|
|
|
8337
8582
|
).requiredOption("--from <file>", "Markdown/text file to import from").option("--scope <scope>", "personal | team (default: team)", "team").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8338
8583
|
const root = findProjectRoot28(opts.dir);
|
|
8339
8584
|
const paths = resolveHaivePaths25(root);
|
|
8340
|
-
if (!
|
|
8585
|
+
if (!existsSync49(paths.haiveDir)) {
|
|
8341
8586
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
8342
8587
|
process.exitCode = 1;
|
|
8343
8588
|
return;
|
|
8344
8589
|
}
|
|
8345
|
-
if (!
|
|
8590
|
+
if (!existsSync49(opts.from)) {
|
|
8346
8591
|
ui.error(`File not found: ${opts.from}`);
|
|
8347
8592
|
process.exitCode = 1;
|
|
8348
8593
|
return;
|
|
@@ -8375,7 +8620,7 @@ function registerMemoryImport(memory2) {
|
|
|
8375
8620
|
}
|
|
8376
8621
|
|
|
8377
8622
|
// src/commands/memory-import-changelog.ts
|
|
8378
|
-
import { existsSync as
|
|
8623
|
+
import { existsSync as existsSync50 } from "fs";
|
|
8379
8624
|
import { readFile as readFile13, mkdir as mkdir12, writeFile as writeFile23 } from "fs/promises";
|
|
8380
8625
|
import path30 from "path";
|
|
8381
8626
|
import "commander";
|
|
@@ -8449,7 +8694,7 @@ function registerMemoryImportChangelog(memory2) {
|
|
|
8449
8694
|
const root = findProjectRoot29(opts.dir);
|
|
8450
8695
|
const paths = resolveHaivePaths26(root);
|
|
8451
8696
|
const changelogPath = path30.resolve(root, opts.fromChangelog);
|
|
8452
|
-
if (!
|
|
8697
|
+
if (!existsSync50(changelogPath)) {
|
|
8453
8698
|
ui.error(`CHANGELOG not found: ${changelogPath}`);
|
|
8454
8699
|
process.exitCode = 1;
|
|
8455
8700
|
return;
|
|
@@ -8535,7 +8780,7 @@ ${ui.bold(`Imported ${saved} changelog entr${saved === 1 ? "y" : "ies"} from ${p
|
|
|
8535
8780
|
}
|
|
8536
8781
|
|
|
8537
8782
|
// src/commands/memory-digest.ts
|
|
8538
|
-
import { existsSync as
|
|
8783
|
+
import { existsSync as existsSync51 } from "fs";
|
|
8539
8784
|
import { writeFile as writeFile24 } from "fs/promises";
|
|
8540
8785
|
import path31 from "path";
|
|
8541
8786
|
import "commander";
|
|
@@ -8543,7 +8788,7 @@ import {
|
|
|
8543
8788
|
deriveConfidence as deriveConfidence12,
|
|
8544
8789
|
findProjectRoot as findProjectRoot30,
|
|
8545
8790
|
getUsage as getUsage17,
|
|
8546
|
-
loadMemoriesFromDir as
|
|
8791
|
+
loadMemoriesFromDir as loadMemoriesFromDir26,
|
|
8547
8792
|
loadUsageIndex as loadUsageIndex21,
|
|
8548
8793
|
resolveHaivePaths as resolveHaivePaths27
|
|
8549
8794
|
} from "@hiveai/core";
|
|
@@ -8560,7 +8805,7 @@ function registerMemoryDigest(program2) {
|
|
|
8560
8805
|
).option("--days <n>", "look-back window in days (default: 7)", "7").option("--scope <scope>", "personal | team | module | all (default: team)", "team").option("--out <file>", "write digest to a file instead of stdout").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8561
8806
|
const root = findProjectRoot30(opts.dir);
|
|
8562
8807
|
const paths = resolveHaivePaths27(root);
|
|
8563
|
-
if (!
|
|
8808
|
+
if (!existsSync51(paths.memoriesDir)) {
|
|
8564
8809
|
ui.error("No .ai/memories found. Run `haive init` first.");
|
|
8565
8810
|
process.exitCode = 1;
|
|
8566
8811
|
return;
|
|
@@ -8568,7 +8813,7 @@ function registerMemoryDigest(program2) {
|
|
|
8568
8813
|
const days = Math.max(1, Number(opts.days ?? 7));
|
|
8569
8814
|
const scopeFilter = opts.scope ?? "team";
|
|
8570
8815
|
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1e3);
|
|
8571
|
-
const all = await
|
|
8816
|
+
const all = await loadMemoriesFromDir26(paths.memoriesDir);
|
|
8572
8817
|
const usage = await loadUsageIndex21(paths);
|
|
8573
8818
|
const recent = all.filter(({ memory: mem }) => {
|
|
8574
8819
|
const fm = mem.frontmatter;
|
|
@@ -8643,20 +8888,20 @@ function registerMemoryDigest(program2) {
|
|
|
8643
8888
|
|
|
8644
8889
|
// src/commands/session-end.ts
|
|
8645
8890
|
import { writeFile as writeFile25, mkdir as mkdir13, readFile as readFile14, rm as rm2 } from "fs/promises";
|
|
8646
|
-
import { existsSync as
|
|
8891
|
+
import { existsSync as existsSync53 } from "fs";
|
|
8647
8892
|
import path33 from "path";
|
|
8648
8893
|
import "commander";
|
|
8649
8894
|
import {
|
|
8650
8895
|
buildFrontmatter as buildFrontmatter10,
|
|
8651
8896
|
findProjectRoot as findProjectRoot31,
|
|
8652
|
-
loadMemoriesFromDir as
|
|
8897
|
+
loadMemoriesFromDir as loadMemoriesFromDir27,
|
|
8653
8898
|
memoryFilePath as memoryFilePath9,
|
|
8654
8899
|
resolveHaivePaths as resolveHaivePaths28,
|
|
8655
8900
|
serializeMemory as serializeMemory21
|
|
8656
8901
|
} from "@hiveai/core";
|
|
8657
8902
|
async function buildAutoRecap(paths) {
|
|
8658
8903
|
const obsFile = path33.join(paths.haiveDir, ".cache", "observations.jsonl");
|
|
8659
|
-
if (!
|
|
8904
|
+
if (!existsSync53(obsFile)) return null;
|
|
8660
8905
|
const raw = await readFile14(obsFile, "utf8").catch(() => "");
|
|
8661
8906
|
if (!raw.trim()) return null;
|
|
8662
8907
|
const lines = raw.split("\n").filter(Boolean);
|
|
@@ -8737,7 +8982,7 @@ function registerSessionEnd(session2) {
|
|
|
8737
8982
|
).option("--goal <text>", "what you were trying to accomplish (1\u20132 sentences)").option("--accomplished <text>", "what was actually done (bullet list recommended)").option("--discoveries <text>", "bugs, surprises, or inconsistencies found during this session").option("--files <csv>", "key files touched, comma-separated (used as anchor for staleness detection)").option("--next <text>", "what should happen next (for the next session or a teammate)").option("--scope <scope>", "personal | team | module (default: personal)", "personal").option("--module <name>", "module name (required when scope=module)").option("--auto", "synthesize the recap from .ai/.cache/observations.jsonl (used by Claude Code SessionEnd hook)").option("--quiet", "suppress non-error output (for hook use)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8738
8983
|
const root = findProjectRoot31(opts.dir);
|
|
8739
8984
|
const paths = resolveHaivePaths28(root);
|
|
8740
|
-
if (!
|
|
8985
|
+
if (!existsSync53(paths.haiveDir)) {
|
|
8741
8986
|
if (opts.auto || opts.quiet) return;
|
|
8742
8987
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
8743
8988
|
process.exitCode = 1;
|
|
@@ -8769,7 +9014,7 @@ function registerSessionEnd(session2) {
|
|
|
8769
9014
|
});
|
|
8770
9015
|
const topic = recapTopic2(scope, opts.module);
|
|
8771
9016
|
const filesTouched = parseCsv5(resolvedFiles);
|
|
8772
|
-
const missingPaths = filesTouched.filter((p) => !
|
|
9017
|
+
const missingPaths = filesTouched.filter((p) => !existsSync53(path33.resolve(root, p)));
|
|
8773
9018
|
if (missingPaths.length > 0 && !opts.quiet) {
|
|
8774
9019
|
ui.warn(`Anchor path${missingPaths.length > 1 ? "s" : ""} not found in project (will be stale):`);
|
|
8775
9020
|
for (const p of missingPaths) ui.warn(` \u2717 ${p}`);
|
|
@@ -8777,11 +9022,11 @@ function registerSessionEnd(session2) {
|
|
|
8777
9022
|
const cleanupObservations = async () => {
|
|
8778
9023
|
if (!opts.auto) return;
|
|
8779
9024
|
const obsFile = path33.join(paths.haiveDir, ".cache", "observations.jsonl");
|
|
8780
|
-
if (
|
|
9025
|
+
if (existsSync53(obsFile)) await rm2(obsFile).catch(() => {
|
|
8781
9026
|
});
|
|
8782
9027
|
};
|
|
8783
|
-
if (
|
|
8784
|
-
const existing = await
|
|
9028
|
+
if (existsSync53(paths.memoriesDir)) {
|
|
9029
|
+
const existing = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
8785
9030
|
const topicMatch = existing.find(
|
|
8786
9031
|
({ memory: memory2 }) => memory2.frontmatter.topic === topic && memory2.frontmatter.scope === scope && (!opts.module || memory2.frontmatter.module === opts.module)
|
|
8787
9032
|
);
|
|
@@ -8834,7 +9079,7 @@ function parseCsv5(value) {
|
|
|
8834
9079
|
}
|
|
8835
9080
|
|
|
8836
9081
|
// src/commands/snapshot.ts
|
|
8837
|
-
import { existsSync as
|
|
9082
|
+
import { existsSync as existsSync54 } from "fs";
|
|
8838
9083
|
import { readdir as readdir4 } from "fs/promises";
|
|
8839
9084
|
import path34 from "path";
|
|
8840
9085
|
import "commander";
|
|
@@ -8869,14 +9114,14 @@ function registerSnapshot(program2) {
|
|
|
8869
9114
|
).option("--diff", "compare the contract against its stored snapshot").option("--list", "list all stored contract snapshots").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8870
9115
|
const root = findProjectRoot32(opts.dir);
|
|
8871
9116
|
const paths = resolveHaivePaths29(root);
|
|
8872
|
-
if (!
|
|
9117
|
+
if (!existsSync54(paths.haiveDir)) {
|
|
8873
9118
|
ui.error("No .ai/ found. Run `haive init` first.");
|
|
8874
9119
|
process.exitCode = 1;
|
|
8875
9120
|
return;
|
|
8876
9121
|
}
|
|
8877
9122
|
if (opts.list) {
|
|
8878
9123
|
const contractsDir = path34.join(paths.haiveDir, "contracts");
|
|
8879
|
-
if (!
|
|
9124
|
+
if (!existsSync54(contractsDir)) {
|
|
8880
9125
|
console.log(ui.dim("No contract snapshots found."));
|
|
8881
9126
|
return;
|
|
8882
9127
|
}
|
|
@@ -9000,7 +9245,7 @@ function detectFormat(filePath) {
|
|
|
9000
9245
|
}
|
|
9001
9246
|
|
|
9002
9247
|
// src/commands/hub.ts
|
|
9003
|
-
import { existsSync as
|
|
9248
|
+
import { existsSync as existsSync55 } from "fs";
|
|
9004
9249
|
import { mkdir as mkdir14, readFile as readFile15, writeFile as writeFile26, copyFile } from "fs/promises";
|
|
9005
9250
|
import path35 from "path";
|
|
9006
9251
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
@@ -9008,7 +9253,7 @@ import "commander";
|
|
|
9008
9253
|
import {
|
|
9009
9254
|
findProjectRoot as findProjectRoot33,
|
|
9010
9255
|
loadConfig as loadConfig6,
|
|
9011
|
-
loadMemoriesFromDir as
|
|
9256
|
+
loadMemoriesFromDir as loadMemoriesFromDir28,
|
|
9012
9257
|
resolveHaivePaths as resolveHaivePaths30,
|
|
9013
9258
|
saveConfig as saveConfig2,
|
|
9014
9259
|
serializeMemory as serializeMemory23
|
|
@@ -9102,7 +9347,7 @@ Next steps:
|
|
|
9102
9347
|
return;
|
|
9103
9348
|
}
|
|
9104
9349
|
const hubRoot = path35.resolve(root, config.hubPath);
|
|
9105
|
-
if (!
|
|
9350
|
+
if (!existsSync55(hubRoot)) {
|
|
9106
9351
|
ui.error(`Hub not found at ${hubRoot}. Run \`haive hub init ${config.hubPath}\` first.`);
|
|
9107
9352
|
process.exitCode = 1;
|
|
9108
9353
|
return;
|
|
@@ -9110,7 +9355,7 @@ Next steps:
|
|
|
9110
9355
|
const projectName = path35.basename(root);
|
|
9111
9356
|
const destDir = path35.join(hubRoot, ".ai", "memories", "shared", projectName);
|
|
9112
9357
|
await mkdir14(destDir, { recursive: true });
|
|
9113
|
-
const all = await
|
|
9358
|
+
const all = await loadMemoriesFromDir28(paths.memoriesDir);
|
|
9114
9359
|
const shared = all.filter(
|
|
9115
9360
|
({ memory: memory2 }) => memory2.frontmatter.scope === "shared" && memory2.frontmatter.status !== "rejected" && memory2.frontmatter.status !== "deprecated" && // Don't push imported memories (avoid echo loops)
|
|
9116
9361
|
!memory2.frontmatter.tags.some((t) => t.startsWith("cross-repo:"))
|
|
@@ -9172,7 +9417,7 @@ Next steps:
|
|
|
9172
9417
|
}
|
|
9173
9418
|
const hubRoot = path35.resolve(root, config.hubPath);
|
|
9174
9419
|
const hubSharedDir = path35.join(hubRoot, ".ai", "memories", "shared");
|
|
9175
|
-
if (!
|
|
9420
|
+
if (!existsSync55(hubSharedDir)) {
|
|
9176
9421
|
ui.warn("Hub has no shared memories yet. Run `haive hub push` from other projects first.");
|
|
9177
9422
|
return;
|
|
9178
9423
|
}
|
|
@@ -9227,7 +9472,7 @@ Next steps:
|
|
|
9227
9472
|
` hubPath: ${config.hubPath ? ui.green(config.hubPath) : ui.dim("not configured")}`
|
|
9228
9473
|
);
|
|
9229
9474
|
const sharedDir = path35.join(paths.memoriesDir, "shared");
|
|
9230
|
-
if (
|
|
9475
|
+
if (existsSync55(sharedDir)) {
|
|
9231
9476
|
const { readdir: readdir5 } = await import("fs/promises");
|
|
9232
9477
|
const sources = (await readdir5(sharedDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
9233
9478
|
console.log(`
|
|
@@ -9239,7 +9484,7 @@ Next steps:
|
|
|
9239
9484
|
} else {
|
|
9240
9485
|
console.log(ui.dim(" No imported shared memories yet."));
|
|
9241
9486
|
}
|
|
9242
|
-
const all = await
|
|
9487
|
+
const all = await loadMemoriesFromDir28(paths.memoriesDir);
|
|
9243
9488
|
const outgoing = all.filter(
|
|
9244
9489
|
({ memory: memory2 }) => memory2.frontmatter.scope === "shared" && !memory2.frontmatter.tags.some((t) => t.startsWith("cross-repo:"))
|
|
9245
9490
|
);
|
|
@@ -9256,13 +9501,13 @@ Next steps:
|
|
|
9256
9501
|
|
|
9257
9502
|
// src/commands/stats.ts
|
|
9258
9503
|
import "commander";
|
|
9259
|
-
import { existsSync as
|
|
9504
|
+
import { existsSync as existsSync56 } from "fs";
|
|
9260
9505
|
import { mkdir as mkdir15, writeFile as writeFile27 } from "fs/promises";
|
|
9261
9506
|
import path36 from "path";
|
|
9262
9507
|
import {
|
|
9263
9508
|
aggregateUsage,
|
|
9264
9509
|
findProjectRoot as findProjectRoot34,
|
|
9265
|
-
loadMemoriesFromDir as
|
|
9510
|
+
loadMemoriesFromDir as loadMemoriesFromDir29,
|
|
9266
9511
|
loadUsageIndex as loadUsageIndex23,
|
|
9267
9512
|
parseSince,
|
|
9268
9513
|
readUsageEvents as readUsageEvents2,
|
|
@@ -9334,8 +9579,8 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
|
|
|
9334
9579
|
const size = await usageLogSize(paths);
|
|
9335
9580
|
let events = await readUsageEvents2(paths);
|
|
9336
9581
|
let memoryCount = { team: 0, personal: 0, total_skipped_session: 0 };
|
|
9337
|
-
if (
|
|
9338
|
-
const mems = await
|
|
9582
|
+
if (existsSync56(paths.memoriesDir)) {
|
|
9583
|
+
const mems = await loadMemoriesFromDir29(paths.memoriesDir);
|
|
9339
9584
|
for (const { memory: memory2 } of mems) {
|
|
9340
9585
|
const fm = memory2.frontmatter;
|
|
9341
9586
|
if (fm.type === "session_recap") memoryCount.total_skipped_session++;
|
|
@@ -9555,14 +9800,14 @@ function summarize(name, t0, payload, notes) {
|
|
|
9555
9800
|
|
|
9556
9801
|
// src/commands/memory-suggest.ts
|
|
9557
9802
|
import { mkdir as mkdir16, writeFile as writeFile28 } from "fs/promises";
|
|
9558
|
-
import { existsSync as
|
|
9803
|
+
import { existsSync as existsSync57 } from "fs";
|
|
9559
9804
|
import path37 from "path";
|
|
9560
9805
|
import "commander";
|
|
9561
9806
|
import {
|
|
9562
9807
|
aggregateUsage as aggregateUsage2,
|
|
9563
9808
|
buildFrontmatter as buildFrontmatter11,
|
|
9564
9809
|
findProjectRoot as findProjectRoot36,
|
|
9565
|
-
loadMemoriesFromDir as
|
|
9810
|
+
loadMemoriesFromDir as loadMemoriesFromDir30,
|
|
9566
9811
|
memoryFilePath as memoryFilePath10,
|
|
9567
9812
|
parseSince as parseSince2,
|
|
9568
9813
|
readUsageEvents as readUsageEvents3,
|
|
@@ -9626,7 +9871,7 @@ function registerMemorySuggest(memory2) {
|
|
|
9626
9871
|
}
|
|
9627
9872
|
const created = [];
|
|
9628
9873
|
const skipped = [];
|
|
9629
|
-
const existing =
|
|
9874
|
+
const existing = existsSync57(paths.memoriesDir) ? await loadMemoriesFromDir30(paths.memoriesDir) : [];
|
|
9630
9875
|
for (const s of top) {
|
|
9631
9876
|
const slug = slugify(s.query);
|
|
9632
9877
|
if (!slug) {
|
|
@@ -9650,7 +9895,7 @@ function registerMemorySuggest(memory2) {
|
|
|
9650
9895
|
const body = renderTemplate(s);
|
|
9651
9896
|
const file = memoryFilePath10(paths, fm.scope, fm.id, fm.module);
|
|
9652
9897
|
await mkdir16(path37.dirname(file), { recursive: true });
|
|
9653
|
-
if (
|
|
9898
|
+
if (existsSync57(file)) {
|
|
9654
9899
|
skipped.push({ query: s.query, reason: `file already exists at ${path37.relative(root, file)}` });
|
|
9655
9900
|
continue;
|
|
9656
9901
|
}
|
|
@@ -9748,14 +9993,14 @@ function truncate2(text, max) {
|
|
|
9748
9993
|
}
|
|
9749
9994
|
|
|
9750
9995
|
// src/commands/memory-archive.ts
|
|
9751
|
-
import { existsSync as
|
|
9996
|
+
import { existsSync as existsSync58 } from "fs";
|
|
9752
9997
|
import { writeFile as writeFile29 } from "fs/promises";
|
|
9753
9998
|
import path38 from "path";
|
|
9754
9999
|
import "commander";
|
|
9755
10000
|
import {
|
|
9756
10001
|
findProjectRoot as findProjectRoot37,
|
|
9757
10002
|
getUsage as getUsage18,
|
|
9758
|
-
loadMemoriesFromDir as
|
|
10003
|
+
loadMemoriesFromDir as loadMemoriesFromDir31,
|
|
9759
10004
|
loadUsageIndex as loadUsageIndex24,
|
|
9760
10005
|
resolveHaivePaths as resolveHaivePaths34,
|
|
9761
10006
|
serializeMemory as serializeMemory25
|
|
@@ -9767,7 +10012,7 @@ function registerMemoryArchive(memory2) {
|
|
|
9767
10012
|
).option("--since <window>", "minimum age since last read (e.g. '180d', '6m')", "180d").option("--type <type>", "limit to a memory type (default 'attempt'). Pass 'all' to scan all types.", "attempt").option("--apply", "actually rewrite files (default: dry run)", false).option("--json", "emit JSON instead of human-readable output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
9768
10013
|
const root = findProjectRoot37(opts.dir);
|
|
9769
10014
|
const paths = resolveHaivePaths34(root);
|
|
9770
|
-
if (!
|
|
10015
|
+
if (!existsSync58(paths.memoriesDir)) {
|
|
9771
10016
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
9772
10017
|
process.exitCode = 1;
|
|
9773
10018
|
return;
|
|
@@ -9779,7 +10024,7 @@ function registerMemoryArchive(memory2) {
|
|
|
9779
10024
|
return;
|
|
9780
10025
|
}
|
|
9781
10026
|
const cutoff = Date.now() - minDays * MS_PER_DAY2;
|
|
9782
|
-
const all = await
|
|
10027
|
+
const all = await loadMemoriesFromDir31(paths.memoriesDir);
|
|
9783
10028
|
const usage = await loadUsageIndex24(paths);
|
|
9784
10029
|
const typeFilter = opts.type === "all" ? null : opts.type ?? "attempt";
|
|
9785
10030
|
const candidates = [];
|
|
@@ -9788,7 +10033,7 @@ function registerMemoryArchive(memory2) {
|
|
|
9788
10033
|
if (typeFilter && fm.type !== typeFilter) continue;
|
|
9789
10034
|
if (fm.status === "deprecated" || fm.status === "rejected") continue;
|
|
9790
10035
|
const hasAnyAnchor = fm.anchor.paths.length + fm.anchor.symbols.length > 0;
|
|
9791
|
-
const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !
|
|
10036
|
+
const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync58(path38.join(paths.root, p)));
|
|
9792
10037
|
const isAnchorless = !hasAnyAnchor;
|
|
9793
10038
|
if (!isAnchorless && !allPathsGone) continue;
|
|
9794
10039
|
const u = getUsage18(usage, fm.id);
|
|
@@ -9862,7 +10107,7 @@ function parseDays(input) {
|
|
|
9862
10107
|
}
|
|
9863
10108
|
|
|
9864
10109
|
// src/commands/doctor.ts
|
|
9865
|
-
import { existsSync as
|
|
10110
|
+
import { existsSync as existsSync59 } from "fs";
|
|
9866
10111
|
import { stat } from "fs/promises";
|
|
9867
10112
|
import "path";
|
|
9868
10113
|
import { execSync as execSync3 } from "child_process";
|
|
@@ -9873,7 +10118,7 @@ import {
|
|
|
9873
10118
|
getUsage as getUsage19,
|
|
9874
10119
|
loadCodeMap as loadCodeMap5,
|
|
9875
10120
|
loadConfig as loadConfig7,
|
|
9876
|
-
loadMemoriesFromDir as
|
|
10121
|
+
loadMemoriesFromDir as loadMemoriesFromDir32,
|
|
9877
10122
|
loadUsageIndex as loadUsageIndex25,
|
|
9878
10123
|
readUsageEvents as readUsageEvents4,
|
|
9879
10124
|
resolveHaivePaths as resolveHaivePaths35
|
|
@@ -9886,7 +10131,7 @@ function registerDoctor(program2) {
|
|
|
9886
10131
|
const root = findProjectRoot38(opts.dir);
|
|
9887
10132
|
const paths = resolveHaivePaths35(root);
|
|
9888
10133
|
const findings = [];
|
|
9889
|
-
if (!
|
|
10134
|
+
if (!existsSync59(paths.haiveDir)) {
|
|
9890
10135
|
findings.push({
|
|
9891
10136
|
severity: "error",
|
|
9892
10137
|
code: "not-initialized",
|
|
@@ -9895,7 +10140,7 @@ function registerDoctor(program2) {
|
|
|
9895
10140
|
});
|
|
9896
10141
|
return emit(findings, opts);
|
|
9897
10142
|
}
|
|
9898
|
-
if (!
|
|
10143
|
+
if (!existsSync59(paths.projectContext)) {
|
|
9899
10144
|
findings.push({
|
|
9900
10145
|
severity: "warn",
|
|
9901
10146
|
code: "no-project-context",
|
|
@@ -9915,7 +10160,7 @@ function registerDoctor(program2) {
|
|
|
9915
10160
|
});
|
|
9916
10161
|
}
|
|
9917
10162
|
}
|
|
9918
|
-
const memories =
|
|
10163
|
+
const memories = existsSync59(paths.memoriesDir) ? await loadMemoriesFromDir32(paths.memoriesDir) : [];
|
|
9919
10164
|
const now = Date.now();
|
|
9920
10165
|
if (memories.length === 0) {
|
|
9921
10166
|
findings.push({
|
|
@@ -10039,7 +10284,7 @@ function registerDoctor(program2) {
|
|
|
10039
10284
|
timeout: 3e3,
|
|
10040
10285
|
stdio: ["ignore", "pipe", "ignore"]
|
|
10041
10286
|
}).trim();
|
|
10042
|
-
const cliVersion = "0.9.
|
|
10287
|
+
const cliVersion = "0.9.5";
|
|
10043
10288
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
10044
10289
|
findings.push({
|
|
10045
10290
|
severity: "warn",
|
|
@@ -10088,11 +10333,11 @@ function isSearchTool(name) {
|
|
|
10088
10333
|
}
|
|
10089
10334
|
|
|
10090
10335
|
// src/commands/playback.ts
|
|
10091
|
-
import { existsSync as
|
|
10336
|
+
import { existsSync as existsSync60 } from "fs";
|
|
10092
10337
|
import "commander";
|
|
10093
10338
|
import {
|
|
10094
10339
|
findProjectRoot as findProjectRoot39,
|
|
10095
|
-
loadMemoriesFromDir as
|
|
10340
|
+
loadMemoriesFromDir as loadMemoriesFromDir33,
|
|
10096
10341
|
parseSince as parseSince3,
|
|
10097
10342
|
readUsageEvents as readUsageEvents5,
|
|
10098
10343
|
resolveHaivePaths as resolveHaivePaths36
|
|
@@ -10118,7 +10363,7 @@ function registerPlayback(program2) {
|
|
|
10118
10363
|
const filtered = cutoff > 0 ? events.filter((e) => Date.parse(e.at) >= cutoff) : events;
|
|
10119
10364
|
const gapMs = Math.max(1, parseInt(opts.sessionGap ?? "30", 10)) * MS_PER_MINUTE;
|
|
10120
10365
|
const sessions = bucketSessions(filtered, gapMs);
|
|
10121
|
-
const all =
|
|
10366
|
+
const all = existsSync60(paths.memoriesDir) ? await loadMemoriesFromDir33(paths.memoriesDir) : [];
|
|
10122
10367
|
const memByCreatedAt = all.filter(({ memory: memory2 }) => memory2.frontmatter.type !== "session_recap").map(({ memory: memory2 }) => ({ id: memory2.frontmatter.id, at: Date.parse(memory2.frontmatter.created_at) })).sort((a, b) => a.at - b.at);
|
|
10123
10368
|
const enriched = sessions.map((s, i) => {
|
|
10124
10369
|
const startMs = Date.parse(s.start);
|
|
@@ -10312,11 +10557,11 @@ function runCommand3(cmd, args, cwd) {
|
|
|
10312
10557
|
}
|
|
10313
10558
|
|
|
10314
10559
|
// src/commands/welcome.ts
|
|
10315
|
-
import { existsSync as
|
|
10560
|
+
import { existsSync as existsSync61 } from "fs";
|
|
10316
10561
|
import "commander";
|
|
10317
10562
|
import {
|
|
10318
10563
|
findProjectRoot as findProjectRoot41,
|
|
10319
|
-
loadMemoriesFromDir as
|
|
10564
|
+
loadMemoriesFromDir as loadMemoriesFromDir34,
|
|
10320
10565
|
resolveHaivePaths as resolveHaivePaths38
|
|
10321
10566
|
} from "@hiveai/core";
|
|
10322
10567
|
var TYPE_RANK = {
|
|
@@ -10333,12 +10578,12 @@ function registerWelcome(program2) {
|
|
|
10333
10578
|
).option("--limit <n>", "maximum memories listed", "20").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10334
10579
|
const root = findProjectRoot41(opts.dir);
|
|
10335
10580
|
const paths = resolveHaivePaths38(root);
|
|
10336
|
-
if (!
|
|
10581
|
+
if (!existsSync61(paths.memoriesDir)) {
|
|
10337
10582
|
ui.error(`No memories at ${paths.memoriesDir}. Run 'haive init' first.`);
|
|
10338
10583
|
process.exitCode = 1;
|
|
10339
10584
|
return;
|
|
10340
10585
|
}
|
|
10341
|
-
const all = await
|
|
10586
|
+
const all = await loadMemoriesFromDir34(paths.memoriesDir);
|
|
10342
10587
|
const team = all.filter(
|
|
10343
10588
|
({ memory: memory2 }) => memory2.frontmatter.scope === "team" && memory2.frontmatter.status !== "rejected" && memory2.frontmatter.status !== "deprecated" && memory2.frontmatter.status !== "stale" && memory2.frontmatter.type !== "session_recap"
|
|
10344
10589
|
);
|
|
@@ -10374,18 +10619,18 @@ function registerWelcome(program2) {
|
|
|
10374
10619
|
}
|
|
10375
10620
|
|
|
10376
10621
|
// src/commands/memory-lint.ts
|
|
10377
|
-
import { existsSync as
|
|
10622
|
+
import { existsSync as existsSync63 } from "fs";
|
|
10378
10623
|
import "commander";
|
|
10379
10624
|
import {
|
|
10380
10625
|
findProjectRoot as findProjectRoot42,
|
|
10381
|
-
loadMemoriesFromDir as
|
|
10626
|
+
loadMemoriesFromDir as loadMemoriesFromDir35,
|
|
10382
10627
|
resolveHaivePaths as resolveHaivePaths39
|
|
10383
10628
|
} from "@hiveai/core";
|
|
10384
10629
|
async function lintMemoriesAsync(root) {
|
|
10385
10630
|
const paths = resolveHaivePaths39(root);
|
|
10386
10631
|
const out = [];
|
|
10387
|
-
if (!
|
|
10388
|
-
const loaded = await
|
|
10632
|
+
if (!existsSync63(paths.memoriesDir)) return out;
|
|
10633
|
+
const loaded = await loadMemoriesFromDir35(paths.memoriesDir);
|
|
10389
10634
|
const ANCHOR_TYPES = /* @__PURE__ */ new Set(["decision", "architecture", "gotcha"]);
|
|
10390
10635
|
for (const { filePath, memory: memory2 } of loaded) {
|
|
10391
10636
|
const fm = memory2.frontmatter;
|
|
@@ -10472,11 +10717,43 @@ function registerMemoryLint(parent) {
|
|
|
10472
10717
|
});
|
|
10473
10718
|
}
|
|
10474
10719
|
|
|
10720
|
+
// src/commands/memory-suggest-topic.ts
|
|
10721
|
+
import "commander";
|
|
10722
|
+
import { MemoryTypeSchema as MemoryTypeSchema2, suggestTopicKey as suggestTopicKey2 } from "@hiveai/core";
|
|
10723
|
+
function registerMemorySuggestTopic(memory2) {
|
|
10724
|
+
memory2.command("suggest-topic").description("Suggest a stable topic key (topic-upsert) from type + title phrase").requiredOption(
|
|
10725
|
+
"--type <type>",
|
|
10726
|
+
"convention | decision | gotcha | architecture | glossary | attempt | session_recap"
|
|
10727
|
+
).argument("<title>", "Short title or phrase to slugify").action((title, opts) => {
|
|
10728
|
+
const parsed = MemoryTypeSchema2.safeParse(opts.type);
|
|
10729
|
+
if (!parsed.success) {
|
|
10730
|
+
ui.error(`Invalid type: ${opts.type}`);
|
|
10731
|
+
process.exit(1);
|
|
10732
|
+
}
|
|
10733
|
+
const suggestion = suggestTopicKey2(parsed.data, title);
|
|
10734
|
+
console.log(JSON.stringify({ type: parsed.data, ...suggestion }, null, 2));
|
|
10735
|
+
});
|
|
10736
|
+
}
|
|
10737
|
+
|
|
10738
|
+
// src/commands/resolve-project.ts
|
|
10739
|
+
import path40 from "path";
|
|
10740
|
+
import "commander";
|
|
10741
|
+
import { resolveProjectInfo as resolveProjectInfo2 } from "@hiveai/core";
|
|
10742
|
+
function registerResolveProject(program2) {
|
|
10743
|
+
program2.command("resolve-project").description(
|
|
10744
|
+
"Print JSON for hAIve project root resolution (HAIVE_PROJECT_ROOT, markers, .ai layout)."
|
|
10745
|
+
).option("-d, --dir <dir>", "working directory", process.cwd()).action((opts) => {
|
|
10746
|
+
const info = resolveProjectInfo2({ cwd: path40.resolve(opts.dir) });
|
|
10747
|
+
console.log(JSON.stringify({ ok: true, info }, null, 2));
|
|
10748
|
+
});
|
|
10749
|
+
}
|
|
10750
|
+
|
|
10475
10751
|
// src/index.ts
|
|
10476
|
-
var program = new
|
|
10477
|
-
program.name("haive").description("hAIve \u2014 team-first persistent memory layer for AI coding agents").version("0.9.
|
|
10752
|
+
var program = new Command44();
|
|
10753
|
+
program.name("haive").description("hAIve \u2014 team-first persistent memory layer for AI coding agents").version("0.9.5");
|
|
10478
10754
|
registerInit(program);
|
|
10479
10755
|
registerWelcome(program);
|
|
10756
|
+
registerResolveProject(program);
|
|
10480
10757
|
registerMcp(program);
|
|
10481
10758
|
registerBriefing(program);
|
|
10482
10759
|
registerTui(program);
|
|
@@ -10507,6 +10784,7 @@ registerMemoryImport(memory);
|
|
|
10507
10784
|
registerMemoryImportChangelog(memory);
|
|
10508
10785
|
registerMemoryDigest(memory);
|
|
10509
10786
|
registerMemorySuggest(memory);
|
|
10787
|
+
registerMemorySuggestTopic(memory);
|
|
10510
10788
|
registerMemoryArchive(memory);
|
|
10511
10789
|
registerMemoryLint(memory);
|
|
10512
10790
|
var session = program.command("session").description("Manage session lifecycle");
|