@hiveai/cli 0.9.5 → 0.9.7
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 +259 -32
- 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 Command47 } 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(([path45, changes]) => ({ path: path45, 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)));
|
|
@@ -843,7 +843,7 @@ async function scanDirs(root, maxDepth = 2) {
|
|
|
843
843
|
if (depth > maxDepth) return;
|
|
844
844
|
let entries;
|
|
845
845
|
try {
|
|
846
|
-
entries = await readdir(dir, { withFileTypes: true });
|
|
846
|
+
entries = await readdir(dir, { withFileTypes: true, encoding: "utf8" });
|
|
847
847
|
} catch {
|
|
848
848
|
return;
|
|
849
849
|
}
|
|
@@ -2297,6 +2297,8 @@ var RUNTIME_README_BODY = `# .ai/.runtime \u2014 disposable local layer
|
|
|
2297
2297
|
Not team truth. Use for machine-local session notes or tooling scratch files.
|
|
2298
2298
|
Official memories belong in .ai/memories/ (versioned in Git).
|
|
2299
2299
|
Only .gitignore and this README are meant to commit; everything else stays untracked.
|
|
2300
|
+
|
|
2301
|
+
Session continuity (local): agents may append \`session-journal.ndjson\` via MCP \`runtime_journal_append\` or \`haive runtime journal append\`.
|
|
2300
2302
|
`;
|
|
2301
2303
|
var RUNTIME_GITIGNORE_BODY = `*
|
|
2302
2304
|
!.gitignore
|
|
@@ -2795,7 +2797,11 @@ import {
|
|
|
2795
2797
|
serializeMemory as serializeMemory8
|
|
2796
2798
|
} from "@hiveai/core";
|
|
2797
2799
|
import { z as z16 } from "zod";
|
|
2798
|
-
import {
|
|
2800
|
+
import {
|
|
2801
|
+
appendUsageEvent,
|
|
2802
|
+
appendRuntimeJournalEntry,
|
|
2803
|
+
loadConfig as loadConfig2
|
|
2804
|
+
} from "@hiveai/core";
|
|
2799
2805
|
import { mkdir as mkdir52, writeFile as writeFile9, rm } from "fs/promises";
|
|
2800
2806
|
import { existsSync as existsSync16 } from "fs";
|
|
2801
2807
|
import path72 from "path";
|
|
@@ -2900,7 +2906,11 @@ import {
|
|
|
2900
2906
|
} from "@hiveai/core";
|
|
2901
2907
|
import { z as z29 } from "zod";
|
|
2902
2908
|
import { existsSync as existsSync27 } from "fs";
|
|
2903
|
-
import {
|
|
2909
|
+
import {
|
|
2910
|
+
findLexicalConflictPairs,
|
|
2911
|
+
findTopicStatusConflictPairs,
|
|
2912
|
+
loadMemoriesFromDir as loadMemoriesFromDir21
|
|
2913
|
+
} from "@hiveai/core";
|
|
2904
2914
|
import { z as z30 } from "zod";
|
|
2905
2915
|
import { resolveProjectInfo } from "@hiveai/core";
|
|
2906
2916
|
import { z as z31 } from "zod";
|
|
@@ -2909,9 +2919,13 @@ import { z as z32 } from "zod";
|
|
|
2909
2919
|
import { existsSync as existsSync28 } from "fs";
|
|
2910
2920
|
import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir222 } from "@hiveai/core";
|
|
2911
2921
|
import { z as z33 } from "zod";
|
|
2922
|
+
import { appendRuntimeJournalEntry as appendRuntimeJournalEntry2 } from "@hiveai/core";
|
|
2912
2923
|
import { z as z34 } from "zod";
|
|
2924
|
+
import { readRuntimeJournalTail } from "@hiveai/core";
|
|
2913
2925
|
import { z as z35 } from "zod";
|
|
2914
2926
|
import { z as z36 } from "zod";
|
|
2927
|
+
import { z as z37 } from "zod";
|
|
2928
|
+
import { z as z38 } from "zod";
|
|
2915
2929
|
function createContext(options = {}) {
|
|
2916
2930
|
const env = options.env ?? process.env;
|
|
2917
2931
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -3936,6 +3950,14 @@ var SessionTracker = class {
|
|
|
3936
3950
|
recapId = result.id;
|
|
3937
3951
|
} catch {
|
|
3938
3952
|
}
|
|
3953
|
+
void appendRuntimeJournalEntry(this.ctx.paths, {
|
|
3954
|
+
kind: "session_end",
|
|
3955
|
+
message: recapId ? `auto session close | ${toolSummary} | recap:${recapId}` : `auto session close | ${toolSummary}`,
|
|
3956
|
+
meta: {
|
|
3957
|
+
recap_id: recapId ?? null,
|
|
3958
|
+
total_tool_calls: totalCalls
|
|
3959
|
+
}
|
|
3960
|
+
});
|
|
3939
3961
|
const ranPostTask = this.events.some(
|
|
3940
3962
|
(e) => e.tool === "mem_session_end" && !e.summary?.startsWith("Auto-captured")
|
|
3941
3963
|
);
|
|
@@ -4600,6 +4622,7 @@ var CodeMapInputSchema = {
|
|
|
4600
4622
|
"Approximate token budget for the response. When the matching set exceeds it, files are ranked by export density (exports per LOC) and the highest-signal ones are kept first. Omit to disable budgeting (legacy behavior)."
|
|
4601
4623
|
)
|
|
4602
4624
|
};
|
|
4625
|
+
var CodeMapInputZod = z18.object(CodeMapInputSchema);
|
|
4603
4626
|
async function codeMapTool(input, ctx) {
|
|
4604
4627
|
const map = await loadCodeMap22(ctx.paths);
|
|
4605
4628
|
if (!map) {
|
|
@@ -5681,12 +5704,16 @@ var MemConflictCandidatesInputSchema = {
|
|
|
5681
5704
|
types: z30.array(z30.enum(["decision", "architecture", "convention", "gotcha"])).default(["decision", "architecture"]).describe("Memory types scanned for pairwise lexical overlap"),
|
|
5682
5705
|
min_jaccard: z30.number().min(0).max(1).default(0.45).describe("Minimum Jaccard token similarity to surface as a candidate pair"),
|
|
5683
5706
|
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.")
|
|
5707
|
+
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."),
|
|
5708
|
+
max_topic_pairs: z30.number().int().positive().max(100).default(20).describe(
|
|
5709
|
+
"Cap for extra signal: memories sharing the same topic with validated vs rejected status."
|
|
5710
|
+
)
|
|
5685
5711
|
};
|
|
5686
5712
|
async function memConflictCandidates(input, ctx) {
|
|
5687
5713
|
if (!existsSync27(ctx.paths.memoriesDir)) {
|
|
5688
5714
|
return {
|
|
5689
5715
|
pairs: [],
|
|
5716
|
+
topic_status_pairs: [],
|
|
5690
5717
|
scanned: 0,
|
|
5691
5718
|
truncated: false,
|
|
5692
5719
|
notice: "No .ai/memories directory."
|
|
@@ -5700,8 +5727,9 @@ async function memConflictCandidates(input, ctx) {
|
|
|
5700
5727
|
maxPairs: input.max_pairs,
|
|
5701
5728
|
maxScan: input.max_scan
|
|
5702
5729
|
});
|
|
5703
|
-
const
|
|
5704
|
-
|
|
5730
|
+
const topicStatusPairs = findTopicStatusConflictPairs(all, input.max_topic_pairs);
|
|
5731
|
+
const notice = pairs.length === 0 && topicStatusPairs.length === 0 ? "No lexical or topic-status candidates \u2014 widen since_days/types or lower min_jaccard." : void 0;
|
|
5732
|
+
return { pairs, topic_status_pairs: topicStatusPairs, scanned, truncated, notice };
|
|
5705
5733
|
}
|
|
5706
5734
|
var MemResolveProjectInputSchema = {
|
|
5707
5735
|
cwd: z31.string().optional().describe("Directory used for root discovery when HAIVE_PROJECT_ROOT is unset.")
|
|
@@ -5741,11 +5769,37 @@ async function memTimeline(input, ctx) {
|
|
|
5741
5769
|
});
|
|
5742
5770
|
return { entries, total: entries.length, notice };
|
|
5743
5771
|
}
|
|
5772
|
+
var RuntimeJournalAppendInputSchema = {
|
|
5773
|
+
message: z34.string().min(1).describe("Short line to append to the runtime session journal"),
|
|
5774
|
+
kind: z34.enum(["note", "session_end", "mcp"]).default("note"),
|
|
5775
|
+
tool: z34.string().optional().describe("When kind=mcp, which tool name (optional)")
|
|
5776
|
+
};
|
|
5777
|
+
async function runtimeJournalAppend(input, ctx) {
|
|
5778
|
+
await appendRuntimeJournalEntry2(ctx.paths, {
|
|
5779
|
+
kind: input.kind,
|
|
5780
|
+
message: input.message,
|
|
5781
|
+
...input.tool ? { tool: input.tool } : {}
|
|
5782
|
+
});
|
|
5783
|
+
return {
|
|
5784
|
+
ok: true,
|
|
5785
|
+
path_hint: `${ctx.paths.runtimeDir}/session-journal.ndjson`
|
|
5786
|
+
};
|
|
5787
|
+
}
|
|
5788
|
+
var RuntimeJournalTailInputSchema = {
|
|
5789
|
+
limit: z35.number().int().positive().max(500).default(30).describe("Last N journal entries to return")
|
|
5790
|
+
};
|
|
5791
|
+
async function runtimeJournalTail(input, ctx) {
|
|
5792
|
+
const entries = await readRuntimeJournalTail(ctx.paths, input.limit);
|
|
5793
|
+
if (entries.length === 0) {
|
|
5794
|
+
return { entries: [], empty: true };
|
|
5795
|
+
}
|
|
5796
|
+
return { entries };
|
|
5797
|
+
}
|
|
5744
5798
|
var BootstrapProjectArgsSchema = {
|
|
5745
|
-
module:
|
|
5799
|
+
module: z36.string().optional().describe(
|
|
5746
5800
|
"Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
|
|
5747
5801
|
),
|
|
5748
|
-
focus:
|
|
5802
|
+
focus: z36.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
|
|
5749
5803
|
};
|
|
5750
5804
|
var ROOT_TEMPLATE = `# Project context
|
|
5751
5805
|
|
|
@@ -5826,8 +5880,8 @@ ${template}\`\`\`
|
|
|
5826
5880
|
};
|
|
5827
5881
|
}
|
|
5828
5882
|
var PostTaskArgsSchema = {
|
|
5829
|
-
task_summary:
|
|
5830
|
-
files_touched:
|
|
5883
|
+
task_summary: z37.string().optional().describe("One sentence describing what you just did"),
|
|
5884
|
+
files_touched: z37.array(z37.string()).optional().describe("Files you created or modified during the task")
|
|
5831
5885
|
};
|
|
5832
5886
|
function postTaskPrompt(args, ctx) {
|
|
5833
5887
|
const taskLine = args.task_summary ? `
|
|
@@ -5910,10 +5964,10 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]. Sessi
|
|
|
5910
5964
|
};
|
|
5911
5965
|
}
|
|
5912
5966
|
var ImportDocsArgsSchema = {
|
|
5913
|
-
content:
|
|
5914
|
-
source:
|
|
5915
|
-
scope:
|
|
5916
|
-
dry_run:
|
|
5967
|
+
content: z38.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
|
|
5968
|
+
source: z38.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
|
|
5969
|
+
scope: z38.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
|
|
5970
|
+
dry_run: z38.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
|
|
5917
5971
|
};
|
|
5918
5972
|
function importDocsPrompt(args, ctx) {
|
|
5919
5973
|
const sourceLine = args.source ? `
|
|
@@ -5976,7 +6030,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
5976
6030
|
};
|
|
5977
6031
|
}
|
|
5978
6032
|
var SERVER_NAME = "haive";
|
|
5979
|
-
var SERVER_VERSION = "0.9.
|
|
6033
|
+
var SERVER_VERSION = "0.9.7";
|
|
5980
6034
|
function jsonResult(data) {
|
|
5981
6035
|
return {
|
|
5982
6036
|
content: [
|
|
@@ -6659,14 +6713,17 @@ function createHaiveServer(options = {}) {
|
|
|
6659
6713
|
server.tool(
|
|
6660
6714
|
"mem_conflict_candidates",
|
|
6661
6715
|
[
|
|
6662
|
-
"Bulk
|
|
6716
|
+
"Bulk scan for conflict CANDIDATES (not proof):",
|
|
6717
|
+
"",
|
|
6718
|
+
" 1. Lexical similarity (Jaccard) on decision/architecture-like pairs",
|
|
6719
|
+
" 2. Same frontmatter.topic with validated vs rejected \u2014 quick human-review signal",
|
|
6663
6720
|
"",
|
|
6664
|
-
"Advisory only \u2014 follow with mem_conflicts_with on specific ids
|
|
6721
|
+
"Advisory only \u2014 follow with mem_conflicts_with on specific ids.",
|
|
6665
6722
|
"",
|
|
6666
6723
|
"PARAMETERS:",
|
|
6667
|
-
" since_days, types, min_jaccard, max_pairs, max_scan",
|
|
6724
|
+
" since_days, types, min_jaccard, max_pairs, max_scan, max_topic_pairs",
|
|
6668
6725
|
"",
|
|
6669
|
-
"RETURNS: { pairs
|
|
6726
|
+
"RETURNS: { pairs, topic_status_pairs, scanned, truncated, notice? }"
|
|
6670
6727
|
].join("\n"),
|
|
6671
6728
|
MemConflictCandidatesInputSchema,
|
|
6672
6729
|
async (input) => {
|
|
@@ -6674,6 +6731,32 @@ function createHaiveServer(options = {}) {
|
|
|
6674
6731
|
return jsonResult(await memConflictCandidates(input, context));
|
|
6675
6732
|
}
|
|
6676
6733
|
);
|
|
6734
|
+
server.tool(
|
|
6735
|
+
"runtime_journal_append",
|
|
6736
|
+
[
|
|
6737
|
+
"Append one line to `.ai/.runtime/session-journal.ndjson` \u2014 machine-local session continuity.",
|
|
6738
|
+
"",
|
|
6739
|
+
"Does NOT replace team memories; complements mem_session_end recaps for local traces.",
|
|
6740
|
+
"",
|
|
6741
|
+
"PARAMETERS: message, kind (note|session_end|mcp), optional tool",
|
|
6742
|
+
"",
|
|
6743
|
+
"RETURNS: { ok, path_hint }"
|
|
6744
|
+
].join("\n"),
|
|
6745
|
+
RuntimeJournalAppendInputSchema,
|
|
6746
|
+
async (input) => jsonResult(await runtimeJournalAppend(input, context))
|
|
6747
|
+
);
|
|
6748
|
+
server.tool(
|
|
6749
|
+
"runtime_journal_tail",
|
|
6750
|
+
[
|
|
6751
|
+
"Read the last N entries from the runtime session journal (parsed JSON lines).",
|
|
6752
|
+
"",
|
|
6753
|
+
"PARAMETERS: limit (default 30, max 500)",
|
|
6754
|
+
"",
|
|
6755
|
+
"RETURNS: { entries: [...], empty?: true }"
|
|
6756
|
+
].join("\n"),
|
|
6757
|
+
RuntimeJournalTailInputSchema,
|
|
6758
|
+
async (input) => jsonResult(await runtimeJournalTail(input, context))
|
|
6759
|
+
);
|
|
6677
6760
|
server.tool(
|
|
6678
6761
|
"pre_commit_check",
|
|
6679
6762
|
[
|
|
@@ -7205,9 +7288,10 @@ Attends une **confirmation explicite** avant d'agir.
|
|
|
7205
7288
|
}
|
|
7206
7289
|
if (opts.embed) {
|
|
7207
7290
|
try {
|
|
7208
|
-
const
|
|
7291
|
+
const { Embedder, rebuildIndex } = await import("@hiveai/embeddings");
|
|
7209
7292
|
log(ui.dim("embed: rebuilding index\u2026"));
|
|
7210
|
-
const
|
|
7293
|
+
const embedder = await Embedder.create();
|
|
7294
|
+
const { report } = await rebuildIndex(paths, embedder);
|
|
7211
7295
|
log(ui.dim(`embed: index rebuilt (${report.added} added, ${report.updated} updated, ${report.removed} removed)`));
|
|
7212
7296
|
} catch {
|
|
7213
7297
|
ui.warn("--embed: @hiveai/embeddings not available or index build failed. Run `haive embeddings index` manually.");
|
|
@@ -8636,8 +8720,8 @@ function parseChangelog(content) {
|
|
|
8636
8720
|
const sections = content.split(/^#{1,3}\s+/m).slice(1);
|
|
8637
8721
|
for (const section of sections) {
|
|
8638
8722
|
const versionMatch = section.match(/^(?:\[?)([0-9]+\.[0-9]+[.0-9]*)/);
|
|
8639
|
-
|
|
8640
|
-
|
|
8723
|
+
const version = versionMatch?.[1];
|
|
8724
|
+
if (!version) continue;
|
|
8641
8725
|
const entry = {
|
|
8642
8726
|
version,
|
|
8643
8727
|
breaking: [],
|
|
@@ -8648,7 +8732,7 @@ function parseChangelog(content) {
|
|
|
8648
8732
|
};
|
|
8649
8733
|
const subSections = section.split(/^#{2,4}\s+/m);
|
|
8650
8734
|
for (const sub of subSections) {
|
|
8651
|
-
const firstLine = sub.split("\n")[0].toLowerCase().trim();
|
|
8735
|
+
const firstLine = (sub.split("\n")[0] ?? "").toLowerCase().trim();
|
|
8652
8736
|
const items = sub.split("\n").slice(1).filter((l) => l.trim().startsWith("-") || l.trim().startsWith("*")).map((l) => l.replace(/^[\s\-*]+/, "").trim()).filter(Boolean);
|
|
8653
8737
|
if (/breaking/.test(firstLine)) {
|
|
8654
8738
|
entry.breaking.push(...items);
|
|
@@ -8664,8 +8748,9 @@ function parseChangelog(content) {
|
|
|
8664
8748
|
for (const sub2 of subSections) {
|
|
8665
8749
|
for (const line of sub2.split("\n")) {
|
|
8666
8750
|
const breakingMatch = line.match(/BREAKING CHANGE[S]?:\s*(.+)/i);
|
|
8667
|
-
|
|
8668
|
-
|
|
8751
|
+
const breakingText = breakingMatch?.[1]?.trim();
|
|
8752
|
+
if (breakingText && !entry.breaking.includes(breakingText)) {
|
|
8753
|
+
entry.breaking.push(breakingText);
|
|
8669
8754
|
}
|
|
8670
8755
|
}
|
|
8671
8756
|
}
|
|
@@ -8707,7 +8792,8 @@ function registerMemoryImportChangelog(memory2) {
|
|
|
8707
8792
|
}
|
|
8708
8793
|
if (opts.versions) {
|
|
8709
8794
|
if (opts.versions === "latest") {
|
|
8710
|
-
|
|
8795
|
+
const latest = entries[0];
|
|
8796
|
+
entries = latest ? [latest] : [];
|
|
8711
8797
|
} else {
|
|
8712
8798
|
const requested = opts.versions.split(",").map((v) => v.trim());
|
|
8713
8799
|
entries = entries.filter((e) => requested.includes(e.version));
|
|
@@ -10284,7 +10370,7 @@ function registerDoctor(program2) {
|
|
|
10284
10370
|
timeout: 3e3,
|
|
10285
10371
|
stdio: ["ignore", "pipe", "ignore"]
|
|
10286
10372
|
}).trim();
|
|
10287
|
-
const cliVersion = "0.9.
|
|
10373
|
+
const cliVersion = "0.9.7";
|
|
10288
10374
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
10289
10375
|
findings.push({
|
|
10290
10376
|
severity: "warn",
|
|
@@ -10748,12 +10834,151 @@ function registerResolveProject(program2) {
|
|
|
10748
10834
|
});
|
|
10749
10835
|
}
|
|
10750
10836
|
|
|
10837
|
+
// src/commands/runtime-journal.ts
|
|
10838
|
+
import { existsSync as existsSync64 } from "fs";
|
|
10839
|
+
import path41 from "path";
|
|
10840
|
+
import "commander";
|
|
10841
|
+
import {
|
|
10842
|
+
appendRuntimeJournalEntry as appendRuntimeJournalEntry3,
|
|
10843
|
+
findProjectRoot as findProjectRoot43,
|
|
10844
|
+
readRuntimeJournalTail as readRuntimeJournalTail2,
|
|
10845
|
+
resolveHaivePaths as resolveHaivePaths40
|
|
10846
|
+
} from "@hiveai/core";
|
|
10847
|
+
function registerRuntime(program2) {
|
|
10848
|
+
const runtime = program2.command("runtime").description(
|
|
10849
|
+
"Local-only .ai/.runtime helpers (not versioned team memory). See session-journal.ndjson."
|
|
10850
|
+
);
|
|
10851
|
+
const journal = runtime.command("journal").description("Append or read the machine-local session journal (NDJSON)");
|
|
10852
|
+
journal.command("append").description("Append one JSON line to .ai/.runtime/session-journal.ndjson").argument("<message>", "short text to log").option("-k, --kind <kind>", "note | session_end | mcp", "note").option("-d, --dir <dir>", "project root", process.cwd()).action(async (message, opts) => {
|
|
10853
|
+
const root = path41.resolve(opts.dir ?? process.cwd());
|
|
10854
|
+
const paths = resolveHaivePaths40(findProjectRoot43(root));
|
|
10855
|
+
const raw = opts.kind ?? "note";
|
|
10856
|
+
const kind = ["note", "session_end", "mcp"].includes(raw) ? raw : "note";
|
|
10857
|
+
await appendRuntimeJournalEntry3(paths, { kind, message });
|
|
10858
|
+
ui.success(`Appended to ${path41.relative(root, paths.runtimeDir)}/session-journal.ndjson`);
|
|
10859
|
+
});
|
|
10860
|
+
journal.command("tail").description("Print the last N entries from the runtime session journal as JSON").option("-n, --limit <n>", "number of lines", "30").option("-d, --dir <dir>", "project root", process.cwd()).action(async (opts) => {
|
|
10861
|
+
const root = path41.resolve(opts.dir ?? process.cwd());
|
|
10862
|
+
const paths = resolveHaivePaths40(findProjectRoot43(root));
|
|
10863
|
+
const limit = Math.min(500, Math.max(1, parseInt(opts.limit, 10) || 30));
|
|
10864
|
+
if (!existsSync64(paths.haiveDir)) {
|
|
10865
|
+
ui.error("No .ai/ \u2014 run `haive init` first.");
|
|
10866
|
+
process.exitCode = 1;
|
|
10867
|
+
return;
|
|
10868
|
+
}
|
|
10869
|
+
const entries = await readRuntimeJournalTail2(paths, limit);
|
|
10870
|
+
if (entries.length === 0) {
|
|
10871
|
+
ui.info("Journal empty or missing.");
|
|
10872
|
+
return;
|
|
10873
|
+
}
|
|
10874
|
+
console.log(JSON.stringify({ entries, count: entries.length }, null, 2));
|
|
10875
|
+
});
|
|
10876
|
+
}
|
|
10877
|
+
|
|
10878
|
+
// src/commands/memory-timeline.ts
|
|
10879
|
+
import { existsSync as existsSync65 } from "fs";
|
|
10880
|
+
import path43 from "path";
|
|
10881
|
+
import "commander";
|
|
10882
|
+
import {
|
|
10883
|
+
collectTimelineEntries as collectTimelineEntries2,
|
|
10884
|
+
findProjectRoot as findProjectRoot44,
|
|
10885
|
+
resolveHaivePaths as resolveHaivePaths41
|
|
10886
|
+
} from "@hiveai/core";
|
|
10887
|
+
function registerMemoryTimeline(memory2) {
|
|
10888
|
+
memory2.command("timeline").description(
|
|
10889
|
+
"List related memories chronologically (topic, related_ids, anchors) \u2014 same logic as MCP mem_timeline."
|
|
10890
|
+
).option("--id <id>", "seed memory id").option("--topic <key>", "filter by frontmatter.topic (use without --id for topic-only)").option("-n, --limit <n>", "max entries", "30").option("-d, --dir <dir>", "project root", process.cwd()).action(async (opts) => {
|
|
10891
|
+
if (!opts.id && !opts.topic) {
|
|
10892
|
+
ui.error("Provide --id and/or --topic.");
|
|
10893
|
+
process.exitCode = 1;
|
|
10894
|
+
return;
|
|
10895
|
+
}
|
|
10896
|
+
const root = path43.resolve(opts.dir ?? process.cwd());
|
|
10897
|
+
const paths = resolveHaivePaths41(findProjectRoot44(root));
|
|
10898
|
+
if (!existsSync65(paths.memoriesDir)) {
|
|
10899
|
+
ui.error("No memories \u2014 run `haive init`.");
|
|
10900
|
+
process.exitCode = 1;
|
|
10901
|
+
return;
|
|
10902
|
+
}
|
|
10903
|
+
const limit = Math.min(100, Math.max(1, parseInt(opts.limit, 10) || 30));
|
|
10904
|
+
const all = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
10905
|
+
const { entries, notice } = collectTimelineEntries2(all, {
|
|
10906
|
+
memoryId: opts.id,
|
|
10907
|
+
topic: opts.topic,
|
|
10908
|
+
limit
|
|
10909
|
+
});
|
|
10910
|
+
if (notice) ui.warn(notice);
|
|
10911
|
+
console.log(JSON.stringify({ entries, total: entries.length }, null, 2));
|
|
10912
|
+
});
|
|
10913
|
+
}
|
|
10914
|
+
|
|
10915
|
+
// src/commands/memory-conflict-candidates.ts
|
|
10916
|
+
import { existsSync as existsSync66 } from "fs";
|
|
10917
|
+
import path44 from "path";
|
|
10918
|
+
import "commander";
|
|
10919
|
+
import {
|
|
10920
|
+
findLexicalConflictPairs as findLexicalConflictPairs2,
|
|
10921
|
+
findTopicStatusConflictPairs as findTopicStatusConflictPairs2,
|
|
10922
|
+
findProjectRoot as findProjectRoot45,
|
|
10923
|
+
resolveHaivePaths as resolveHaivePaths42
|
|
10924
|
+
} from "@hiveai/core";
|
|
10925
|
+
function parseTypes(csv) {
|
|
10926
|
+
const allowed = ["decision", "architecture", "convention", "gotcha"];
|
|
10927
|
+
const parts = csv.split(",").map((s) => s.trim().toLowerCase());
|
|
10928
|
+
const out = parts.filter((p) => allowed.includes(p));
|
|
10929
|
+
return out.length ? out : ["decision", "architecture"];
|
|
10930
|
+
}
|
|
10931
|
+
function registerMemoryConflictCandidates(memory2) {
|
|
10932
|
+
memory2.command("conflict-candidates").description(
|
|
10933
|
+
"Heuristic conflict candidates (lexical Jaccard + same-topic validated/rejected pairs) \u2014 aligns with MCP mem_conflict_candidates."
|
|
10934
|
+
).option("-d, --dir <dir>", "project root", process.cwd()).option("--since-days <n>", "only memories created within N days (lexical scan)", "365").option(
|
|
10935
|
+
"--types <csv>",
|
|
10936
|
+
"decision,architecture,convention,gotcha (lexical scan)",
|
|
10937
|
+
"decision,architecture"
|
|
10938
|
+
).option("--min-jaccard <x>", "minimum Jaccard for lexical pairs", "0.45").option("--max-pairs <n>", "cap lexical pairs", "20").option("--max-scan <n>", "max memories scanned (lexical)", "500").option("--max-topic-pairs <n>", "cap topic/status pairs", "20").action(async (opts) => {
|
|
10939
|
+
const root = path44.resolve(opts.dir ?? process.cwd());
|
|
10940
|
+
const paths = resolveHaivePaths42(findProjectRoot45(root));
|
|
10941
|
+
if (!existsSync66(paths.memoriesDir)) {
|
|
10942
|
+
ui.error("No memories \u2014 run `haive init`.");
|
|
10943
|
+
process.exitCode = 1;
|
|
10944
|
+
return;
|
|
10945
|
+
}
|
|
10946
|
+
const sinceDays = Math.max(1, parseInt(opts.sinceDays, 10) || 365);
|
|
10947
|
+
const minJaccard = parseFloat(opts.minJaccard) || 0.45;
|
|
10948
|
+
const maxPairs = Math.min(100, Math.max(1, parseInt(opts.maxPairs, 10) || 20));
|
|
10949
|
+
const maxScan = Math.min(2e3, Math.max(1, parseInt(opts.maxScan, 10) || 500));
|
|
10950
|
+
const maxTopicPairs = Math.min(100, Math.max(1, parseInt(opts.maxTopicPairs, 10) || 20));
|
|
10951
|
+
const all = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
10952
|
+
const lexical = findLexicalConflictPairs2(all, {
|
|
10953
|
+
sinceDays,
|
|
10954
|
+
types: parseTypes(opts.types),
|
|
10955
|
+
minJaccard,
|
|
10956
|
+
maxPairs,
|
|
10957
|
+
maxScan
|
|
10958
|
+
});
|
|
10959
|
+
const topicStatusPairs = findTopicStatusConflictPairs2(all, maxTopicPairs);
|
|
10960
|
+
console.log(
|
|
10961
|
+
JSON.stringify(
|
|
10962
|
+
{
|
|
10963
|
+
pairs: lexical.pairs,
|
|
10964
|
+
topic_status_pairs: topicStatusPairs,
|
|
10965
|
+
scanned: lexical.scanned,
|
|
10966
|
+
truncated: lexical.truncated
|
|
10967
|
+
},
|
|
10968
|
+
null,
|
|
10969
|
+
2
|
|
10970
|
+
)
|
|
10971
|
+
);
|
|
10972
|
+
});
|
|
10973
|
+
}
|
|
10974
|
+
|
|
10751
10975
|
// src/index.ts
|
|
10752
|
-
var program = new
|
|
10753
|
-
program.name("haive").description("hAIve \u2014 team-first persistent memory layer for AI coding agents").version("0.9.
|
|
10976
|
+
var program = new Command47();
|
|
10977
|
+
program.name("haive").description("hAIve \u2014 team-first persistent memory layer for AI coding agents").version("0.9.7");
|
|
10754
10978
|
registerInit(program);
|
|
10755
10979
|
registerWelcome(program);
|
|
10756
10980
|
registerResolveProject(program);
|
|
10981
|
+
registerRuntime(program);
|
|
10757
10982
|
registerMcp(program);
|
|
10758
10983
|
registerBriefing(program);
|
|
10759
10984
|
registerTui(program);
|
|
@@ -10785,6 +11010,8 @@ registerMemoryImportChangelog(memory);
|
|
|
10785
11010
|
registerMemoryDigest(memory);
|
|
10786
11011
|
registerMemorySuggest(memory);
|
|
10787
11012
|
registerMemorySuggestTopic(memory);
|
|
11013
|
+
registerMemoryTimeline(memory);
|
|
11014
|
+
registerMemoryConflictCandidates(memory);
|
|
10788
11015
|
registerMemoryArchive(memory);
|
|
10789
11016
|
registerMemoryLint(memory);
|
|
10790
11017
|
var session = program.command("session").description("Manage session lifecycle");
|