@hivelore/cli 0.31.0 → 0.34.1
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/chunk-532TRMXJ.js +13 -0
- package/dist/chunk-532TRMXJ.js.map +1 -0
- package/dist/chunk-DGZDP34Z.js +83 -0
- package/dist/chunk-DGZDP34Z.js.map +1 -0
- package/dist/chunk-GGDCU7PH.js +45 -0
- package/dist/chunk-GGDCU7PH.js.map +1 -0
- package/dist/chunk-HIWRB454.js +108 -0
- package/dist/chunk-HIWRB454.js.map +1 -0
- package/dist/chunk-OYJKHD22.js +85 -0
- package/dist/chunk-OYJKHD22.js.map +1 -0
- package/dist/chunk-X6UHROFL.js +4996 -0
- package/dist/chunk-X6UHROFL.js.map +1 -0
- package/dist/chunk-X6Y2B3TJ.js +173 -0
- package/dist/chunk-X6Y2B3TJ.js.map +1 -0
- package/dist/index.js +1962 -8903
- package/dist/index.js.map +1 -1
- package/dist/memory-import-changelog-ZQW6QJVE.js +11 -0
- package/dist/memory-import-changelog-ZQW6QJVE.js.map +1 -0
- package/dist/memory-pending-GKZE3IYA.js +12 -0
- package/dist/memory-pending-GKZE3IYA.js.map +1 -0
- package/dist/memory-resolve-conflict-I7NEQ4VA.js +12 -0
- package/dist/memory-resolve-conflict-I7NEQ4VA.js.map +1 -0
- package/dist/memory-seed-git-U4DR2SOX.js +11 -0
- package/dist/memory-seed-git-U4DR2SOX.js.map +1 -0
- package/dist/server-FQ5C43MV.js +58 -0
- package/dist/server-FQ5C43MV.js.map +1 -0
- package/package.json +4 -4
- package/dist/Dashboard-3WMW72XE.js +0 -361
- package/dist/Dashboard-3WMW72XE.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/fs.ts"],"sourcesContent":["export {\n loadMemoriesFromDir,\n loadMemory,\n listMarkdownFilesRecursive,\n type LoadedMemory,\n} from \"@hivelore/core\";\n"],"mappings":";;;AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;","names":[]}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
loadMemoriesFromDir
|
|
4
|
+
} from "./chunk-532TRMXJ.js";
|
|
5
|
+
import {
|
|
6
|
+
ui
|
|
7
|
+
} from "./chunk-GGDCU7PH.js";
|
|
8
|
+
|
|
9
|
+
// src/commands/memory-resolve-conflict.ts
|
|
10
|
+
import { writeFile } from "fs/promises";
|
|
11
|
+
import { existsSync } from "fs";
|
|
12
|
+
import "commander";
|
|
13
|
+
import {
|
|
14
|
+
applyConflictResolution,
|
|
15
|
+
findProjectRoot,
|
|
16
|
+
planConflictResolution,
|
|
17
|
+
resolveHaivePaths,
|
|
18
|
+
serializeMemory
|
|
19
|
+
} from "@hivelore/core";
|
|
20
|
+
function registerMemoryResolveConflict(memory) {
|
|
21
|
+
memory.command("resolve-conflict <id_a> <id_b>", { hidden: true }).description("Resolve a contradiction: keep the stronger memory, deprecate (supersede) the other").option("--yes", "apply the resolution (without this, only previews it)", false).option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (idA, idB, opts) => runResolveConflict(idA, idB, opts));
|
|
22
|
+
}
|
|
23
|
+
async function runResolveConflict(idA, idB, opts) {
|
|
24
|
+
const root = findProjectRoot(opts.dir);
|
|
25
|
+
const paths = resolveHaivePaths(root);
|
|
26
|
+
if (!existsSync(paths.memoriesDir)) {
|
|
27
|
+
ui.error(`No .ai/memories at ${root}.`);
|
|
28
|
+
process.exitCode = 1;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const memories = await loadMemoriesFromDir(paths.memoriesDir);
|
|
32
|
+
const a = memories.find((m) => m.memory.frontmatter.id === idA);
|
|
33
|
+
const b = memories.find((m) => m.memory.frontmatter.id === idB);
|
|
34
|
+
if (!a || !b) {
|
|
35
|
+
ui.error(`Memory not found: ${!a ? idA : ""} ${!b ? idB : ""}`.trim());
|
|
36
|
+
process.exitCode = 1;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const plan = planConflictResolution(a, b);
|
|
40
|
+
const winner = plan.keep_id === idA ? a : b;
|
|
41
|
+
const loser = plan.supersede_id === idA ? a : b;
|
|
42
|
+
const applied = applyConflictResolution(winner, loser, plan);
|
|
43
|
+
if (opts.json) {
|
|
44
|
+
console.log(JSON.stringify({
|
|
45
|
+
...plan,
|
|
46
|
+
winner_revision_count: applied.winner.revision_count,
|
|
47
|
+
topic: applied.topic,
|
|
48
|
+
topic_adopted: applied.topic_adopted,
|
|
49
|
+
applied: Boolean(opts.yes)
|
|
50
|
+
}, null, 2));
|
|
51
|
+
} else {
|
|
52
|
+
console.log(ui.bold("Conflict resolution"));
|
|
53
|
+
console.log(` keep: ${ui.green(plan.keep_id)} ${ui.dim(`(rev ${winner.memory.frontmatter.revision_count}\u2192${applied.winner.revision_count})`)}`);
|
|
54
|
+
console.log(` supersede: ${ui.red(plan.supersede_id)} ${ui.dim(`\u2192 deprecated`)}`);
|
|
55
|
+
console.log(` reason: ${plan.reason}`);
|
|
56
|
+
if (applied.topic) {
|
|
57
|
+
console.log(` topic: ${applied.topic}${applied.topic_adopted ? ui.dim(" (adopted from superseded \u2014 future captures upsert into the winner)") : ""}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (!opts.yes) {
|
|
61
|
+
if (!opts.json) ui.info("Preview only \u2014 re-run with --yes to apply.");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
await writeFile(
|
|
65
|
+
winner.filePath,
|
|
66
|
+
serializeMemory({ frontmatter: applied.winner, body: winner.memory.body }),
|
|
67
|
+
"utf8"
|
|
68
|
+
);
|
|
69
|
+
await writeFile(
|
|
70
|
+
loser.filePath,
|
|
71
|
+
serializeMemory({ frontmatter: applied.loser, body: loser.memory.body }),
|
|
72
|
+
"utf8"
|
|
73
|
+
);
|
|
74
|
+
if (!opts.json) {
|
|
75
|
+
ui.success(`Deprecated ${plan.supersede_id}; promoted ${plan.keep_id} (rev ${applied.winner.revision_count}${applied.topic ? `, topic=${applied.topic}` : ""}).`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export {
|
|
80
|
+
registerMemoryResolveConflict,
|
|
81
|
+
runResolveConflict
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=chunk-DGZDP34Z.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/memory-resolve-conflict.ts"],"sourcesContent":["/**\n * `hivelore memory resolve-conflict <id_a> <id_b>` — turn a detected contradiction into a resolution.\n *\n * `hivelore memory conflict-candidates` finds pairs that contradict each other; this APPLIES the fix:\n * it deprecates the losing memory (by the deterministic order in `planConflictResolution`) and\n * stamps it with a stale_reason pointing at the winner. Keeps the corpus coherent as it grows —\n * Fowler's \"incoherence at scale\" challenge.\n */\nimport { writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { Command } from \"commander\";\nimport {\n applyConflictResolution,\n findProjectRoot,\n planConflictResolution,\n resolveHaivePaths,\n serializeMemory,\n} from \"@hivelore/core\";\nimport { loadMemoriesFromDir } from \"../utils/fs.js\";\nimport { ui } from \"../utils/ui.js\";\n\nexport interface ResolveConflictOptions {\n yes?: boolean;\n json?: boolean;\n dir?: string;\n}\n\nexport function registerMemoryResolveConflict(memory: Command): void {\n memory\n .command(\"resolve-conflict <id_a> <id_b>\", { hidden: true })\n .description(\"Resolve a contradiction: keep the stronger memory, deprecate (supersede) the other\")\n .option(\"--yes\", \"apply the resolution (without this, only previews it)\", false)\n .option(\"--json\", \"emit JSON\", false)\n .option(\"-d, --dir <dir>\", \"project root\")\n .action(async (idA: string, idB: string, opts: ResolveConflictOptions) => runResolveConflict(idA, idB, opts));\n}\n\nexport async function runResolveConflict(idA: string, idB: string, opts: ResolveConflictOptions): Promise<void> {\n const root = findProjectRoot(opts.dir);\n const paths = resolveHaivePaths(root);\n if (!existsSync(paths.memoriesDir)) {\n ui.error(`No .ai/memories at ${root}.`);\n process.exitCode = 1;\n return;\n }\n\n const memories = await loadMemoriesFromDir(paths.memoriesDir);\n const a = memories.find((m) => m.memory.frontmatter.id === idA);\n const b = memories.find((m) => m.memory.frontmatter.id === idB);\n if (!a || !b) {\n ui.error(`Memory not found: ${!a ? idA : \"\"} ${!b ? idB : \"\"}`.trim());\n process.exitCode = 1;\n return;\n }\n\n const plan = planConflictResolution(a, b);\n const winner = plan.keep_id === idA ? a : b;\n const loser = plan.supersede_id === idA ? a : b;\n const applied = applyConflictResolution(winner, loser, plan);\n\n if (opts.json) {\n console.log(JSON.stringify({\n ...plan,\n winner_revision_count: applied.winner.revision_count,\n topic: applied.topic,\n topic_adopted: applied.topic_adopted,\n applied: Boolean(opts.yes),\n }, null, 2));\n } else {\n console.log(ui.bold(\"Conflict resolution\"));\n console.log(` keep: ${ui.green(plan.keep_id)} ${ui.dim(`(rev ${winner.memory.frontmatter.revision_count}→${applied.winner.revision_count})`)}`);\n console.log(` supersede: ${ui.red(plan.supersede_id)} ${ui.dim(`→ deprecated`)}`);\n console.log(` reason: ${plan.reason}`);\n if (applied.topic) {\n console.log(` topic: ${applied.topic}${applied.topic_adopted ? ui.dim(\" (adopted from superseded — future captures upsert into the winner)\") : \"\"}`);\n }\n }\n\n if (!opts.yes) {\n if (!opts.json) ui.info(\"Preview only — re-run with --yes to apply.\");\n return;\n }\n\n // Persist BOTH: promote the winner (revision/topic) and deprecate the loser, so the corpus\n // converges on a single authoritative memory per subject instead of leaving a silent contradiction.\n await writeFile(\n winner.filePath,\n serializeMemory({ frontmatter: applied.winner, body: winner.memory.body }),\n \"utf8\",\n );\n await writeFile(\n loser.filePath,\n serializeMemory({ frontmatter: applied.loser, body: loser.memory.body }),\n \"utf8\",\n );\n if (!opts.json) {\n ui.success(`Deprecated ${plan.supersede_id}; promoted ${plan.keep_id} (rev ${applied.winner.revision_count}${applied.topic ? `, topic=${applied.topic}` : \"\"}).`);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/utils/ui.ts
|
|
4
|
+
import pc from "picocolors";
|
|
5
|
+
var jsonMode = false;
|
|
6
|
+
function setUiJsonMode(on) {
|
|
7
|
+
jsonMode = on;
|
|
8
|
+
}
|
|
9
|
+
var logHuman = (icon, msg) => {
|
|
10
|
+
if (jsonMode) console.error(icon, msg);
|
|
11
|
+
else console.log(icon, msg);
|
|
12
|
+
};
|
|
13
|
+
var ui = {
|
|
14
|
+
info: (msg) => logHuman(pc.cyan("\u2139"), msg),
|
|
15
|
+
success: (msg) => logHuman(pc.green("\u2713"), msg),
|
|
16
|
+
warn: (msg) => logHuman(pc.yellow("\u26A0"), msg),
|
|
17
|
+
error: (msg) => console.error(pc.red("\u2717"), msg),
|
|
18
|
+
dim: (msg) => pc.dim(msg),
|
|
19
|
+
bold: (msg) => pc.bold(msg),
|
|
20
|
+
green: (msg) => pc.green(msg),
|
|
21
|
+
yellow: (msg) => pc.yellow(msg),
|
|
22
|
+
red: (msg) => pc.red(msg),
|
|
23
|
+
statusBadge: (status) => {
|
|
24
|
+
switch (status) {
|
|
25
|
+
case "validated":
|
|
26
|
+
return pc.green(status);
|
|
27
|
+
case "proposed":
|
|
28
|
+
return pc.yellow(status);
|
|
29
|
+
case "stale":
|
|
30
|
+
return pc.yellow(status);
|
|
31
|
+
case "rejected":
|
|
32
|
+
return pc.red(status);
|
|
33
|
+
case "deprecated":
|
|
34
|
+
return pc.dim(status);
|
|
35
|
+
default:
|
|
36
|
+
return pc.dim(status);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export {
|
|
42
|
+
setUiJsonMode,
|
|
43
|
+
ui
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=chunk-GGDCU7PH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/ui.ts"],"sourcesContent":["import pc from \"picocolors\";\n\n// When a command emits machine-readable JSON on stdout, human log lines must not pollute it.\n// `setUiJsonMode(true)` routes info/success/warn to stderr so stdout stays a clean JSON channel.\nlet jsonMode = false;\nexport function setUiJsonMode(on: boolean): void {\n jsonMode = on;\n}\nconst logHuman = (icon: string, msg: string): void => {\n if (jsonMode) console.error(icon, msg);\n else console.log(icon, msg);\n};\n\nexport const ui = {\n info: (msg: string) => logHuman(pc.cyan(\"ℹ\"), msg),\n success: (msg: string) => logHuman(pc.green(\"✓\"), msg),\n warn: (msg: string) => logHuman(pc.yellow(\"⚠\"), msg),\n error: (msg: string) => console.error(pc.red(\"✗\"), msg),\n dim: (msg: string) => pc.dim(msg),\n bold: (msg: string) => pc.bold(msg),\n green: (msg: string) => pc.green(msg),\n yellow: (msg: string) => pc.yellow(msg),\n red: (msg: string) => pc.red(msg),\n statusBadge: (status: string): string => {\n switch (status) {\n case \"validated\": return pc.green(status);\n case \"proposed\": return pc.yellow(status);\n case \"stale\": return pc.yellow(status);\n case \"rejected\": return pc.red(status);\n case \"deprecated\": return pc.dim(status);\n default: return pc.dim(status); // draft\n }\n },\n};\n"],"mappings":";;;AAAA,OAAO,QAAQ;AAIf,IAAI,WAAW;AACR,SAAS,cAAc,IAAmB;AAC/C,aAAW;AACb;AACA,IAAM,WAAW,CAAC,MAAc,QAAsB;AACpD,MAAI,SAAU,SAAQ,MAAM,MAAM,GAAG;AAAA,MAChC,SAAQ,IAAI,MAAM,GAAG;AAC5B;AAEO,IAAM,KAAK;AAAA,EAChB,MAAM,CAAC,QAAgB,SAAS,GAAG,KAAK,QAAG,GAAG,GAAG;AAAA,EACjD,SAAS,CAAC,QAAgB,SAAS,GAAG,MAAM,QAAG,GAAG,GAAG;AAAA,EACrD,MAAM,CAAC,QAAgB,SAAS,GAAG,OAAO,QAAG,GAAG,GAAG;AAAA,EACnD,OAAO,CAAC,QAAgB,QAAQ,MAAM,GAAG,IAAI,QAAG,GAAG,GAAG;AAAA,EACtD,KAAK,CAAC,QAAgB,GAAG,IAAI,GAAG;AAAA,EAChC,MAAM,CAAC,QAAgB,GAAG,KAAK,GAAG;AAAA,EAClC,OAAO,CAAC,QAAgB,GAAG,MAAM,GAAG;AAAA,EACpC,QAAQ,CAAC,QAAgB,GAAG,OAAO,GAAG;AAAA,EACtC,KAAK,CAAC,QAAgB,GAAG,IAAI,GAAG;AAAA,EAChC,aAAa,CAAC,WAA2B;AACvC,YAAQ,QAAQ;AAAA,MACd,KAAK;AAAa,eAAO,GAAG,MAAM,MAAM;AAAA,MACxC,KAAK;AAAY,eAAO,GAAG,OAAO,MAAM;AAAA,MACxC,KAAK;AAAS,eAAO,GAAG,OAAO,MAAM;AAAA,MACrC,KAAK;AAAY,eAAO,GAAG,IAAI,MAAM;AAAA,MACrC,KAAK;AAAc,eAAO,GAAG,IAAI,MAAM;AAAA,MACvC;AAAS,eAAO,GAAG,IAAI,MAAM;AAAA,IAC/B;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ui
|
|
4
|
+
} from "./chunk-GGDCU7PH.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/memory-seed-git.ts
|
|
7
|
+
import { execFile } from "child_process";
|
|
8
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
9
|
+
import { existsSync } from "fs";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { promisify } from "util";
|
|
12
|
+
import "commander";
|
|
13
|
+
import {
|
|
14
|
+
buildFrontmatter,
|
|
15
|
+
findProjectRoot,
|
|
16
|
+
memoryFilePath,
|
|
17
|
+
proposeSeedsFromCommits,
|
|
18
|
+
resolveHaivePaths,
|
|
19
|
+
serializeMemory
|
|
20
|
+
} from "@hivelore/core";
|
|
21
|
+
var exec = promisify(execFile);
|
|
22
|
+
function registerMemorySeedGit(memory) {
|
|
23
|
+
memory.command("seed-git", { hidden: true }).description("Propose draft `attempt` seeds from revert/hotfix commits in git history (cold-start)").option("--apply", "write the proposed seeds as draft memories (default: preview only)", false).option("--limit <n>", "max seeds to propose", "20").option("--days <n>", "git-history lookback window in days", "365").option("--scope <scope>", "personal | team", "team").option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => runGitSeed(opts));
|
|
24
|
+
}
|
|
25
|
+
async function runGitSeed(opts) {
|
|
26
|
+
const root = findProjectRoot(opts.dir);
|
|
27
|
+
const paths = resolveHaivePaths(root);
|
|
28
|
+
if (!existsSync(paths.haiveDir)) {
|
|
29
|
+
ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
|
|
30
|
+
process.exitCode = 1;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const limit = Math.max(1, parseInt(opts.limit ?? "20", 10) || 20);
|
|
34
|
+
const days = Math.max(1, parseInt(opts.days ?? "365", 10) || 365);
|
|
35
|
+
const commits = await readCommits(root, days);
|
|
36
|
+
const proposals = proposeSeedsFromCommits(commits, limit);
|
|
37
|
+
if (opts.json) {
|
|
38
|
+
console.log(JSON.stringify({ scanned_commits: commits.length, proposals, applied: Boolean(opts.apply) }, null, 2));
|
|
39
|
+
} else if (proposals.length === 0) {
|
|
40
|
+
ui.info("No revert/hotfix signals found in git history \u2014 nothing to seed.");
|
|
41
|
+
return;
|
|
42
|
+
} else {
|
|
43
|
+
console.log(ui.bold(`Hivelore seed-git \u2014 ${proposals.length} proposal(s) from ${commits.length} commit(s)`));
|
|
44
|
+
for (const p of proposals) {
|
|
45
|
+
console.log(` ${ui.yellow("\u25C6")} ${ui.dim(`[${p.kind}]`)} ${p.what}`);
|
|
46
|
+
if (p.paths.length > 0) console.log(` ${ui.dim("paths:")} ${p.paths.join(", ")}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (!opts.apply) {
|
|
50
|
+
if (!opts.json) ui.info("Preview only \u2014 re-run with --apply to write these as draft memories.");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
let written = 0;
|
|
54
|
+
for (const p of proposals) {
|
|
55
|
+
const fm = {
|
|
56
|
+
...buildFrontmatter({
|
|
57
|
+
type: "attempt",
|
|
58
|
+
slug: p.slug,
|
|
59
|
+
scope: opts.scope ?? "team",
|
|
60
|
+
tags: ["seed", "git-history", p.kind],
|
|
61
|
+
paths: p.paths
|
|
62
|
+
}),
|
|
63
|
+
status: "draft"
|
|
64
|
+
// human reviews before it becomes validated
|
|
65
|
+
};
|
|
66
|
+
const body = `# ${p.what}
|
|
67
|
+
|
|
68
|
+
**Why it failed / do NOT use:** ${p.why_failed}
|
|
69
|
+
|
|
70
|
+
_Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delete) \u2014 not yet authoritative._
|
|
71
|
+
`;
|
|
72
|
+
const file = memoryFilePath(paths, fm.scope, fm.id, fm.module);
|
|
73
|
+
if (existsSync(file)) continue;
|
|
74
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
75
|
+
await writeFile(file, serializeMemory({ frontmatter: fm, body }), "utf8");
|
|
76
|
+
written += 1;
|
|
77
|
+
}
|
|
78
|
+
if (!opts.json) {
|
|
79
|
+
ui.success(`Wrote ${written} draft seed(s). Review them: \`hivelore memory pending\` \u2192 validate or delete.`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function readCommits(root, days) {
|
|
83
|
+
try {
|
|
84
|
+
const { stdout } = await exec(
|
|
85
|
+
"git",
|
|
86
|
+
["log", `--since=${days}.days.ago`, "--name-only", "--pretty=format:%x1f%h%x1f%s", "-n", "500"],
|
|
87
|
+
{ cwd: root, maxBuffer: 8 * 1024 * 1024 }
|
|
88
|
+
);
|
|
89
|
+
const blocks = stdout.split("").filter((b) => b.length > 0);
|
|
90
|
+
const commits = [];
|
|
91
|
+
for (let i = 0; i + 1 < blocks.length; i += 2) {
|
|
92
|
+
const sha = blocks[i].trim();
|
|
93
|
+
const tail = blocks[i + 1];
|
|
94
|
+
const lines = tail.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
95
|
+
const subject = lines.shift() ?? "";
|
|
96
|
+
commits.push({ sha, subject, files: lines });
|
|
97
|
+
}
|
|
98
|
+
return commits;
|
|
99
|
+
} catch {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export {
|
|
105
|
+
registerMemorySeedGit,
|
|
106
|
+
runGitSeed
|
|
107
|
+
};
|
|
108
|
+
//# sourceMappingURL=chunk-HIWRB454.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/memory-seed-git.ts"],"sourcesContent":["/**\n * `hivelore memory seed-git` — cold-start the corpus from git history.\n *\n * A fresh repo has no memories, so the harness has no feedforward value until the team invests.\n * Reverts and urgent fixups are the cheapest signal of a real, repo-specific mistake already paid\n * for. This scans `git log`, proposes DRAFT `attempt` seeds (never validated — human reviews), and\n * (with --apply) writes them so future briefings carry the lesson. Closes Fowler's \"legacy is hard\"\n * harnessability gap with zero manual authoring.\n */\nimport { execFile } from \"node:child_process\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { Command } from \"commander\";\nimport {\n buildFrontmatter,\n findProjectRoot,\n memoryFilePath,\n proposeSeedsFromCommits,\n resolveHaivePaths,\n serializeMemory,\n type GitCommit,\n type MemoryScope,\n} from \"@hivelore/core\";\nimport { ui } from \"../utils/ui.js\";\n\nconst exec = promisify(execFile);\n\nexport interface SeedGitOptions {\n apply?: boolean;\n limit?: string;\n days?: string;\n scope?: MemoryScope;\n json?: boolean;\n dir?: string;\n}\n\nexport function registerMemorySeedGit(memory: Command): void {\n memory\n .command(\"seed-git\", { hidden: true })\n .description(\"Propose draft `attempt` seeds from revert/hotfix commits in git history (cold-start)\")\n .option(\"--apply\", \"write the proposed seeds as draft memories (default: preview only)\", false)\n .option(\"--limit <n>\", \"max seeds to propose\", \"20\")\n .option(\"--days <n>\", \"git-history lookback window in days\", \"365\")\n .option(\"--scope <scope>\", \"personal | team\", \"team\")\n .option(\"--json\", \"emit JSON\", false)\n .option(\"-d, --dir <dir>\", \"project root\")\n .action(async (opts: SeedGitOptions) => runGitSeed(opts));\n}\n\nexport async function runGitSeed(opts: SeedGitOptions): Promise<void> {\n const root = findProjectRoot(opts.dir);\n const paths = resolveHaivePaths(root);\n if (!existsSync(paths.haiveDir)) {\n ui.error(`No .ai/ found at ${root}. Run \\`hivelore init\\` first.`);\n process.exitCode = 1;\n return;\n }\n\n const limit = Math.max(1, parseInt(opts.limit ?? \"20\", 10) || 20);\n const days = Math.max(1, parseInt(opts.days ?? \"365\", 10) || 365);\n const commits = await readCommits(root, days);\n const proposals = proposeSeedsFromCommits(commits, limit);\n\n if (opts.json) {\n console.log(JSON.stringify({ scanned_commits: commits.length, proposals, applied: Boolean(opts.apply) }, null, 2));\n } else if (proposals.length === 0) {\n ui.info(\"No revert/hotfix signals found in git history — nothing to seed.\");\n return;\n } else {\n console.log(ui.bold(`Hivelore seed-git — ${proposals.length} proposal(s) from ${commits.length} commit(s)`));\n for (const p of proposals) {\n console.log(` ${ui.yellow(\"◆\")} ${ui.dim(`[${p.kind}]`)} ${p.what}`);\n if (p.paths.length > 0) console.log(` ${ui.dim(\"paths:\")} ${p.paths.join(\", \")}`);\n }\n }\n\n if (!opts.apply) {\n if (!opts.json) ui.info(\"Preview only — re-run with --apply to write these as draft memories.\");\n return;\n }\n\n let written = 0;\n for (const p of proposals) {\n const fm = {\n ...buildFrontmatter({\n type: \"attempt\",\n slug: p.slug,\n scope: opts.scope ?? \"team\",\n tags: [\"seed\", \"git-history\", p.kind],\n paths: p.paths,\n }),\n status: \"draft\" as const, // human reviews before it becomes validated\n };\n const body = `# ${p.what}\\n\\n**Why it failed / do NOT use:** ${p.why_failed}\\n\\n_Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delete) — not yet authoritative._\\n`;\n const file = memoryFilePath(paths, fm.scope, fm.id, fm.module);\n if (existsSync(file)) continue;\n await mkdir(path.dirname(file), { recursive: true });\n await writeFile(file, serializeMemory({ frontmatter: fm, body }), \"utf8\");\n written += 1;\n }\n if (!opts.json) {\n ui.success(`Wrote ${written} draft seed(s). Review them: \\`hivelore memory pending\\` → validate or delete.`);\n }\n}\n\n/** Read recent commits with their touched files for seeding. Best-effort; returns [] off-git. */\nasync function readCommits(root: string, days: number): Promise<GitCommit[]> {\n try {\n const { stdout } = await exec(\n \"git\",\n [\"log\", `--since=${days}.days.ago`, \"--name-only\", \"--pretty=format:%x1f%h%x1f%s\", \"-n\", \"500\"],\n { cwd: root, maxBuffer: 8 * 1024 * 1024 },\n );\n const blocks = stdout.split(\"\\x1f\").filter((b) => b.length > 0);\n const commits: GitCommit[] = [];\n for (let i = 0; i + 1 < blocks.length; i += 2) {\n const sha = blocks[i]!.trim();\n const tail = blocks[i + 1]!;\n const lines = tail.split(\"\\n\").map((l) => l.trim()).filter(Boolean);\n const subject = lines.shift() ?? \"\";\n commits.push({ sha, subject, files: lines });\n }\n return commits;\n } catch {\n return [];\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
loadMemoriesFromDir
|
|
4
|
+
} from "./chunk-532TRMXJ.js";
|
|
5
|
+
import {
|
|
6
|
+
ui
|
|
7
|
+
} from "./chunk-GGDCU7PH.js";
|
|
8
|
+
|
|
9
|
+
// src/commands/memory-pending.ts
|
|
10
|
+
import { existsSync } from "fs";
|
|
11
|
+
import path from "path";
|
|
12
|
+
import "commander";
|
|
13
|
+
import {
|
|
14
|
+
findProjectRoot,
|
|
15
|
+
getUsage,
|
|
16
|
+
loadUsageIndex,
|
|
17
|
+
resolveHaivePaths
|
|
18
|
+
} from "@hivelore/core";
|
|
19
|
+
function registerMemoryPending(memory) {
|
|
20
|
+
memory.command("pending", { hidden: true }).description("List draft and proposed memories awaiting review (sorted by reads desc).\n\n draft = created but not yet activated \xB7 proposed = promoted, awaiting team validation").option("--scope <scope>", "filter by scope (personal | team | module)").option("-d, --dir <dir>", "project root").action(async (opts) => runPending(opts));
|
|
21
|
+
}
|
|
22
|
+
async function runPending(opts) {
|
|
23
|
+
const root = findProjectRoot(opts.dir);
|
|
24
|
+
const paths = resolveHaivePaths(root);
|
|
25
|
+
if (!existsSync(paths.memoriesDir)) {
|
|
26
|
+
ui.error(`No .ai/memories at ${root}.`);
|
|
27
|
+
process.exitCode = 1;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const all = await loadMemoriesFromDir(paths.memoriesDir);
|
|
31
|
+
const usage = await loadUsageIndex(paths);
|
|
32
|
+
const filterFn = ({ memory: mem }) => {
|
|
33
|
+
if (mem.frontmatter.status !== "proposed" && mem.frontmatter.status !== "draft") return false;
|
|
34
|
+
if (opts.scope && mem.frontmatter.scope !== opts.scope) return false;
|
|
35
|
+
return true;
|
|
36
|
+
};
|
|
37
|
+
const pending = all.filter(filterFn);
|
|
38
|
+
if (pending.length === 0) {
|
|
39
|
+
ui.info("No draft or proposed memories awaiting review.");
|
|
40
|
+
ui.info("Drafts are created by `hivelore memory save` without `--status validated`.");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
pending.sort(
|
|
44
|
+
(a, b) => getUsage(usage, b.memory.frontmatter.id).read_count - getUsage(usage, a.memory.frontmatter.id).read_count
|
|
45
|
+
);
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
const drafts = pending.filter((m) => m.memory.frontmatter.status === "draft");
|
|
48
|
+
const proposed = pending.filter((m) => m.memory.frontmatter.status === "proposed");
|
|
49
|
+
if (proposed.length > 0) {
|
|
50
|
+
console.log(ui.bold(`Proposed (${proposed.length}) \u2014 awaiting team validation`));
|
|
51
|
+
for (const { memory: mem, filePath } of proposed) {
|
|
52
|
+
const fm = mem.frontmatter;
|
|
53
|
+
const u = getUsage(usage, fm.id);
|
|
54
|
+
const ageDays = Math.floor((now - new Date(fm.created_at).getTime()) / 864e5);
|
|
55
|
+
const ageStr = ageDays === 0 ? "today" : `${ageDays}d`;
|
|
56
|
+
console.log(
|
|
57
|
+
` ${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count}`)}`
|
|
58
|
+
);
|
|
59
|
+
console.log(` ${ui.dim(path.relative(root, filePath))}`);
|
|
60
|
+
}
|
|
61
|
+
if (proposed.length > 0) console.log(ui.dim(` \u2192 hivelore memory approve <id> or hivelore memory auto-promote`));
|
|
62
|
+
console.log();
|
|
63
|
+
}
|
|
64
|
+
if (drafts.length > 0) {
|
|
65
|
+
console.log(ui.bold(`Draft (${drafts.length}) \u2014 created but not yet activated`));
|
|
66
|
+
for (const { memory: mem, filePath } of drafts) {
|
|
67
|
+
const fm = mem.frontmatter;
|
|
68
|
+
const u = getUsage(usage, fm.id);
|
|
69
|
+
const ageDays = Math.floor((now - new Date(fm.created_at).getTime()) / 864e5);
|
|
70
|
+
const ageStr = ageDays === 0 ? "today" : `${ageDays}d`;
|
|
71
|
+
console.log(
|
|
72
|
+
` ${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count}`)}`
|
|
73
|
+
);
|
|
74
|
+
console.log(` ${ui.dim(path.relative(root, filePath))}`);
|
|
75
|
+
}
|
|
76
|
+
console.log(ui.dim(` \u2192 hivelore memory approve <id> (activate) | hivelore memory promote <id> (share with team)`));
|
|
77
|
+
}
|
|
78
|
+
ui.info(`${pending.length} total pending (${proposed.length} proposed \xB7 ${drafts.length} draft)`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export {
|
|
82
|
+
registerMemoryPending,
|
|
83
|
+
runPending
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=chunk-OYJKHD22.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/memory-pending.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { Command } from \"commander\";\nimport {\n findProjectRoot,\n getUsage,\n loadUsageIndex,\n resolveHaivePaths,\n} from \"@hivelore/core\";\nimport { loadMemoriesFromDir } from \"../utils/fs.js\";\nimport { ui } from \"../utils/ui.js\";\n\nexport interface PendingOptions {\n scope?: \"personal\" | \"team\" | \"module\";\n dir?: string;\n}\n\nexport function registerMemoryPending(memory: Command): void {\n memory\n .command(\"pending\", { hidden: true })\n .description(\"List draft and proposed memories awaiting review (sorted by reads desc).\\n\\n draft = created but not yet activated · proposed = promoted, awaiting team validation\")\n .option(\"--scope <scope>\", \"filter by scope (personal | team | module)\")\n .option(\"-d, --dir <dir>\", \"project root\")\n .action(async (opts: PendingOptions) => runPending(opts));\n}\n\nexport async function runPending(opts: PendingOptions): Promise<void> {\n const root = findProjectRoot(opts.dir);\n const paths = resolveHaivePaths(root);\n if (!existsSync(paths.memoriesDir)) {\n ui.error(`No .ai/memories at ${root}.`);\n process.exitCode = 1;\n return;\n }\n\n const all = await loadMemoriesFromDir(paths.memoriesDir);\n const usage = await loadUsageIndex(paths);\n\n const filterFn = ({ memory: mem }: { memory: { frontmatter: { status: string; scope: string } } }) => {\n if (mem.frontmatter.status !== \"proposed\" && mem.frontmatter.status !== \"draft\") return false;\n if (opts.scope && mem.frontmatter.scope !== opts.scope) return false;\n return true;\n };\n const pending = all.filter(filterFn);\n\n if (pending.length === 0) {\n ui.info(\"No draft or proposed memories awaiting review.\");\n ui.info(\"Drafts are created by `hivelore memory save` without `--status validated`.\");\n return;\n }\n\n pending.sort(\n (a, b) =>\n getUsage(usage, b.memory.frontmatter.id).read_count -\n getUsage(usage, a.memory.frontmatter.id).read_count,\n );\n\n const now = Date.now();\n const drafts = pending.filter((m) => m.memory.frontmatter.status === \"draft\");\n const proposed = pending.filter((m) => m.memory.frontmatter.status === \"proposed\");\n\n if (proposed.length > 0) {\n console.log(ui.bold(`Proposed (${proposed.length}) — awaiting team validation`));\n for (const { memory: mem, filePath } of proposed) {\n const fm = mem.frontmatter;\n const u = getUsage(usage, fm.id);\n const ageDays = Math.floor((now - new Date(fm.created_at).getTime()) / 86_400_000);\n const ageStr = ageDays === 0 ? \"today\" : `${ageDays}d`;\n console.log(\n ` ${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count}`)}`,\n );\n console.log(` ${ui.dim(path.relative(root, filePath))}`);\n }\n if (proposed.length > 0) console.log(ui.dim(` → hivelore memory approve <id> or hivelore memory auto-promote`));\n console.log();\n }\n\n if (drafts.length > 0) {\n console.log(ui.bold(`Draft (${drafts.length}) — created but not yet activated`));\n for (const { memory: mem, filePath } of drafts) {\n const fm = mem.frontmatter;\n const u = getUsage(usage, fm.id);\n const ageDays = Math.floor((now - new Date(fm.created_at).getTime()) / 86_400_000);\n const ageStr = ageDays === 0 ? \"today\" : `${ageDays}d`;\n console.log(\n ` ${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count}`)}`,\n );\n console.log(` ${ui.dim(path.relative(root, filePath))}`);\n }\n console.log(ui.dim(` → hivelore memory approve <id> (activate) | hivelore memory promote <id> (share with team)`));\n }\n\n ui.info(`${pending.length} total pending (${proposed.length} proposed · ${drafts.length} draft)`);\n}\n"],"mappings":"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","names":[]}
|