@hiveai/cli 0.2.16 → 0.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command28 } from "commander";
4
+ import { Command as Command29 } from "commander";
5
5
 
6
6
  // src/commands/briefing.ts
7
7
  import { existsSync } from "fs";
@@ -11,8 +11,10 @@ import {
11
11
  findProjectRoot,
12
12
  literalMatchesAllTokens,
13
13
  literalMatchesAnyToken,
14
+ loadCodeMap,
14
15
  loadMemoriesFromDir,
15
16
  memoryMatchesAnchorPaths,
17
+ queryCodeMap,
16
18
  resolveHaivePaths,
17
19
  tokenizeQuery,
18
20
  trackReads
@@ -52,7 +54,7 @@ var ui = {
52
54
  function registerBriefing(program2) {
53
55
  program2.command("briefing").description(
54
56
  "Print project context + relevant memories in one shot \u2014 ideal for agent onboarding"
55
- ).option("--task <text>", "what you are about to do \u2014 filters memories by relevance").option("--files <csv>", "comma-separated file paths being worked on (anchors memories)").option("--max-memories <n>", "cap on memories surfaced", "10").option(
57
+ ).option("--task <text>", "what you are about to do \u2014 filters memories by relevance").option("--files <csv>", "comma-separated file paths being worked on (anchors memories)").option("--symbols <csv>", "symbol names to look up in the code-map (e.g. PaymentService,TenantFilter)").option("--max-memories <n>", "cap on memories surfaced", "10").option(
56
58
  "--scope <scope>",
57
59
  "personal | team | module | all (default: team)",
58
60
  "team"
@@ -163,6 +165,34 @@ function registerBriefing(program2) {
163
165
  await trackReads(paths, ids).catch(() => {
164
166
  });
165
167
  }
168
+ const requestedSymbols = (opts.symbols ?? "").split(",").map((s) => s.trim()).filter(Boolean);
169
+ if (requestedSymbols.length > 0) {
170
+ const codeMap = await loadCodeMap(paths);
171
+ if (!codeMap) {
172
+ ui.warn("No code-map found. Run `haive index code` first to enable symbol lookup.");
173
+ } else {
174
+ console.log(`
175
+ ${ui.bold("=== Symbol Locations ===")}
176
+ `);
177
+ for (const sym of requestedSymbols) {
178
+ const { files } = queryCodeMap(codeMap, { symbol: sym });
179
+ if (files.length === 0) {
180
+ console.log(`${ui.dim(sym)} (not found in code-map)`);
181
+ } else {
182
+ for (const f of files) {
183
+ const exports = f.entry.exports.filter(
184
+ (e) => e.name.toLowerCase().includes(sym.toLowerCase())
185
+ );
186
+ for (const e of exports) {
187
+ const desc = e.description ? ` \u2014 ${e.description}` : "";
188
+ console.log(`${ui.bold(e.name)} ${ui.dim(f.path + ":" + e.line)} [${e.kind}]${desc}`);
189
+ }
190
+ }
191
+ }
192
+ }
193
+ console.log();
194
+ }
195
+ }
166
196
  });
167
197
  }
168
198
  function parseCsv(value) {
@@ -183,7 +213,7 @@ function registerTui(program2) {
183
213
  const root = findProjectRoot2(opts.dir);
184
214
  const { render } = await import("ink");
185
215
  const { createElement } = await import("react");
186
- const { Dashboard } = await import("./Dashboard-SRPCHP7Z.js");
216
+ const { Dashboard } = await import("./Dashboard-HVELRRC7.js");
187
217
  const { waitUntilExit } = render(createElement(Dashboard, { root }));
188
218
  await waitUntilExit();
189
219
  });
@@ -2129,17 +2159,124 @@ function registerMemoryImport(memory2) {
2129
2159
  });
2130
2160
  }
2131
2161
 
2132
- // src/commands/session-end.ts
2133
- import { writeFile as writeFile12, mkdir as mkdir6 } from "fs/promises";
2162
+ // src/commands/memory-digest.ts
2134
2163
  import { existsSync as existsSync25 } from "fs";
