@hiveai/cli 0.9.29 → 0.9.30
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 +277 -101
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1213,12 +1213,14 @@ async function loadEmbeddings() {
|
|
|
1213
1213
|
}
|
|
1214
1214
|
|
|
1215
1215
|
// src/commands/index-code.ts
|
|
1216
|
+
import { existsSync as existsSync5, statSync } from "fs";
|
|
1216
1217
|
import path5 from "path";
|
|
1217
1218
|
import "commander";
|
|
1218
1219
|
import {
|
|
1219
1220
|
buildCodeMap as buildCodeMap2,
|
|
1220
1221
|
codeMapPath,
|
|
1221
1222
|
findProjectRoot as findProjectRoot5,
|
|
1223
|
+
loadCodeMap as loadCodeMap4,
|
|
1222
1224
|
resolveHaivePaths as resolveHaivePaths4,
|
|
1223
1225
|
saveCodeMap as saveCodeMap2
|
|
1224
1226
|
} from "@hiveai/core";
|
|
@@ -1228,14 +1230,18 @@ function registerIndexCode(program2) {
|
|
|
1228
1230
|
);
|
|
1229
1231
|
idx.action(() => idx.help());
|
|
1230
1232
|
idx.command("code").description(
|
|
1231
|
-
"Scan source files and write .ai/code-map.json (file \u2192 exports + 1-line description).\n\n Supported languages: TypeScript, JavaScript, Java, Python, Go, Rust, C#, PHP.\n The map is used by:\n \u2022 get_briefing (symbol_locations) \u2014 look up where a class/function lives\n \u2022 code_map MCP tool \u2014 browse exports without grepping\n \u2022 haive briefing --symbols \u2014 look up symbols from the CLI\n\n Run automatically by haive init (autopilot mode) and haive sync (if source changed).\n\n Example:\n haive index code\n haive index code --exclude generated,proto\n"
|
|
1233
|
+
"Scan source files and write .ai/code-map.json (file \u2192 exports + 1-line description).\n\n Supported languages: TypeScript, JavaScript, Java, Python, Go, Rust, C#, PHP.\n The map is used by:\n \u2022 get_briefing (symbol_locations) \u2014 look up where a class/function lives\n \u2022 code_map MCP tool \u2014 browse exports without grepping\n \u2022 haive briefing --symbols \u2014 look up symbols from the CLI\n\n Run automatically by haive init (autopilot mode) and haive sync (if source changed).\n\n Example:\n haive index code\n haive index code --status # report freshness without rebuilding\n haive index code --exclude generated,proto\n"
|
|
1232
1234
|
).option("-d, --dir <dir>", "project root").option(
|
|
1233
1235
|
"--exclude <csv>",
|
|
1234
1236
|
"extra directory names to skip (comma-separated)",
|
|
1235
1237
|
""
|
|
1236
|
-
).action(async (opts) => {
|
|
1238
|
+
).option("--status", "report code-map / code-search index freshness without rebuilding").option("--json", "with --status, emit machine-readable JSON (for CI / agents)").action(async (opts) => {
|
|
1237
1239
|
const root = findProjectRoot5(opts.dir);
|
|
1238
1240
|
const paths = resolveHaivePaths4(root);
|
|
1241
|
+
if (opts.status) {
|
|
1242
|
+
await reportIndexStatus(root, paths, opts.json === true);
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1239
1245
|
const extraExcludes = (opts.exclude ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
1240
1246
|
ui.info(`Indexing source files in ${root}\u2026`);
|
|
1241
1247
|
const map = await buildCodeMap2(root, {
|
|
@@ -1288,10 +1294,49 @@ function registerIndexCode(program2) {
|
|
|
1288
1294
|
}
|
|
1289
1295
|
});
|
|
1290
1296
|
}
|
|
1297
|
+
async function reportIndexStatus(root, paths, asJson) {
|
|
1298
|
+
const mapFile = codeMapPath(paths);
|
|
1299
|
+
const map = existsSync5(mapFile) ? await loadCodeMap4(paths) : null;
|
|
1300
|
+
const fileCount = map ? Object.keys(map.files).length : 0;
|
|
1301
|
+
const exportCount = map ? Object.values(map.files).reduce((s, f) => s + f.exports.length, 0) : 0;
|
|
1302
|
+
const mapMtime = existsSync5(mapFile) ? statSync(mapFile).mtime.toISOString() : null;
|
|
1303
|
+
const searchIndexFile = path5.join(paths.haiveDir, ".cache", "embeddings", "code-embeddings-index.json");
|
|
1304
|
+
const searchIndexPresent = existsSync5(searchIndexFile);
|
|
1305
|
+
const status = {
|
|
1306
|
+
code_map: {
|
|
1307
|
+
present: map !== null,
|
|
1308
|
+
path: path5.relative(root, mapFile),
|
|
1309
|
+
files: fileCount,
|
|
1310
|
+
exports: exportCount,
|
|
1311
|
+
generated_at: map?.generated_at ?? null,
|
|
1312
|
+
file_mtime: mapMtime
|
|
1313
|
+
},
|
|
1314
|
+
code_search_index: {
|
|
1315
|
+
present: searchIndexPresent,
|
|
1316
|
+
path: path5.relative(root, searchIndexFile)
|
|
1317
|
+
}
|
|
1318
|
+
};
|
|
1319
|
+
if (asJson) {
|
|
1320
|
+
console.log(JSON.stringify(status, null, 2));
|
|
1321
|
+
if (!status.code_map.present) process.exitCode = 1;
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
if (!status.code_map.present) {
|
|
1325
|
+
ui.warn(`No code-map at ${status.code_map.path}. Run \`haive index code\`.`);
|
|
1326
|
+
process.exitCode = 1;
|
|
1327
|
+
return;
|
|
1328
|
+
}
|
|
1329
|
+
ui.info(
|
|
1330
|
+
`code-map: ${fileCount} file(s), ${exportCount} export(s) \xB7 generated ${status.code_map.generated_at ?? "?"}`
|
|
1331
|
+
);
|
|
1332
|
+
ui.info(
|
|
1333
|
+
status.code_search_index.present ? `code-search index: present (${status.code_search_index.path})` : `code-search index: missing \u2014 run \`haive index code-search\` for semantic code lookup.`
|
|
1334
|
+
);
|
|
1335
|
+
}
|
|
1291
1336
|
|
|
1292
1337
|
// src/commands/init.ts
|
|
1293
1338
|
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
|
|
1294
|
-
import { existsSync as
|
|
1339
|
+
import { existsSync as existsSync10 } from "fs";
|
|
1295
1340
|
import path10 from "path";
|
|
1296
1341
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
1297
1342
|
import "commander";
|
|
@@ -1305,7 +1350,7 @@ import {
|
|
|
1305
1350
|
|
|
1306
1351
|
// src/commands/agent.ts
|
|
1307
1352
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
1308
|
-
import { existsSync as
|
|
1353
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1309
1354
|
import { mkdir as mkdir3, writeFile as writeFile4 } from "fs/promises";
|
|
1310
1355
|
import os2 from "os";
|
|
1311
1356
|
import path7 from "path";
|
|
@@ -1315,7 +1360,7 @@ import { findProjectRoot as findProjectRoot6, resolveHaivePaths as resolveHaiveP
|
|
|
1315
1360
|
|
|
1316
1361
|
// src/commands/init-mcp-setup.ts
|
|
1317
1362
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
|
|
1318
|
-
import { existsSync as
|
|
1363
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1319
1364
|
import path6 from "path";
|
|
1320
1365
|
import os from "os";
|
|
1321
1366
|
var HOME = os.homedir();
|
|
@@ -1336,9 +1381,9 @@ function cursorMcpPath() {
|
|
|
1336
1381
|
async function configureCursor() {
|
|
1337
1382
|
const mcpPath = cursorMcpPath();
|
|
1338
1383
|
const cursorDir = path6.join(HOME, ".cursor");
|
|
1339
|
-
if (!
|
|
1384
|
+
if (!existsSync6(cursorDir)) return { client: "Cursor", status: "not_installed" };
|
|
1340
1385
|
let config = {};
|
|
1341
|
-
if (
|
|
1386
|
+
if (existsSync6(mcpPath)) {
|
|
1342
1387
|
try {
|
|
1343
1388
|
config = JSON.parse(await readFile3(mcpPath, "utf8"));
|
|
1344
1389
|
} catch {
|
|
@@ -1362,7 +1407,7 @@ function vscodeMcpPath() {
|
|
|
1362
1407
|
path6.join(HOME, ".config", "Code - Insiders", "User", "mcp.json")
|
|
1363
1408
|
];
|
|
1364
1409
|
for (const c of candidates) {
|
|
1365
|
-
if (
|
|
1410
|
+
if (existsSync6(path6.dirname(c))) return c;
|
|
1366
1411
|
}
|
|
1367
1412
|
return null;
|
|
1368
1413
|
}
|
|
@@ -1370,7 +1415,7 @@ async function configureVSCode() {
|
|
|
1370
1415
|
const mcpPath = vscodeMcpPath();
|
|
1371
1416
|
if (!mcpPath) return { client: "VS Code", status: "not_installed" };
|
|
1372
1417
|
let config = {};
|
|
1373
|
-
if (
|
|
1418
|
+
if (existsSync6(mcpPath)) {
|
|
1374
1419
|
try {
|
|
1375
1420
|
config = JSON.parse(await readFile3(mcpPath, "utf8"));
|
|
1376
1421
|
} catch {
|
|
@@ -1385,18 +1430,18 @@ async function configureVSCode() {
|
|
|
1385
1430
|
}
|
|
1386
1431
|
function claudeConfigPath() {
|
|
1387
1432
|
const p = path6.join(HOME, ".claude.json");
|
|
1388
|
-
if (
|
|
1433
|
+
if (existsSync6(p)) return p;
|
|
1389
1434
|
const p2 = path6.join(HOME, ".config", "claude", "claude.json");
|
|
1390
|
-
if (
|
|
1435
|
+
if (existsSync6(path6.dirname(p2))) return p2;
|
|
1391
1436
|
return null;
|
|
1392
1437
|
}
|
|
1393
1438
|
async function configureClaude() {
|
|
1394
1439
|
const cfgPath = claudeConfigPath() ?? path6.join(HOME, ".claude.json");
|
|
1395
|
-
if (!
|
|
1440
|
+
if (!existsSync6(cfgPath) && !existsSync6(path6.join(HOME, ".claude"))) {
|
|
1396
1441
|
return { client: "Claude Code", status: "not_installed" };
|
|
1397
1442
|
}
|
|
1398
1443
|
let config = {};
|
|
1399
|
-
if (
|
|
1444
|
+
if (existsSync6(cfgPath)) {
|
|
1400
1445
|
try {
|
|
1401
1446
|
config = JSON.parse(await readFile3(cfgPath, "utf8"));
|
|
1402
1447
|
} catch {
|
|
@@ -1414,7 +1459,7 @@ function windsurfMcpPath() {
|
|
|
1414
1459
|
path6.join(HOME, ".windsurf", "mcp.json")
|
|
1415
1460
|
];
|
|
1416
1461
|
for (const c of candidates) {
|
|
1417
|
-
if (
|
|
1462
|
+
if (existsSync6(path6.dirname(c))) return c;
|
|
1418
1463
|
}
|
|
1419
1464
|
return null;
|
|
1420
1465
|
}
|
|
@@ -1422,7 +1467,7 @@ async function configureWindsurf() {
|
|
|
1422
1467
|
const mcpPath = windsurfMcpPath();
|
|
1423
1468
|
if (!mcpPath) return { client: "Windsurf", status: "not_installed" };
|
|
1424
1469
|
let config = {};
|
|
1425
|
-
if (
|
|
1470
|
+
if (existsSync6(mcpPath)) {
|
|
1426
1471
|
try {
|
|
1427
1472
|
config = JSON.parse(await readFile3(mcpPath, "utf8"));
|
|
1428
1473
|
} catch {
|
|
@@ -1454,7 +1499,7 @@ async function configureProjectMcpClients(root) {
|
|
|
1454
1499
|
try {
|
|
1455
1500
|
const cursorPath = path6.join(root, ".cursor", "mcp.json");
|
|
1456
1501
|
let config = {};
|
|
1457
|
-
if (
|
|
1502
|
+
if (existsSync6(cursorPath)) {
|
|
1458
1503
|
try {
|
|
1459
1504
|
config = JSON.parse(await readFile3(cursorPath, "utf8"));
|
|
1460
1505
|
} catch {
|
|
@@ -1471,7 +1516,7 @@ async function configureProjectMcpClients(root) {
|
|
|
1471
1516
|
try {
|
|
1472
1517
|
const vscodePath = path6.join(root, ".vscode", "mcp.json");
|
|
1473
1518
|
let config = {};
|
|
1474
|
-
if (
|
|
1519
|
+
if (existsSync6(vscodePath)) {
|
|
1475
1520
|
try {
|
|
1476
1521
|
config = JSON.parse(await readFile3(vscodePath, "utf8"));
|
|
1477
1522
|
} catch {
|
|
@@ -1488,7 +1533,7 @@ async function configureProjectMcpClients(root) {
|
|
|
1488
1533
|
try {
|
|
1489
1534
|
const mcpPath = path6.join(root, ".mcp.json");
|
|
1490
1535
|
let config = {};
|
|
1491
|
-
if (
|
|
1536
|
+
if (existsSync6(mcpPath)) {
|
|
1492
1537
|
try {
|
|
1493
1538
|
config = JSON.parse(await readFile3(mcpPath, "utf8"));
|
|
1494
1539
|
} catch {
|
|
@@ -1562,9 +1607,9 @@ async function detectAgentMode(dir) {
|
|
|
1562
1607
|
const root = findProjectRoot6(dir);
|
|
1563
1608
|
const paths = resolveHaivePaths5(root);
|
|
1564
1609
|
const projectMcp = [
|
|
1565
|
-
{ client: "Claude Code", path: path7.join(root, ".mcp.json"), present:
|
|
1566
|
-
{ client: "Cursor", path: path7.join(root, ".cursor", "mcp.json"), present:
|
|
1567
|
-
{ client: "VS Code", path: path7.join(root, ".vscode", "mcp.json"), present:
|
|
1610
|
+
{ client: "Claude Code", path: path7.join(root, ".mcp.json"), present: existsSync7(path7.join(root, ".mcp.json")) },
|
|
1611
|
+
{ client: "Cursor", path: path7.join(root, ".cursor", "mcp.json"), present: existsSync7(path7.join(root, ".cursor", "mcp.json")) },
|
|
1612
|
+
{ client: "VS Code", path: path7.join(root, ".vscode", "mcp.json"), present: existsSync7(path7.join(root, ".vscode", "mcp.json")) }
|
|
1568
1613
|
];
|
|
1569
1614
|
const installedAgents = [
|
|
1570
1615
|
{ agent: "Codex", command: "codex", installed: commandExists("codex"), mcp_configured: codexMcpConfigured() },
|
|
@@ -1579,7 +1624,7 @@ async function detectAgentMode(dir) {
|
|
|
1579
1624
|
const recommendedCommand = recommendedMode === "mcp" ? "Restart your AI client, then call get_briefing before editing." : recommendedMode === "wrapped" && wrapperAgent ? `haive run -- ${wrapperAgent.command}` : 'haive briefing --task "..." --files "..."';
|
|
1580
1625
|
return {
|
|
1581
1626
|
root,
|
|
1582
|
-
initialized:
|
|
1627
|
+
initialized: existsSync7(paths.haiveDir),
|
|
1583
1628
|
project_mcp: projectMcp,
|
|
1584
1629
|
installed_agents: installedAgents,
|
|
1585
1630
|
recommended_mode: recommendedMode,
|
|
@@ -1686,7 +1731,7 @@ function printSetupResult(result) {
|
|
|
1686
1731
|
|
|
1687
1732
|
// src/commands/init-bootstrap.ts
|
|
1688
1733
|
import { readdir, readFile as readFile4 } from "fs/promises";
|
|
1689
|
-
import { existsSync as
|
|
1734
|
+
import { existsSync as existsSync8, readdirSync } from "fs";
|
|
1690
1735
|
import path8 from "path";
|
|
1691
1736
|
var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
1692
1737
|
"node_modules",
|
|
@@ -1773,14 +1818,38 @@ function detectKeyDeps(allDeps) {
|
|
|
1773
1818
|
return KEY_DEPS.filter((d) => allDeps[d] !== void 0);
|
|
1774
1819
|
}
|
|
1775
1820
|
function detectLanguage(root) {
|
|
1776
|
-
if (
|
|
1777
|
-
if (
|
|
1778
|
-
if (
|
|
1779
|
-
if (
|
|
1780
|
-
if (
|
|
1781
|
-
if (
|
|
1821
|
+
if (existsSync8(path8.join(root, "tsconfig.json"))) return "TypeScript";
|
|
1822
|
+
if (existsSync8(path8.join(root, "pyproject.toml")) || existsSync8(path8.join(root, "setup.py"))) return "Python";
|
|
1823
|
+
if (existsSync8(path8.join(root, "go.mod"))) return "Go";
|
|
1824
|
+
if (existsSync8(path8.join(root, "pom.xml")) || existsSync8(path8.join(root, "build.gradle"))) return "Java/Kotlin";
|
|
1825
|
+
if (existsSync8(path8.join(root, "Cargo.toml"))) return "Rust";
|
|
1826
|
+
if (existsSync8(path8.join(root, "package.json"))) {
|
|
1827
|
+
return hasSourceWithExt(root, [".ts", ".tsx", ".mts", ".cts"]) ? "TypeScript" : "JavaScript";
|
|
1828
|
+
}
|
|
1782
1829
|
return "Unknown";
|
|
1783
1830
|
}
|
|
1831
|
+
function hasSourceWithExt(root, exts) {
|
|
1832
|
+
const matches = (name) => !name.endsWith(".d.ts") && exts.some((ext) => name.endsWith(ext));
|
|
1833
|
+
const scanDir = (dir, depth) => {
|
|
1834
|
+
let entries;
|
|
1835
|
+
try {
|
|
1836
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
1837
|
+
} catch {
|
|
1838
|
+
return false;
|
|
1839
|
+
}
|
|
1840
|
+
for (const entry of entries) {
|
|
1841
|
+
if (entry.isFile() && matches(entry.name)) return true;
|
|
1842
|
+
}
|
|
1843
|
+
if (depth <= 0) return false;
|
|
1844
|
+
for (const entry of entries) {
|
|
1845
|
+
if (entry.isDirectory() && !IGNORE_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
|
|
1846
|
+
if (scanDir(path8.join(dir, entry.name), depth - 1)) return true;
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
return false;
|
|
1850
|
+
};
|
|
1851
|
+
return scanDir(root, 2);
|
|
1852
|
+
}
|
|
1784
1853
|
function detectProjectType(frameworks, scripts, isMonorepo) {
|
|
1785
1854
|
if (isMonorepo) {
|
|
1786
1855
|
if (frameworks.includes("NestJS")) return "Monorepo (NestJS backend)";
|
|
@@ -1795,7 +1864,7 @@ function detectProjectType(frameworks, scripts, isMonorepo) {
|
|
|
1795
1864
|
if (frameworks.includes("Express") || frameworks.includes("Fastify") || frameworks.includes("Hono")) return "Backend API";
|
|
1796
1865
|
if (frameworks.includes("React") || frameworks.includes("Vue") || frameworks.includes("Svelte")) return "Frontend SPA";
|
|
1797
1866
|
if (scripts["build"] && !scripts["dev"]) return "CLI tool / library";
|
|
1798
|
-
if (
|
|
1867
|
+
if (existsSync8("pom.xml")) return "Java backend";
|
|
1799
1868
|
return "Application";
|
|
1800
1869
|
}
|
|
1801
1870
|
async function scanDirs(root, maxDepth = 2) {
|
|
@@ -1931,7 +2000,7 @@ function readmeExcerpt(readme) {
|
|
|
1931
2000
|
async function generateBootstrapContext(root) {
|
|
1932
2001
|
let pkg = {};
|
|
1933
2002
|
const pkgPath = path8.join(root, "package.json");
|
|
1934
|
-
if (
|
|
2003
|
+
if (existsSync8(pkgPath)) {
|
|
1935
2004
|
try {
|
|
1936
2005
|
pkg = JSON.parse(await readFile4(pkgPath, "utf8"));
|
|
1937
2006
|
} catch {
|
|
@@ -1948,7 +2017,7 @@ async function generateBootstrapContext(root) {
|
|
|
1948
2017
|
let readmeSummary = "";
|
|
1949
2018
|
for (const name of ["README.md", "readme.md", "README"]) {
|
|
1950
2019
|
const p = path8.join(root, name);
|
|
1951
|
-
if (
|
|
2020
|
+
if (existsSync8(p)) {
|
|
1952
2021
|
try {
|
|
1953
2022
|
const content = await readFile4(p, "utf8");
|
|
1954
2023
|
readmeSummary = readmeExcerpt(content);
|
|
@@ -2015,7 +2084,7 @@ async function generateBootstrapContext(root) {
|
|
|
2015
2084
|
|
|
2016
2085
|
// src/commands/init-stack-packs.ts
|
|
2017
2086
|
import { mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
|
|
2018
|
-
import { existsSync as
|
|
2087
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2019
2088
|
import path9 from "path";
|
|
2020
2089
|
import {
|
|
2021
2090
|
buildFrontmatter,
|
|
@@ -2643,7 +2712,7 @@ async function seedStackPack(haivePaths, stack) {
|
|
|
2643
2712
|
tags: [...mem.tags, STACK_PACK_TAG]
|
|
2644
2713
|
});
|
|
2645
2714
|
const filePath = memoryFilePath(haivePaths, "team", fm.id);
|
|
2646
|
-
if (
|
|
2715
|
+
if (existsSync9(filePath)) continue;
|
|
2647
2716
|
const content = serializeMemory2({ frontmatter: fm, body: `${mem.body}
|
|
2648
2717
|
|
|
2649
2718
|
${SEED_FOOTER(stack)}` });
|
|
@@ -2867,7 +2936,7 @@ function registerInit(program2) {
|
|
|
2867
2936
|
const autopilot = opts.manual !== true;
|
|
2868
2937
|
const wantBootstrap = opts.bootstrap === void 0 ? autopilot : opts.bootstrap;
|
|
2869
2938
|
const wantStack = opts.stack === void 0 ? autopilot ? "auto" : void 0 : opts.stack === "none" ? void 0 : opts.stack;
|
|
2870
|
-
if (
|
|
2939
|
+
if (existsSync10(paths.haiveDir)) {
|
|
2871
2940
|
ui.warn(`.ai/ already exists at ${paths.haiveDir} \u2014 leaving existing files in place.`);
|
|
2872
2941
|
}
|
|
2873
2942
|
await mkdir5(paths.personalDir, { recursive: true });
|
|
@@ -2876,7 +2945,7 @@ function registerInit(program2) {
|
|
|
2876
2945
|
await mkdir5(paths.modulesContextDir, { recursive: true });
|
|
2877
2946
|
await ensureAiRuntimeLayout(paths.runtimeDir);
|
|
2878
2947
|
await ensureAiCacheLayout(path10.join(paths.haiveDir, ".cache"));
|
|
2879
|
-
if (!
|
|
2948
|
+
if (!existsSync10(paths.projectContext)) {
|
|
2880
2949
|
if (wantBootstrap) {
|
|
2881
2950
|
ui.info("Bootstrapping project context from local files\u2026");
|
|
2882
2951
|
try {
|
|
@@ -2892,7 +2961,7 @@ function registerInit(program2) {
|
|
|
2892
2961
|
ui.success(`Created ${path10.relative(root, paths.projectContext)}`);
|
|
2893
2962
|
}
|
|
2894
2963
|
}
|
|
2895
|
-
const configExists =
|
|
2964
|
+
const configExists = existsSync10(
|
|
2896
2965
|
path10.join(paths.haiveDir, "haive.config.json")
|
|
2897
2966
|
);
|
|
2898
2967
|
if (!configExists) {
|
|
@@ -2931,7 +3000,7 @@ function registerInit(program2) {
|
|
|
2931
3000
|
const wantCi = opts.withCi || autopilot;
|
|
2932
3001
|
if (wantCi) {
|
|
2933
3002
|
const ciPath = path10.join(root, ".github", "workflows", "haive-sync.yml");
|
|
2934
|
-
if (
|
|
3003
|
+
if (existsSync10(ciPath)) {
|
|
2935
3004
|
ui.info("CI workflow already exists \u2014 skipped");
|
|
2936
3005
|
} else {
|
|
2937
3006
|
await mkdir5(path10.dirname(ciPath), { recursive: true });
|
|
@@ -2980,8 +3049,8 @@ function registerInit(program2) {
|
|
|
2980
3049
|
else if (r.status === "error") ui.warn(`${r.client}: ${r.error}`);
|
|
2981
3050
|
}
|
|
2982
3051
|
for (const r of agentSetup.global_results) {
|
|
2983
|
-
if (r.status === "configured") ui.success(`haive MCP configured in ${r.client}${r.path ? ` (${r.path})` : ""}`);
|
|
2984
|
-
else if (r.status === "already_configured") ui.info(`haive MCP already
|
|
3052
|
+
if (r.status === "configured") ui.success(`haive MCP configured in ${r.client} user-level config${r.path ? ` (${r.path})` : ""}`);
|
|
3053
|
+
else if (r.status === "already_configured") ui.info(`haive MCP already present in ${r.client} user-level config \u2014 left unchanged (this project's config was written above)`);
|
|
2985
3054
|
}
|
|
2986
3055
|
if (agentSetup.global_skipped_reason) ui.warn(agentSetup.global_skipped_reason);
|
|
2987
3056
|
ui.info(`Recommended agent mode: ${agentSetup.detection.recommended_mode}`);
|
|
@@ -3048,7 +3117,7 @@ async function resolveStacksToSeed(root, stackOpt) {
|
|
|
3048
3117
|
if (!stackOpt) return [];
|
|
3049
3118
|
if (stackOpt === "auto") {
|
|
3050
3119
|
const pkgPath = path10.join(root, "package.json");
|
|
3051
|
-
if (!
|
|
3120
|
+
if (!existsSync10(pkgPath)) return [];
|
|
3052
3121
|
try {
|
|
3053
3122
|
const pkg = JSON.parse(await readFile5(pkgPath, "utf8"));
|
|
3054
3123
|
const allDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
@@ -3062,7 +3131,7 @@ async function resolveStacksToSeed(root, stackOpt) {
|
|
|
3062
3131
|
async function writeCursorHaiveRule(root) {
|
|
3063
3132
|
const relPath = ".cursor/rules/haive-mcp-required.mdc";
|
|
3064
3133
|
const target = path10.join(root, relPath);
|
|
3065
|
-
if (
|
|
3134
|
+
if (existsSync10(target)) {
|
|
3066
3135
|
ui.info(`Cursor rule ${relPath} already exists \u2014 skipped`);
|
|
3067
3136
|
return;
|
|
3068
3137
|
}
|
|
@@ -3072,7 +3141,7 @@ async function writeCursorHaiveRule(root) {
|
|
|
3072
3141
|
}
|
|
3073
3142
|
async function writeBridge(root, relPath) {
|
|
3074
3143
|
const target = path10.join(root, relPath);
|
|
3075
|
-
if (
|
|
3144
|
+
if (existsSync10(target)) {
|
|
3076
3145
|
ui.info(`Bridge ${relPath} already exists \u2014 skipped`);
|
|
3077
3146
|
return;
|
|
3078
3147
|
}
|
|
@@ -3095,18 +3164,18 @@ var RUNTIME_GITIGNORE_BODY = `*
|
|
|
3095
3164
|
async function ensureAiRuntimeLayout(runtimeDir) {
|
|
3096
3165
|
await mkdir5(runtimeDir, { recursive: true });
|
|
3097
3166
|
const gi = path10.join(runtimeDir, ".gitignore");
|
|
3098
|
-
if (!
|
|
3167
|
+
if (!existsSync10(gi)) {
|
|
3099
3168
|
await writeFile6(gi, RUNTIME_GITIGNORE_BODY, "utf8");
|
|
3100
3169
|
}
|
|
3101
3170
|
const readme = path10.join(runtimeDir, "README.md");
|
|
3102
|
-
if (!
|
|
3171
|
+
if (!existsSync10(readme)) {
|
|
3103
3172
|
await writeFile6(readme, RUNTIME_README_BODY, "utf8");
|
|
3104
3173
|
}
|
|
3105
3174
|
}
|
|
3106
3175
|
async function ensureAiCacheLayout(cacheDir) {
|
|
3107
3176
|
await mkdir5(cacheDir, { recursive: true });
|
|
3108
3177
|
const gi = path10.join(cacheDir, ".gitignore");
|
|
3109
|
-
if (!
|
|
3178
|
+
if (!existsSync10(gi)) {
|
|
3110
3179
|
await writeFile6(gi, "*\n!.gitignore\n", "utf8");
|
|
3111
3180
|
}
|
|
3112
3181
|
}
|
|
@@ -3114,7 +3183,7 @@ async function ensureGitignoreEntries(root, patterns) {
|
|
|
3114
3183
|
try {
|
|
3115
3184
|
const gitignorePath = path10.join(root, ".gitignore");
|
|
3116
3185
|
let existing = "";
|
|
3117
|
-
if (
|
|
3186
|
+
if (existsSync10(gitignorePath)) {
|
|
3118
3187
|
existing = await readFile5(gitignorePath, "utf8");
|
|
3119
3188
|
}
|
|
3120
3189
|
const lines = existing.split("\n");
|
|
@@ -3128,13 +3197,13 @@ async function ensureGitignoreEntries(root, patterns) {
|
|
|
3128
3197
|
|
|
3129
3198
|
// src/commands/install-hooks.ts
|
|
3130
3199
|
import { mkdir as mkdir7, writeFile as writeFile8, chmod, readFile as readFile7 } from "fs/promises";
|
|
3131
|
-
import { existsSync as
|
|
3200
|
+
import { existsSync as existsSync12 } from "fs";
|
|
3132
3201
|
import path12 from "path";
|
|
3133
3202
|
import "commander";
|
|
3134
3203
|
import { findProjectRoot as findProjectRoot8 } from "@hiveai/core";
|
|
3135
3204
|
|
|
3136
3205
|
// src/utils/claude-hooks.ts
|
|
3137
|
-
import { existsSync as
|
|
3206
|
+
import { existsSync as existsSync11 } from "fs";
|
|
3138
3207
|
import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile7 } from "fs/promises";
|
|
3139
3208
|
import path11 from "path";
|
|
3140
3209
|
var HAIVE_HOOK_TAG = "haive-enforcement";
|
|
@@ -3222,7 +3291,7 @@ function unpatchClaudeSettings(input) {
|
|
|
3222
3291
|
async function installClaudeHooksAtPath(settingsPath) {
|
|
3223
3292
|
let raw = null;
|
|
3224
3293
|
let created = false;
|
|
3225
|
-
if (
|
|
3294
|
+
if (existsSync11(settingsPath)) {
|
|
3226
3295
|
try {
|
|
3227
3296
|
raw = JSON.parse(await readFile6(settingsPath, "utf8"));
|
|
3228
3297
|
} catch {
|
|
@@ -3237,7 +3306,7 @@ async function installClaudeHooksAtPath(settingsPath) {
|
|
|
3237
3306
|
return { settingsPath, created };
|
|
3238
3307
|
}
|
|
3239
3308
|
async function uninstallClaudeHooksAtPath(settingsPath) {
|
|
3240
|
-
if (!
|
|
3309
|
+
if (!existsSync11(settingsPath)) {
|
|
3241
3310
|
return { settingsPath, created: false };
|
|
3242
3311
|
}
|
|
3243
3312
|
const raw = JSON.parse(await readFile6(settingsPath, "utf8"));
|
|
@@ -3308,7 +3377,7 @@ fi
|
|
|
3308
3377
|
async function installGitHooks(opts) {
|
|
3309
3378
|
const root = findProjectRoot8(opts.dir);
|
|
3310
3379
|
const gitDir = path12.join(root, ".git");
|
|
3311
|
-
if (!
|
|
3380
|
+
if (!existsSync12(gitDir)) {
|
|
3312
3381
|
ui.error(`No .git directory at ${root}.`);
|
|
3313
3382
|
process.exitCode = 1;
|
|
3314
3383
|
return;
|
|
@@ -3319,7 +3388,7 @@ async function installGitHooks(opts) {
|
|
|
3319
3388
|
let skipped = 0;
|
|
3320
3389
|
for (const { name, body } of HOOKS) {
|
|
3321
3390
|
const file = path12.join(hooksDir, name);
|
|
3322
|
-
if (
|
|
3391
|
+
if (existsSync12(file) && !opts.force) {
|
|
3323
3392
|
const existing = await readFile7(file, "utf8");
|
|
3324
3393
|
if (!existing.includes(HOOK_MARKER)) {
|
|
3325
3394
|
ui.warn(`${name} already exists and was not written by hAIve. Re-run with --force to overwrite.`);
|
|
@@ -3383,7 +3452,7 @@ function registerInstallHooks(program2) {
|
|
|
3383
3452
|
|
|
3384
3453
|
// src/commands/observe.ts
|
|
3385
3454
|
import { appendFile, mkdir as mkdir8 } from "fs/promises";
|
|
3386
|
-
import { existsSync as
|
|
3455
|
+
import { existsSync as existsSync13 } from "fs";
|
|
3387
3456
|
import path13 from "path";
|
|
3388
3457
|
import "commander";
|
|
3389
3458
|
import { findProjectRoot as findProjectRoot9, resolveHaivePaths as resolveHaivePaths7 } from "@hiveai/core";
|
|
@@ -3484,7 +3553,7 @@ function registerObserve(program2) {
|
|
|
3484
3553
|
})();
|
|
3485
3554
|
if (!root) return;
|
|
3486
3555
|
const paths = resolveHaivePaths7(root);
|
|
3487
|
-
if (!
|
|
3556
|
+
if (!existsSync13(paths.haiveDir)) return;
|
|
3488
3557
|
const failureHint = detectFailure(payload);
|
|
3489
3558
|
const observation = {
|
|
3490
3559
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3516,7 +3585,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3516
3585
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3517
3586
|
import { findProjectRoot as findProjectRoot10, resolveHaivePaths as resolveHaivePaths8 } from "@hiveai/core";
|
|
3518
3587
|
import { mkdir as mkdir9, writeFile as writeFile9 } from "fs/promises";
|
|
3519
|
-
import { existsSync as
|
|
3588
|
+
import { existsSync as existsSync14 } from "fs";
|
|
3520
3589
|
import path14 from "path";
|
|
3521
3590
|
import { z } from "zod";
|
|
3522
3591
|
import { readFile as readFile8, readdir as readdir2 } from "fs/promises";
|
|
@@ -3619,7 +3688,7 @@ import {
|
|
|
3619
3688
|
} from "@hiveai/core";
|
|
3620
3689
|
import { z as z13 } from "zod";
|
|
3621
3690
|
import { mkdir as mkdir32, writeFile as writeFile72 } from "fs/promises";
|
|
3622
|
-
import { existsSync as
|
|
3691
|
+
import { existsSync as existsSync142 } from "fs";
|
|
3623
3692
|
import path52 from "path";
|
|
3624
3693
|
import {
|
|
3625
3694
|
buildFrontmatter as buildFrontmatter22,
|
|
@@ -3672,7 +3741,7 @@ import {
|
|
|
3672
3741
|
isStackPackSeed as isStackPackSeed2,
|
|
3673
3742
|
literalMatchesAllTokens as literalMatchesAllTokens22,
|
|
3674
3743
|
literalMatchesAnyToken as literalMatchesAnyToken22,
|
|
3675
|
-
loadCodeMap as
|
|
3744
|
+
loadCodeMap as loadCodeMap5,
|
|
3676
3745
|
loadConfig as loadConfig3,
|
|
3677
3746
|
loadMemoriesFromDir as loadMemoriesFromDir13,
|
|
3678
3747
|
loadUsageIndex as loadUsageIndex7,
|
|
@@ -3795,7 +3864,7 @@ var BootstrapProjectSaveInputSchema = {
|
|
|
3795
3864
|
};
|
|
3796
3865
|
async function bootstrapProjectSave(input, ctx) {
|
|
3797
3866
|
const target = input.module ? path14.join(ctx.paths.modulesContextDir, input.module, "context.md") : ctx.paths.projectContext;
|
|
3798
|
-
const exists =
|
|
3867
|
+
const exists = existsSync14(target);
|
|
3799
3868
|
if (exists && !input.overwrite) {
|
|
3800
3869
|
throw new Error(
|
|
3801
3870
|
`${target} already exists. Pass overwrite=true to replace it.`
|
|
@@ -4675,7 +4744,7 @@ var MemTriedInputSchema = {
|
|
|
4675
4744
|
author: z14.string().optional().describe("Author handle or email")
|
|
4676
4745
|
};
|
|
4677
4746
|
async function memTried(input, ctx) {
|
|
4678
|
-
if (!
|
|
4747
|
+
if (!existsSync142(ctx.paths.haiveDir)) {
|
|
4679
4748
|
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
|
|
4680
4749
|
}
|
|
4681
4750
|
const slug = input.what.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 5).join("-");
|
|
@@ -4697,7 +4766,7 @@ async function memTried(input, ctx) {
|
|
|
4697
4766
|
const body = lines.join("\n") + "\n";
|
|
4698
4767
|
const file = memoryFilePath22(ctx.paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
4699
4768
|
await mkdir32(path52.dirname(file), { recursive: true });
|
|
4700
|
-
if (
|
|
4769
|
+
if (existsSync142(file)) {
|
|
4701
4770
|
throw new Error(`Memory already exists at ${file}`);
|
|
4702
4771
|
}
|
|
4703
4772
|
await writeFile72(file, serializeMemory6({ frontmatter, body }), "utf8");
|
|
@@ -5189,7 +5258,7 @@ async function getBriefing(input, ctx) {
|
|
|
5189
5258
|
if ((isTemplateContext || !existsSync18(ctx.paths.projectContext)) && input.include_project_context) {
|
|
5190
5259
|
const haiveConfig = await loadConfig3(ctx.paths);
|
|
5191
5260
|
if (haiveConfig.autoContext) {
|
|
5192
|
-
const codeMap = await
|
|
5261
|
+
const codeMap = await loadCodeMap5(ctx.paths);
|
|
5193
5262
|
if (codeMap) {
|
|
5194
5263
|
const totalFiles = Object.keys(codeMap.files).length;
|
|
5195
5264
|
const extensions = /* @__PURE__ */ new Map();
|
|
@@ -5317,7 +5386,7 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
5317
5386
|
}
|
|
5318
5387
|
}
|
|
5319
5388
|
if (symbolsToLookup.size > 0) {
|
|
5320
|
-
const codeMap = await
|
|
5389
|
+
const codeMap = await loadCodeMap5(ctx.paths);
|
|
5321
5390
|
if (codeMap) {
|
|
5322
5391
|
symbolLocations = [];
|
|
5323
5392
|
for (const sym of symbolsToLookup) {
|
|
@@ -5944,6 +6013,58 @@ var AntiPatternsCheckInputSchema = {
|
|
|
5944
6013
|
"When true, also use semantic search (requires @hiveai/embeddings + memory index) to find related anti-patterns."
|
|
5945
6014
|
)
|
|
5946
6015
|
};
|
|
6016
|
+
var CODE_STOPWORDS = /* @__PURE__ */ new Set([
|
|
6017
|
+
"import",
|
|
6018
|
+
"export",
|
|
6019
|
+
"function",
|
|
6020
|
+
"return",
|
|
6021
|
+
"const",
|
|
6022
|
+
"let",
|
|
6023
|
+
"var",
|
|
6024
|
+
"class",
|
|
6025
|
+
"public",
|
|
6026
|
+
"private",
|
|
6027
|
+
"protected",
|
|
6028
|
+
"static",
|
|
6029
|
+
"this",
|
|
6030
|
+
"true",
|
|
6031
|
+
"false",
|
|
6032
|
+
"null",
|
|
6033
|
+
"undefined",
|
|
6034
|
+
"void",
|
|
6035
|
+
"async",
|
|
6036
|
+
"await",
|
|
6037
|
+
"from",
|
|
6038
|
+
"type",
|
|
6039
|
+
"interface",
|
|
6040
|
+
"extends",
|
|
6041
|
+
"implements",
|
|
6042
|
+
"number",
|
|
6043
|
+
"string",
|
|
6044
|
+
"boolean",
|
|
6045
|
+
"value",
|
|
6046
|
+
"default",
|
|
6047
|
+
"case",
|
|
6048
|
+
"break",
|
|
6049
|
+
"continue",
|
|
6050
|
+
"throw",
|
|
6051
|
+
"catch",
|
|
6052
|
+
"finally",
|
|
6053
|
+
"else",
|
|
6054
|
+
"while",
|
|
6055
|
+
"for",
|
|
6056
|
+
"new",
|
|
6057
|
+
"super",
|
|
6058
|
+
"yield",
|
|
6059
|
+
"module",
|
|
6060
|
+
"require",
|
|
6061
|
+
"console"
|
|
6062
|
+
]);
|
|
6063
|
+
function tokenizeDiffForLiteral(diff) {
|
|
6064
|
+
const wsTokens = tokenizeQuery3(diff);
|
|
6065
|
+
const wordTokens = diff.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 4 && !CODE_STOPWORDS.has(t));
|
|
6066
|
+
return [.../* @__PURE__ */ new Set([...wsTokens, ...wordTokens])];
|
|
6067
|
+
}
|
|
5947
6068
|
async function antiPatternsCheck(input, ctx) {
|
|
5948
6069
|
if (!input.diff && input.paths.length === 0) {
|
|
5949
6070
|
return {
|
|
@@ -5997,7 +6118,7 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
5997
6118
|
}
|
|
5998
6119
|
}
|
|
5999
6120
|
if (input.diff) {
|
|
6000
|
-
const tokens =
|
|
6121
|
+
const tokens = tokenizeDiffForLiteral(input.diff);
|
|
6001
6122
|
if (tokens.length > 0) {
|
|
6002
6123
|
for (const { memory: memory2 } of negative) {
|
|
6003
6124
|
if (literalMatchesAnyToken3(memory2, tokens)) {
|
|
@@ -6450,7 +6571,10 @@ var PreCommitCheckInputSchema = {
|
|
|
6450
6571
|
block_on: z28.enum(["any", "high-confidence", "never"]).default("high-confidence").describe(
|
|
6451
6572
|
"When to set should_block=true: 'any' = any warning blocks; 'high-confidence' = only warnings from authoritative/trusted memories block; 'never' = report only, never block."
|
|
6452
6573
|
),
|
|
6453
|
-
semantic: z28.boolean().default(true).describe("Enable semantic search in anti_patterns_check (requires embeddings index).")
|
|
6574
|
+
semantic: z28.boolean().default(true).describe("Enable semantic search in anti_patterns_check (requires embeddings index)."),
|
|
6575
|
+
anchored_blocks: z28.boolean().default(false).describe(
|
|
6576
|
+
"When true, ALSO block a high-confidence anti-pattern (attempt/gotcha) that is anchored to a touched file AND corroborated by the diff (literal token overlap, or semantic >= 0.45) \u2014 not just very strong semantic matches. Powers the 'anchored' enforcement gate. Config/docs-only commits are still downgraded. Default false preserves the soft, semantic-only blocking behavior."
|
|
6577
|
+
)
|
|
6454
6578
|
};
|
|
6455
6579
|
async function preCommitCheck(input, ctx) {
|
|
6456
6580
|
if (!input.diff && input.paths.length === 0) {
|
|
@@ -6475,7 +6599,7 @@ async function preCommitCheck(input, ctx) {
|
|
|
6475
6599
|
const filesTouching = new Set(relevantMatches.map((m) => m.id));
|
|
6476
6600
|
const staleHits = verifyResult.results.filter((r) => r.stale && filesTouching.has(r.id));
|
|
6477
6601
|
const blockOn = input.block_on;
|
|
6478
|
-
const classifiedWarnings = apResult.warnings.map((warning) => classifyWarning(warning, input.paths));
|
|
6602
|
+
const classifiedWarnings = apResult.warnings.map((warning) => classifyWarning(warning, input.paths, input.anchored_blocks));
|
|
6479
6603
|
const blockingWarnings = classifiedWarnings.filter((w) => w.level === "blocking");
|
|
6480
6604
|
const reviewWarnings = classifiedWarnings.filter((w) => w.level === "review");
|
|
6481
6605
|
const infoWarnings = classifiedWarnings.filter((w) => w.level === "info");
|
|
@@ -6515,7 +6639,7 @@ async function preCommitCheck(input, ctx) {
|
|
|
6515
6639
|
})
|
|
6516
6640
|
};
|
|
6517
6641
|
}
|
|
6518
|
-
function classifyWarning(warning, paths) {
|
|
6642
|
+
function classifyWarning(warning, paths, anchoredBlocks = false) {
|
|
6519
6643
|
const affectedFiles = paths.filter((p) => !p.startsWith(".ai/.usage/"));
|
|
6520
6644
|
const repairCommand = repairCommandForWarning(warning, affectedFiles);
|
|
6521
6645
|
const fileDowngrade = fileTypeDowngradeReason(warning, affectedFiles);
|
|
@@ -6540,6 +6664,15 @@ function classifyWarning(warning, paths) {
|
|
|
6540
6664
|
const hasSemantic = warning.reasons.includes("semantic");
|
|
6541
6665
|
const semanticScore = warning.semantic_score ?? 0;
|
|
6542
6666
|
const highConfidence = warning.confidence === "authoritative" || warning.confidence === "trusted";
|
|
6667
|
+
if (anchoredBlocks && highConfidence && warning.reasons.includes("anchor") && (warning.reasons.includes("literal") || hasSemantic && semanticScore >= 0.45)) {
|
|
6668
|
+
return {
|
|
6669
|
+
...warning,
|
|
6670
|
+
level: "blocking",
|
|
6671
|
+
rationale: "high-confidence anti-pattern anchored to a touched file and corroborated by the diff (anchored gate)",
|
|
6672
|
+
affected_files: affectedFiles,
|
|
6673
|
+
repair_command: repairCommand
|
|
6674
|
+
};
|
|
6675
|
+
}
|
|
6543
6676
|
if (hasSemantic && semanticScore >= 0.45 || highConfidence && warning.reasons.includes("anchor") && warning.reasons.includes("literal")) {
|
|
6544
6677
|
return {
|
|
6545
6678
|
...warning,
|
|
@@ -7190,7 +7323,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
7190
7323
|
};
|
|
7191
7324
|
}
|
|
7192
7325
|
var SERVER_NAME = "haive";
|
|
7193
|
-
var SERVER_VERSION = "0.9.
|
|
7326
|
+
var SERVER_VERSION = "0.9.30";
|
|
7194
7327
|
function jsonResult(data) {
|
|
7195
7328
|
return {
|
|
7196
7329
|
content: [
|
|
@@ -8172,7 +8305,7 @@ import {
|
|
|
8172
8305
|
isAutoPromoteEligible as isAutoPromoteEligible2,
|
|
8173
8306
|
isDecaying as isDecaying2,
|
|
8174
8307
|
isStackPackSeed as isStackPackSeed3,
|
|
8175
|
-
loadCodeMap as
|
|
8308
|
+
loadCodeMap as loadCodeMap6,
|
|
8176
8309
|
loadConfig as loadConfig4,
|
|
8177
8310
|
loadMemoriesFromDir as loadMemoriesFromDir23,
|
|
8178
8311
|
loadUsageIndex as loadUsageIndex12,
|
|
@@ -8543,7 +8676,7 @@ Attends une **confirmation explicite** avant d'agir.
|
|
|
8543
8676
|
ui.warn(`contract watcher failed: ${String(err)}`);
|
|
8544
8677
|
}
|
|
8545
8678
|
}
|
|
8546
|
-
const existingMap = await
|
|
8679
|
+
const existingMap = await loadCodeMap6(paths);
|
|
8547
8680
|
if (!dryRun && !existingMap && (config.autopilot || autoRepair.codeMap)) {
|
|
8548
8681
|
try {
|
|
8549
8682
|
const { buildCodeMap: buildCodeMap4, saveCodeMap: saveCodeMap4 } = await import("@hiveai/core");
|
|
@@ -8737,7 +8870,7 @@ function registerMemoryAdd(memory2) {
|
|
|
8737
8870
|
haive memory add --type convention --slug flyway-no-modify --topic flyway \\\\
|
|
8738
8871
|
--scope team --body "Never modify existing migrations. Create V{n+1}__desc.sql."
|
|
8739
8872
|
`
|
|
8740
|
-
).requiredOption("--type <type>", "skill | convention | decision | gotcha | architecture | glossary | attempt").option("--slug <slug>", "short kebab-case identifier used in the file name (auto-derived from --title/--body when omitted)").option("--title <text>", "memory title \u2014 becomes the first heading of the body").option("--scope <scope>", "personal | team | module (default: config default; team in autopilot)").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) => {
|
|
8873
|
+
).requiredOption("--type <type>", "skill | convention | decision | gotcha | architecture | glossary | attempt").option("--slug <slug>", "short kebab-case identifier used in the file name (auto-derived from --title/--body when omitted)").option("--title <text>", "memory title \u2014 becomes the first heading of the body").option("--scope <scope>", "personal | team | module (default: config default; team in autopilot)").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("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").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) => {
|
|
8741
8874
|
const root = findProjectRoot13(opts.dir);
|
|
8742
8875
|
const paths = resolveHaivePaths10(root);
|
|
8743
8876
|
if (!existsSync30(paths.haiveDir)) {
|
|
@@ -8747,7 +8880,7 @@ function registerMemoryAdd(memory2) {
|
|
|
8747
8880
|
}
|
|
8748
8881
|
const config = await loadConfig5(paths);
|
|
8749
8882
|
const userTags = parseCsv2(opts.tags);
|
|
8750
|
-
const anchorPaths = parseCsv2(opts.paths);
|
|
8883
|
+
const anchorPaths = parseCsv2(opts.paths ?? opts.files);
|
|
8751
8884
|
const autoTagsEnabled = opts.autoTag !== false;
|
|
8752
8885
|
const inferredTags = autoTagsEnabled ? inferModulesFromPaths3(anchorPaths) : [];
|
|
8753
8886
|
const mergedTags = Array.from(/* @__PURE__ */ new Set([...userTags, ...inferredTags]));
|
|
@@ -9156,7 +9289,7 @@ import {
|
|
|
9156
9289
|
serializeMemory as serializeMemory15
|
|
9157
9290
|
} from "@hiveai/core";
|
|
9158
9291
|
function registerMemoryUpdate(memory2) {
|
|
9159
|
-
memory2.command("update <id>").description("Update body, tags, or anchor of an existing memory (preserves id and usage history)").option("--type <type>", "change the memory type (convention | decision | gotcha | architecture | glossary | skill | attempt)").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("--body-file <path>", "read new body from a Markdown file \u2014 for long content").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) => {
|
|
9292
|
+
memory2.command("update <id>").description("Update body, tags, or anchor of an existing memory (preserves id and usage history)").option("--type <type>", "change the memory type (convention | decision | gotcha | architecture | glossary | skill | attempt)").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("--body-file <path>", "read new body from a Markdown file \u2014 for long content").option("--tags <csv>", "new tags, comma-separated \u2014 fully replaces existing tags").option("--paths <csv>", "new anchor paths, comma-separated").option("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").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) => {
|
|
9160
9293
|
const root = findProjectRoot17(opts.dir);
|
|
9161
9294
|
const paths = resolveHaivePaths14(root);
|
|
9162
9295
|
if (!existsSync35(paths.memoriesDir)) {
|
|
@@ -9174,8 +9307,9 @@ function registerMemoryUpdate(memory2) {
|
|
|
9174
9307
|
const updated = [];
|
|
9175
9308
|
const { frontmatter, body } = loaded.memory;
|
|
9176
9309
|
const newAnchor = { ...frontmatter.anchor };
|
|
9177
|
-
|
|
9178
|
-
|
|
9310
|
+
const pathsOpt = opts.paths ?? opts.files;
|
|
9311
|
+
if (pathsOpt !== void 0) {
|
|
9312
|
+
newAnchor.paths = parseCsv3(pathsOpt);
|
|
9179
9313
|
updated.push("anchor.paths");
|
|
9180
9314
|
}
|
|
9181
9315
|
if (opts.symbols !== void 0) {
|
|
@@ -9570,7 +9704,7 @@ function registerMemoryTried(memory2) {
|
|
|
9570
9704
|
--instead "use static import in the entry file" \\\\
|
|
9571
9705
|
--paths packages/cli/src/index.ts
|
|
9572
9706
|
`
|
|
9573
|
-
).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", "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) => {
|
|
9707
|
+
).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", "personal").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags").option("--paths <csv>", "anchor paths, comma-separated").option("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").option("--author <author>", "author email or handle").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
9574
9708
|
const root = findProjectRoot22(opts.dir);
|
|
9575
9709
|
const paths = resolveHaivePaths19(root);
|
|
9576
9710
|
if (!existsSync40(paths.haiveDir)) {
|
|
@@ -9585,7 +9719,7 @@ function registerMemoryTried(memory2) {
|
|
|
9585
9719
|
scope: opts.scope,
|
|
9586
9720
|
module: opts.module,
|
|
9587
9721
|
tags: parseCsv4(opts.tags),
|
|
9588
|
-
paths: parseCsv4(opts.paths),
|
|
9722
|
+
paths: parseCsv4(opts.paths ?? opts.files),
|
|
9589
9723
|
author: opts.author
|
|
9590
9724
|
});
|
|
9591
9725
|
const frontmatter = { ...baseFm, status: "validated" };
|
|
@@ -10075,44 +10209,64 @@ import {
|
|
|
10075
10209
|
function registerMemoryVerify(memory2) {
|
|
10076
10210
|
memory2.command("verify").description(
|
|
10077
10211
|
"Check that memory anchor paths still exist in the current codebase.\n\n A memory is 'stale' when its anchored file or symbol was moved, deleted, or renamed.\n Stale memories are shown with a warning in get_briefing and should be updated or deleted.\n\n haive sync runs this automatically. Use this command for on-demand checks or in CI.\n\n CI recommendation: add 'haive memory verify' to your haive-sync.yml PR check job\n to catch stale memories before they reach main.\n\n Examples:\n haive memory verify # check all, report only\n haive memory verify --update # mark stale/fresh on disk\n haive memory verify --id 2026-04-28-gotcha-x # check one memory\n"
|
|
10078
|
-
).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) => {
|
|
10212
|
+
).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("--json", "emit machine-readable JSON (for CI / agents)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10079
10213
|
const root = findProjectRoot30(opts.dir);
|
|
10080
10214
|
const paths = resolveHaivePaths27(root);
|
|
10081
10215
|
if (!existsSync49(paths.memoriesDir)) {
|
|
10082
|
-
|
|
10216
|
+
if (opts.json) {
|
|
10217
|
+
console.log(JSON.stringify({ error: "not-initialized", root }, null, 2));
|
|
10218
|
+
} else {
|
|
10219
|
+
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
10220
|
+
}
|
|
10083
10221
|
process.exitCode = 1;
|
|
10084
10222
|
return;
|
|
10085
10223
|
}
|
|
10086
10224
|
const all = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
10087
10225
|
const targets = opts.id ? all.filter((m) => m.memory.frontmatter.id === opts.id) : all;
|
|
10088
10226
|
if (opts.id && targets.length === 0) {
|
|
10089
|
-
|
|
10227
|
+
if (opts.json) {
|
|
10228
|
+
console.log(JSON.stringify({ error: "not-found", id: opts.id }, null, 2));
|
|
10229
|
+
} else {
|
|
10230
|
+
ui.error(`No memory with id "${opts.id}".`);
|
|
10231
|
+
}
|
|
10090
10232
|
process.exitCode = 1;
|
|
10091
10233
|
return;
|
|
10092
10234
|
}
|
|
10093
10235
|
let staleCount = 0;
|
|
10094
10236
|
let freshCount = 0;
|
|
10095
10237
|
const anchorlessIds = [];
|
|
10238
|
+
const entries = [];
|
|
10096
10239
|
let updated = 0;
|
|
10097
10240
|
for (const { memory: mem, filePath } of targets) {
|
|
10098
10241
|
const result = await verifyAnchor3(mem, { projectRoot: root });
|
|
10099
10242
|
const isAnchored = mem.frontmatter.anchor.paths.length > 0 || mem.frontmatter.anchor.symbols.length > 0;
|
|
10243
|
+
const rel = path34.relative(root, filePath);
|
|
10100
10244
|
if (!isAnchored) {
|
|
10101
10245
|
anchorlessIds.push(mem.frontmatter.id);
|
|
10246
|
+
entries.push({ id: mem.frontmatter.id, status: "anchorless", path: rel });
|
|
10102
10247
|
continue;
|
|
10103
10248
|
}
|
|
10104
|
-
const rel = path34.relative(root, filePath);
|
|
10105
10249
|
if (result.stale) {
|
|
10106
10250
|
staleCount++;
|
|
10107
|
-
|
|
10108
|
-
|
|
10109
|
-
|
|
10110
|
-
|
|
10111
|
-
|
|
10251
|
+
entries.push({
|
|
10252
|
+
id: mem.frontmatter.id,
|
|
10253
|
+
status: "stale",
|
|
10254
|
+
path: rel,
|
|
10255
|
+
reason: result.reason ?? void 0,
|
|
10256
|
+
possible_renames: result.possibleRenames
|
|
10257
|
+
});
|
|
10258
|
+
if (!opts.json) {
|
|
10259
|
+
console.log(`${ui.bold("STALE")} ${mem.frontmatter.id}`);
|
|
10260
|
+
console.log(` ${ui.dim(rel)}`);
|
|
10261
|
+
console.log(` ${result.reason}`);
|
|
10262
|
+
if (result.possibleRenames.length > 0) {
|
|
10263
|
+
console.log(` ${ui.yellow("Possible renames:")} ${result.possibleRenames.join(", ")}`);
|
|
10264
|
+
}
|
|
10112
10265
|
}
|
|
10113
10266
|
} else {
|
|
10114
10267
|
freshCount++;
|
|
10115
|
-
|
|
10268
|
+
entries.push({ id: mem.frontmatter.id, status: "fresh", path: rel });
|
|
10269
|
+
if (!opts.json) console.log(`${ui.dim("fresh")} ${mem.frontmatter.id}`);
|
|
10116
10270
|
}
|
|
10117
10271
|
if (opts.update) {
|
|
10118
10272
|
const next = applyVerification2(mem, result);
|
|
@@ -10120,6 +10274,20 @@ function registerMemoryVerify(memory2) {
|
|
|
10120
10274
|
updated++;
|
|
10121
10275
|
}
|
|
10122
10276
|
}
|
|
10277
|
+
if (opts.json) {
|
|
10278
|
+
console.log(JSON.stringify({
|
|
10279
|
+
summary: {
|
|
10280
|
+
checked: freshCount + staleCount,
|
|
10281
|
+
fresh: freshCount,
|
|
10282
|
+
stale: staleCount,
|
|
10283
|
+
anchorless: anchorlessIds.length,
|
|
10284
|
+
updated
|
|
10285
|
+
},
|
|
10286
|
+
results: entries
|
|
10287
|
+
}, null, 2));
|
|
10288
|
+
if (staleCount > 0) process.exitCode = 1;
|
|
10289
|
+
return;
|
|
10290
|
+
}
|
|
10123
10291
|
const summary = [
|
|
10124
10292
|
`${freshCount} fresh`,
|
|
10125
10293
|
`${staleCount} stale`,
|
|
@@ -11982,7 +12150,7 @@ function parseDays(input) {
|
|
|
11982
12150
|
}
|
|
11983
12151
|
|
|
11984
12152
|
// src/commands/doctor.ts
|
|
11985
|
-
import { existsSync as existsSync61, statSync } from "fs";
|
|
12153
|
+
import { existsSync as existsSync61, statSync as statSync2 } from "fs";
|
|
11986
12154
|
import { readFile as readFile20, stat, writeFile as writeFile31 } from "fs/promises";
|
|
11987
12155
|
import path45 from "path";
|
|
11988
12156
|
import { execFileSync, execSync as execSync3 } from "child_process";
|
|
@@ -11991,7 +12159,7 @@ import {
|
|
|
11991
12159
|
codeMapPath as codeMapPath2,
|
|
11992
12160
|
findProjectRoot as findProjectRoot42,
|
|
11993
12161
|
getUsage as getUsage19,
|
|
11994
|
-
loadCodeMap as
|
|
12162
|
+
loadCodeMap as loadCodeMap7,
|
|
11995
12163
|
loadConfig as loadConfig10,
|
|
11996
12164
|
loadMemoriesFromDir as loadMemoriesFromDir33,
|
|
11997
12165
|
loadUsageIndex as loadUsageIndex25,
|
|
@@ -12140,7 +12308,7 @@ function registerDoctor(program2) {
|
|
|
12140
12308
|
fix: "haive memory lint --fix --apply"
|
|
12141
12309
|
});
|
|
12142
12310
|
}
|
|
12143
|
-
const codeMap = await
|
|
12311
|
+
const codeMap = await loadCodeMap7(paths);
|
|
12144
12312
|
if (!codeMap) {
|
|
12145
12313
|
findings.push({
|
|
12146
12314
|
severity: "warn",
|
|
@@ -12227,14 +12395,14 @@ function registerDoctor(program2) {
|
|
|
12227
12395
|
fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
|
|
12228
12396
|
});
|
|
12229
12397
|
}
|
|
12230
|
-
findings.push(...await collectInstallFindings(root, "0.9.
|
|
12398
|
+
findings.push(...await collectInstallFindings(root, "0.9.30"));
|
|
12231
12399
|
try {
|
|
12232
12400
|
const legacyRaw = execSync3("haive-mcp --version", {
|
|
12233
12401
|
encoding: "utf8",
|
|
12234
12402
|
timeout: 3e3,
|
|
12235
12403
|
stdio: ["ignore", "pipe", "ignore"]
|
|
12236
12404
|
}).trim();
|
|
12237
|
-
const cliVersion = "0.9.
|
|
12405
|
+
const cliVersion = "0.9.30";
|
|
12238
12406
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
12239
12407
|
findings.push({
|
|
12240
12408
|
severity: "warn",
|
|
@@ -12685,7 +12853,7 @@ function extractAbsoluteHaiveBins(text) {
|
|
|
12685
12853
|
const p = match[2];
|
|
12686
12854
|
if (!p) continue;
|
|
12687
12855
|
try {
|
|
12688
|
-
if (
|
|
12856
|
+
if (statSync2(p).isDirectory()) continue;
|
|
12689
12857
|
} catch {
|
|
12690
12858
|
}
|
|
12691
12859
|
out.add(p);
|
|
@@ -12824,7 +12992,10 @@ function registerPrecommit(program2) {
|
|
|
12824
12992
|
"--block-on <mode>",
|
|
12825
12993
|
"'any' | 'high-confidence' (default) | 'never' (report only)",
|
|
12826
12994
|
"high-confidence"
|
|
12827
|
-
).option("--no-semantic", "disable semantic search in anti-patterns matching").option(
|
|
12995
|
+
).option("--no-semantic", "disable semantic search in anti-patterns matching").option(
|
|
12996
|
+
"--no-anchored-blocks",
|
|
12997
|
+
"do not block on anchored, diff-corroborated anti-patterns (only block on very strong semantic matches)"
|
|
12998
|
+
).option("--json", "emit JSON instead of human-readable output", false).option("--paths <paths...>", "explicit paths to check (skips git diff)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
12828
12999
|
const root = findProjectRoot44(opts.dir);
|
|
12829
13000
|
const paths = resolveHaivePaths40(root);
|
|
12830
13001
|
const ctx = { paths };
|
|
@@ -12866,6 +13037,7 @@ function registerPrecommit(program2) {
|
|
|
12866
13037
|
diff: diff || void 0,
|
|
12867
13038
|
paths: touchedPaths,
|
|
12868
13039
|
block_on: opts.blockOn ?? "high-confidence",
|
|
13040
|
+
anchored_blocks: opts.anchoredBlocks !== false,
|
|
12869
13041
|
semantic: opts.noSemantic ? false : true
|
|
12870
13042
|
}, ctx);
|
|
12871
13043
|
if (opts.json) {
|
|
@@ -13189,7 +13361,7 @@ function registerMemoryConflictCandidates(memory2) {
|
|
|
13189
13361
|
|
|
13190
13362
|
// src/commands/enforce.ts
|
|
13191
13363
|
import { execFileSync as execFileSync2, spawn as spawn6 } from "child_process";
|
|
13192
|
-
import { existsSync as existsSync68, statSync as
|
|
13364
|
+
import { existsSync as existsSync68, statSync as statSync3 } from "fs";
|
|
13193
13365
|
import { chmod as chmod2, mkdir as mkdir19, readFile as readFile21, readdir as readdir6, rm as rm3, writeFile as writeFile33 } from "fs/promises";
|
|
13194
13366
|
import path50 from "path";
|
|
13195
13367
|
import "commander";
|
|
@@ -13521,7 +13693,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
13521
13693
|
findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
|
|
13522
13694
|
});
|
|
13523
13695
|
}
|
|
13524
|
-
findings.push(...await inspectIntegrationVersions(root, "0.9.
|
|
13696
|
+
findings.push(...await inspectIntegrationVersions(root, "0.9.30"));
|
|
13525
13697
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
13526
13698
|
const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
|
|
13527
13699
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
|
|
@@ -13555,7 +13727,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
13555
13727
|
findings.push(...await verifyDecisionCoverage(paths, stage, sessionId));
|
|
13556
13728
|
}
|
|
13557
13729
|
if (stage === "pre-commit" || stage === "ci") {
|
|
13558
|
-
findings.push(...await runPrecommitPolicy(paths));
|
|
13730
|
+
findings.push(...await runPrecommitPolicy(paths, config.enforcement?.antiPatternGate ?? "anchored"));
|
|
13559
13731
|
}
|
|
13560
13732
|
if (config.enforcement?.cleanupGeneratedArtifacts !== false) {
|
|
13561
13733
|
findings.push(...await findGeneratedArtifacts(paths));
|
|
@@ -13679,7 +13851,10 @@ async function verifyDecisionCoverage(paths, stage, sessionId) {
|
|
|
13679
13851
|
impact: Math.min(35, 10 + missing.length * 5)
|
|
13680
13852
|
}];
|
|
13681
13853
|
}
|
|
13682
|
-
async function runPrecommitPolicy(paths) {
|
|
13854
|
+
async function runPrecommitPolicy(paths, gate) {
|
|
13855
|
+
if (gate === "off") {
|
|
13856
|
+
return [{ severity: "info", code: "precommit-policy-off", message: "Anti-pattern gate is disabled (enforcement.antiPatternGate=off)." }];
|
|
13857
|
+
}
|
|
13683
13858
|
const staged = await runCommand4("git", ["diff", "--cached", "--name-only"], paths.root).catch(() => "");
|
|
13684
13859
|
const touchedPaths = staged.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
13685
13860
|
if (touchedPaths.length === 0) {
|
|
@@ -13689,7 +13864,8 @@ async function runPrecommitPolicy(paths) {
|
|
|
13689
13864
|
const result = await preCommitCheck({
|
|
13690
13865
|
diff,
|
|
13691
13866
|
paths: touchedPaths,
|
|
13692
|
-
block_on: "high-confidence",
|
|
13867
|
+
block_on: gate === "strict" ? "any" : "high-confidence",
|
|
13868
|
+
anchored_blocks: gate === "anchored",
|
|
13693
13869
|
semantic: true
|
|
13694
13870
|
}, { paths });
|
|
13695
13871
|
if (!result.should_block) {
|
|
@@ -13827,7 +14003,7 @@ function extractAbsoluteHaiveBins2(text) {
|
|
|
13827
14003
|
const p = match[2];
|
|
13828
14004
|
if (!p) continue;
|
|
13829
14005
|
try {
|
|
13830
|
-
if (
|
|
14006
|
+
if (statSync3(p).isDirectory()) continue;
|
|
13831
14007
|
} catch {
|
|
13832
14008
|
}
|
|
13833
14009
|
out.add(p);
|
|
@@ -14128,7 +14304,7 @@ function registerRun(program2) {
|
|
|
14128
14304
|
|
|
14129
14305
|
// src/index.ts
|
|
14130
14306
|
var program = new Command52();
|
|
14131
|
-
program.name("haive").description("hAIve \u2014 the memory and enforcement layer of your agent harness").version("0.9.
|
|
14307
|
+
program.name("haive").description("hAIve \u2014 the memory and enforcement layer of your agent harness").version("0.9.30").option("--advanced", "show maintenance and experimental commands in help");
|
|
14132
14308
|
registerInit(program);
|
|
14133
14309
|
registerWelcome(program);
|
|
14134
14310
|
registerResolveProject(program);
|