2164
+ import { writeFile as writeFile12 } from "fs/promises";
2135
2165
  import path23 from "path";
2136
2166
  import "commander";
2137
2167
  import {
2138
- buildFrontmatter as buildFrontmatter3,
2168
+ deriveConfidence as deriveConfidence4,
2139
2169
  findProjectRoot as findProjectRoot27,
2170
+ getUsage as getUsage8,
2140
2171
  loadMemoriesFromDir as loadMemoriesFromDir5,
2172
+ loadUsageIndex as loadUsageIndex10,
2173
+ resolveHaivePaths as resolveHaivePaths24
2174
+ } from "@hiveai/core";
2175
+ var CONFIDENCE_EMOJI = {
2176
+ unverified: "\u2B1C",
2177
+ low: "\u{1F7E1}",
2178
+ trusted: "\u{1F7E2}",
2179
+ authoritative: "\u2B50",
2180
+ stale: "\u{1F534}"
2181
+ };
2182
+ function registerMemoryDigest(program2) {
2183
+ program2.command("digest").description(
2184
+ "Generate a Markdown review digest of recently added/updated memories (default: last 7 days)"
2185
+ ).option("--days <n>", "look-back window in days", "7").option("--scope <scope>", "personal | team | module | all", "team").option("--out <file>", "write digest to a file instead of stdout").option("-d, --dir <dir>", "project root").action(async (opts) => {
2186
+ const root = findProjectRoot27(opts.dir);
2187
+ const paths = resolveHaivePaths24(root);
2188
+ if (!existsSync25(paths.memoriesDir)) {
2189
+ ui.error("No .ai/memories found. Run `haive init` first.");
2190
+ process.exitCode = 1;
2191
+ return;
2192
+ }
2193
+ const days = Math.max(1, Number(opts.days ?? 7));
2194
+ const scopeFilter = opts.scope ?? "team";
2195
+ const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1e3);
2196
+ const all = await loadMemoriesFromDir5(paths.memoriesDir);
2197
+ const usage = await loadUsageIndex10(paths);
2198
+ const recent = all.filter(({ memory: mem }) => {
2199
+ const fm = mem.frontmatter;
2200
+ if (fm.type === "session_recap") return false;
2201
+ if (fm.status === "rejected" || fm.status === "deprecated") return false;
2202
+ if (scopeFilter !== "all" && fm.scope !== scopeFilter) return false;
2203
+ return new Date(fm.created_at) >= cutoff;
2204
+ });
2205
+ const now = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2206
+ const lines = [
2207
+ `# hAIve Memory Digest \u2014 ${now}`,
2208
+ ``,
2209
+ `> **Period:** last ${days} day${days > 1 ? "s" : ""} | **Scope:** ${scopeFilter} | **Total:** ${recent.length} memor${recent.length === 1 ? "y" : "ies"}`,
2210
+ ``,
2211
+ `---`,
2212
+ ``
2213
+ ];
2214
+ if (recent.length === 0) {
2215
+ lines.push(`_No new memories in the last ${days} days._`);
2216
+ } else {
2217
+ const byType = /* @__PURE__ */ new Map();
2218
+ for (const m of recent) {
2219
+ const t = m.memory.frontmatter.type;
2220
+ if (!byType.has(t)) byType.set(t, []);
2221
+ byType.get(t).push(m);
2222
+ }
2223
+ for (const [type, mems] of byType) {
2224
+ lines.push(`## ${type.charAt(0).toUpperCase() + type.slice(1)} (${mems.length})`);
2225
+ lines.push(``);
2226
+ for (const { memory: mem } of mems) {
2227
+ const fm = mem.frontmatter;
2228
+ const u = getUsage8(usage, fm.id);
2229
+ const confidence = deriveConfidence4(fm, u);
2230
+ const emoji = CONFIDENCE_EMOJI[confidence] ?? "\u2B1C";
2231
+ const anchor = fm.anchor.paths.length > 0 ? `\`${fm.anchor.paths[0]}\`` + (fm.anchor.paths.length > 1 ? ` +${fm.anchor.paths.length - 1}` : "") : "_no anchor_";
2232
+ lines.push(`### ${emoji} \`${fm.id}\``);
2233
+ lines.push(``);
2234
+ lines.push(`| Field | Value |`);
2235
+ lines.push(`|---|---|`);
2236
+ lines.push(`| **Status** | \`${fm.status}\` |`);
2237
+ lines.push(`| **Confidence** | ${confidence} |`);
2238
+ lines.push(`| **Scope** | ${fm.scope}${fm.module ? `/${fm.module}` : ""} |`);
2239
+ lines.push(`| **Tags** | ${fm.tags.length > 0 ? fm.tags.map((t) => `\`${t}\``).join(", ") : "_none_"} |`);
2240
+ lines.push(`| **Anchor** | ${anchor} |`);
2241
+ lines.push(`| **Reads** | ${u.read_count} |`);
2242
+ lines.push(`| **Created** | ${fm.created_at.slice(0, 10)} |`);
2243
+ lines.push(``);
2244
+ const bodyPreview = mem.body.split("\n").slice(0, 6).join("\n").trim();
2245
+ lines.push(bodyPreview);
2246
+ lines.push(``);
2247
+ lines.push(`**Action:** [ ] approve &nbsp;&nbsp; [ ] reject &nbsp;&nbsp; [ ] keep as-is`);
2248
+ lines.push(``);
2249
+ lines.push(`---`);
2250
+ lines.push(``);
2251
+ }
2252
+ }
2253
+ }
2254
+ lines.push(``);
2255
+ lines.push(
2256
+ `> _To take action: \`haive memory approve <id>\`, \`haive memory reject <id>\`, or open \`haive tui\` for interactive review._`
2257
+ );
2258
+ const digest = lines.join("\n");
2259
+ if (opts.out) {
2260
+ const outPath = path23.resolve(process.cwd(), opts.out);
2261
+ await writeFile12(outPath, digest, "utf8");
2262
+ ui.success(`Digest written to ${opts.out} (${recent.length} memor${recent.length === 1 ? "y" : "ies"})`);
2263
+ } else {
2264
+ console.log(digest);
2265
+ }
2266
+ });
2267
+ }
2268
+
2269
+ // src/commands/session-end.ts
2270
+ import { writeFile as writeFile13, mkdir as mkdir6 } from "fs/promises";
2271
+ import { existsSync as existsSync26 } from "fs";
2272
+ import path24 from "path";
2273
+ import "commander";
2274
+ import {
2275
+ buildFrontmatter as buildFrontmatter3,
2276
+ findProjectRoot as findProjectRoot28,
2277
+ loadMemoriesFromDir as loadMemoriesFromDir6,
2141
2278
  memoryFilePath as memoryFilePath4,
2142
- resolveHaivePaths as resolveHaivePaths24,
2279
+ resolveHaivePaths as resolveHaivePaths25,
2143
2280
  serializeMemory as serializeMemory10
2144
2281
  } from "@hiveai/core";
2145
2282
  function buildRecapBody(opts) {
@@ -2172,9 +2309,9 @@ function recapTopic(scope, module) {
2172
2309
  }
2173
2310
  function registerSessionEnd(session2) {
2174
2311
  session2.command("end").description("Save a structured end-of-session recap (goal / accomplished / discoveries / next steps)").requiredOption("--goal <text>", "What you were trying to accomplish (1\u20132 sentences)").requiredOption("--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").option("--next <text>", "What should happen next (for the next session or a teammate)").option("--scope <scope>", "personal | team | module", "personal").option("--module <name>", "module name (required when scope=module)").option("-d, --dir <dir>", "project root").action(async (opts) => {
2175
- const root = findProjectRoot27(opts.dir);
2176
- const paths = resolveHaivePaths24(root);
2177
- if (!existsSync25(paths.haiveDir)) {
2312
+ const root = findProjectRoot28(opts.dir);
2313
+ const paths = resolveHaivePaths25(root);
2314
+ if (!existsSync26(paths.haiveDir)) {
2178
2315
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
2179
2316
  process.exitCode = 1;
2180
2317
  return;
@@ -2183,13 +2320,13 @@ function registerSessionEnd(session2) {
2183
2320
  const body = buildRecapBody(opts);
2184
2321
  const topic = recapTopic(scope, opts.module);
2185
2322
  const filesTouched = parseCsv5(opts.files);
2186
- const missingPaths = filesTouched.filter((p) => !existsSync25(path23.resolve(root, p)));
2323
+ const missingPaths = filesTouched.filter((p) => !existsSync26(path24.resolve(root, p)));
2187
2324
  if (missingPaths.length > 0) {
2188
2325
  ui.warn(`Anchor path${missingPaths.length > 1 ? "s" : ""} not found in project (will be stale):`);
2189
2326
  for (const p of missingPaths) ui.warn(` \u2717 ${p}`);
2190
2327
  }
2191
- if (existsSync25(paths.memoriesDir)) {
2192
- const existing = await loadMemoriesFromDir5(paths.memoriesDir);
2328
+ if (existsSync26(paths.memoriesDir)) {
2329
+ const existing = await loadMemoriesFromDir6(paths.memoriesDir);
2193
2330
  const topicMatch = existing.find(
2194
2331
  ({ memory: memory2 }) => memory2.frontmatter.topic === topic && memory2.frontmatter.scope === scope && (!opts.module || memory2.frontmatter.module === opts.module)
2195
2332
  );
@@ -2204,9 +2341,9 @@ function registerSessionEnd(session2) {
2204
2341
  paths: filesTouched.length ? filesTouched : fm.anchor.paths
2205
2342
  }
2206
2343
  };
2207
- await writeFile12(topicMatch.filePath, serializeMemory10({ frontmatter: newFrontmatter, body }), "utf8");
2344
+ await writeFile13(topicMatch.filePath, serializeMemory10({ frontmatter: newFrontmatter, body }), "utf8");
2208
2345
  ui.success(`Session recap updated (revision #${revisionCount})`);
2209
- ui.info(`id=${fm.id} file=${path23.relative(root, topicMatch.filePath)}`);
2346
+ ui.info(`id=${fm.id} file=${path24.relative(root, topicMatch.filePath)}`);
2210
2347
  return;
2211
2348
  }
2212
2349
  }
@@ -2221,10 +2358,10 @@ function registerSessionEnd(session2) {
2221
2358
  status: "validated"
2222
2359
  });
2223
2360
  const file = memoryFilePath4(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
2224
- await mkdir6(path23.dirname(file), { recursive: true });
2225
- await writeFile12(file, serializeMemory10({ frontmatter, body }), "utf8");
2361
+ await mkdir6(path24.dirname(file), { recursive: true });
2362
+ await writeFile13(file, serializeMemory10({ frontmatter, body }), "utf8");
2226
2363
  ui.success(`Session recap created`);
2227
- ui.info(`id=${frontmatter.id} scope=${scope} file=${path23.relative(root, file)}`);
2364
+ ui.info(`id=${frontmatter.id} scope=${scope} file=${path24.relative(root, file)}`);
2228
2365
  ui.info("Next session: call `get_briefing` \u2014 the recap will be surfaced automatically.");
2229
2366
  });
2230
2367
  }
@@ -2234,8 +2371,8 @@ function parseCsv5(value) {
2234
2371
  }
2235
2372
 
2236
2373
  // src/index.ts
2237
- var program = new Command28();
2238
- program.name("haive").description("hAIve \u2014 team-first persistent memory layer for AI coding agents").version("0.2.16");
2374
+ var program = new Command29();
2375
+ program.name("haive").description("hAIve \u2014 team-first persistent memory layer for AI coding agents").version("0.3.0");
2239
2376
  registerInit(program);
2240
2377
  registerMcp(program);
2241
2378
  registerBriefing(program);
@@ -2263,6 +2400,7 @@ registerMemoryUpdate(memory);
2263
2400
  registerMemoryHot(memory);
2264
2401
  registerMemoryTried(memory);
2265
2402
  registerMemoryImport(memory);
2403
+ registerMemoryDigest(memory);
2266
2404
  var session = program.command("session").description("Manage session lifecycle");
2267
2405
  registerSessionEnd(session);
2268
2406
  program.parseAsync(process.argv).catch((err) => {