@hivelore/mcp 0.38.0 → 0.39.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/index.js +128 -65
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +27 -8
- package/dist/server.js +136 -64
- package/dist/server.js.map +1 -1
- package/package.json +3 -3
package/dist/server.d.ts
CHANGED
|
@@ -552,7 +552,7 @@ declare const MemTriedInputSchema: {
|
|
|
552
552
|
what: z.ZodString;
|
|
553
553
|
why_failed: z.ZodString;
|
|
554
554
|
instead: z.ZodOptional<z.ZodString>;
|
|
555
|
-
scope: z.
|
|
555
|
+
scope: z.ZodOptional<z.ZodEnum<["personal", "team", "module"]>>;
|
|
556
556
|
module: z.ZodOptional<z.ZodString>;
|
|
557
557
|
tags: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
558
558
|
paths: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
@@ -619,16 +619,22 @@ interface MemTriedOutput {
|
|
|
619
619
|
}
|
|
620
620
|
declare function memTried(input: MemTriedInput, ctx: HaiveContext): Promise<MemTriedOutput>;
|
|
621
621
|
|
|
622
|
-
/**
|
|
623
|
-
* Detect the test framework + owning package dir (repo-relative) for an incident's anchor paths.
|
|
624
|
-
* Walks up from each anchor path's directory to the repo root and uses the NEAREST enclosing manifest
|
|
625
|
-
* (package.json / go.mod / a python signal); falls back to the repo root with a vitest default. This
|
|
626
|
-
* is FS I/O, so it lives in the MCP layer (imported by the CLI too) rather than in pure core.
|
|
627
|
-
*/
|
|
628
622
|
declare function detectTestFrameworkForPaths(root: string, anchorPaths: string[]): Promise<{
|
|
629
623
|
framework: TestFramework;
|
|
630
624
|
baseDir: string;
|
|
631
625
|
}>;
|
|
626
|
+
interface AnchorFrameworkGroup {
|
|
627
|
+
framework: TestFramework;
|
|
628
|
+
baseDir: string;
|
|
629
|
+
/** The anchor paths owned by this package (subset of the lesson's anchors). */
|
|
630
|
+
anchors: string[];
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Group a lesson's anchor paths by OWNING package: one entry per distinct enclosing manifest dir,
|
|
634
|
+
* in first-anchor order. A lesson that spans several packages gets one scaffold per package instead
|
|
635
|
+
* of "first anchor wins". Anchors with no enclosing manifest fall back to the repo root + vitest.
|
|
636
|
+
*/
|
|
637
|
+
declare function detectTestFrameworksForAnchors(root: string, anchorPaths: string[]): Promise<AnchorFrameworkGroup[]>;
|
|
632
638
|
declare const ScaffoldTestInputSchema: {
|
|
633
639
|
memory_id: z.ZodString;
|
|
634
640
|
framework: z.ZodOptional<z.ZodEnum<["vitest", "jest", "pytest", "gotest"]>>;
|
|
@@ -653,6 +659,19 @@ interface ScaffoldTestOutput {
|
|
|
653
659
|
written?: boolean;
|
|
654
660
|
already_exists?: boolean;
|
|
655
661
|
notice?: string;
|
|
662
|
+
/**
|
|
663
|
+
* One entry per generated test. A lesson whose anchors span several packages scaffolds one test
|
|
664
|
+
* per OWNING package; they share a single propose_command (a memory carries one sensor) whose
|
|
665
|
+
* command chains every run command.
|
|
666
|
+
*/
|
|
667
|
+
scaffolds?: Array<{
|
|
668
|
+
framework: TestFramework;
|
|
669
|
+
path: string;
|
|
670
|
+
run_command: string;
|
|
671
|
+
content: string;
|
|
672
|
+
written: boolean;
|
|
673
|
+
already_exists: boolean;
|
|
674
|
+
}>;
|
|
656
675
|
}
|
|
657
676
|
declare function scaffoldTest(input: ScaffoldTestInput, ctx: HaiveContext): Promise<ScaffoldTestOutput>;
|
|
658
677
|
|
|
@@ -761,4 +780,4 @@ declare function runHaiveMcpStdio(options: {
|
|
|
761
780
|
root?: string;
|
|
762
781
|
}): Promise<void>;
|
|
763
782
|
|
|
764
|
-
export { type AntiPatternsCheckInput, type AntiPatternsCheckOutput, type BriefingOutput, type CodeMapInput, type CodeMapToolOutput, type CodeSearchInput, type CodeSearchOutput, ENFORCEMENT_PROFILE_TOOLS, EXPERIMENTAL_PROFILE_TOOLS, type GetBriefingInput, type GetRecapInput, type GetRecapOutput, MAINTENANCE_PROFILE_TOOLS, type MemConflictCandidatesInput, type MemDistillInput, type MemDistillOutput, type MemRelevantToInput, type MemRelevantToOutput, type MemResolveProjectInput, type MemSuggestTopicInput, type MemTimelineInput, type MemTriedOutput, type PreCommitCheckInput, type PreCommitCheckOutput, type ProposeSensorOutput, SERVER_NAME, SERVER_VERSION, type ScaffoldTestOutput, TOOL_PROFILES, type ToolProfile, antiPatternsCheck, codeMapTool, codeSearch, createHaiveServer, detectTestFrameworkForPaths, getAllowedToolsForProfile, getBriefing, getRecap, memConflictCandidates, memDistill, memRelevantTo, memResolveProject, memSuggestTopic, memTimeline, memTried, parseMcpCliArgs, preCommitCheck, printHaiveMcpVersion, proposeSensor, readPresumedCorrectTargets, runHaiveMcpStdio, scaffoldTest };
|
|
783
|
+
export { type AnchorFrameworkGroup, type AntiPatternsCheckInput, type AntiPatternsCheckOutput, type BriefingOutput, type CodeMapInput, type CodeMapToolOutput, type CodeSearchInput, type CodeSearchOutput, ENFORCEMENT_PROFILE_TOOLS, EXPERIMENTAL_PROFILE_TOOLS, type GetBriefingInput, type GetRecapInput, type GetRecapOutput, MAINTENANCE_PROFILE_TOOLS, type MemConflictCandidatesInput, type MemDistillInput, type MemDistillOutput, type MemRelevantToInput, type MemRelevantToOutput, type MemResolveProjectInput, type MemSuggestTopicInput, type MemTimelineInput, type MemTriedOutput, type PreCommitCheckInput, type PreCommitCheckOutput, type ProposeSensorOutput, SERVER_NAME, SERVER_VERSION, type ScaffoldTestOutput, TOOL_PROFILES, type ToolProfile, antiPatternsCheck, codeMapTool, codeSearch, createHaiveServer, detectTestFrameworkForPaths, detectTestFrameworksForAnchors, getAllowedToolsForProfile, getBriefing, getRecap, memConflictCandidates, memDistill, memRelevantTo, memResolveProject, memSuggestTopic, memTimeline, memTried, parseMcpCliArgs, preCommitCheck, printHaiveMcpVersion, proposeSensor, readPresumedCorrectTargets, runHaiveMcpStdio, scaffoldTest };
|
package/dist/server.js
CHANGED
|
@@ -1138,6 +1138,8 @@ import { existsSync as existsSync15 } from "fs";
|
|
|
1138
1138
|
import path5 from "path";
|
|
1139
1139
|
import {
|
|
1140
1140
|
extractSensorExamples,
|
|
1141
|
+
extractTestFilePathsFromCommand,
|
|
1142
|
+
hasPendingTestMarker,
|
|
1141
1143
|
judgeProposedSensor,
|
|
1142
1144
|
loadMemoriesFromDir as loadMemoriesFromDir13,
|
|
1143
1145
|
serializeMemory as serializeMemory7
|
|
@@ -1259,7 +1261,26 @@ async function proposeSensor(input, ctx) {
|
|
|
1259
1261
|
if (!found) {
|
|
1260
1262
|
throw new Error(`No memory found with id ${input.memory_id}`);
|
|
1261
1263
|
}
|
|
1264
|
+
const personalScopeNudge = found.memory.frontmatter.scope === "personal" ? ` Note: this lesson is personal-scoped, so the sensor guards only YOUR machine (personal memories are gitignored). Promote it so the gate travels with the repo: hivelore memory promote ${input.memory_id}.` : "";
|
|
1262
1265
|
if (kind !== "regex") {
|
|
1266
|
+
const referencedTests = extractTestFilePathsFromCommand(input.command.trim()).filter((rel) => existsSync15(path5.resolve(ctx.paths.root, rel)));
|
|
1267
|
+
const pendingTests = [];
|
|
1268
|
+
for (const rel of referencedTests) {
|
|
1269
|
+
try {
|
|
1270
|
+
if (hasPendingTestMarker(await readFile3(path5.resolve(ctx.paths.root, rel), "utf8"))) pendingTests.push(rel);
|
|
1271
|
+
} catch {
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
if (pendingTests.length > 0 && input.severity === "block") {
|
|
1275
|
+
return {
|
|
1276
|
+
accepted: false,
|
|
1277
|
+
memory_id: input.memory_id,
|
|
1278
|
+
severity: input.severity,
|
|
1279
|
+
reason: "oracle-pending",
|
|
1280
|
+
guidance: `The routed test is still a PENDING stub (${pendingTests.join(", ")}) \u2014 it passes on anything, so the sensor would enforce nothing while reporting protection. Write the assertion (RED on the incident, GREEN once fixed), run it, then re-propose.`,
|
|
1281
|
+
self_check: { silent_on_current: false, fires_on_bad: null, fired_on: pendingTests }
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1263
1284
|
const verdictCmd = runCommandForValidation(input.command.trim(), ctx.paths.root, input.timeout_ms);
|
|
1264
1285
|
const anchorPathsCmd = input.paths.length > 0 ? input.paths : found.memory.frontmatter.anchor.paths;
|
|
1265
1286
|
if (verdictCmd.status !== "passed" && input.severity === "block") {
|
|
@@ -1293,7 +1314,7 @@ ${verdictCmd.detail}`,
|
|
|
1293
1314
|
accepted: true,
|
|
1294
1315
|
memory_id: input.memory_id,
|
|
1295
1316
|
severity: input.severity,
|
|
1296
|
-
guidance: verdictCmd.status === "passed" ? "Command oracle passes on the current tree; the gate now runs it when the diff touches the sensor's paths (requires enforcement.runCommandSensors=true)." : `Accepted at warn severity, but note: ${verdictCmd.status} on the current tree (${verdictCmd.detail})
|
|
1317
|
+
guidance: (verdictCmd.status === "passed" ? "Command oracle passes on the current tree; the gate now runs it when the diff touches the sensor's paths (requires enforcement.runCommandSensors=true)." : `Accepted at warn severity, but note: ${verdictCmd.status} on the current tree (${verdictCmd.detail}).`) + (pendingTests.length > 0 ? ` Note: the routed test is still a PENDING stub (${pendingTests.join(", ")}) \u2014 it passes on anything; write the assertion to make this oracle real.` : "") + personalScopeNudge,
|
|
1297
1318
|
self_check: { silent_on_current: verdictCmd.status === "passed", fires_on_bad: null, fired_on: [] }
|
|
1298
1319
|
};
|
|
1299
1320
|
}
|
|
@@ -1342,6 +1363,7 @@ ${verdictCmd.detail}`,
|
|
|
1342
1363
|
accepted: true,
|
|
1343
1364
|
memory_id: input.memory_id,
|
|
1344
1365
|
severity: input.severity,
|
|
1366
|
+
...personalScopeNudge ? { guidance: personalScopeNudge.trim() } : {},
|
|
1345
1367
|
self_check,
|
|
1346
1368
|
file_path: found.filePath
|
|
1347
1369
|
};
|
|
@@ -1352,7 +1374,9 @@ var MemTriedInputSchema = {
|
|
|
1352
1374
|
what: z16.string().min(1).describe("Brief description of the approach that was tried"),
|
|
1353
1375
|
why_failed: z16.string().min(1).describe("Why it failed or why it should NOT be used"),
|
|
1354
1376
|
instead: z16.string().optional().describe("What to use or do instead (recommended alternative)"),
|
|
1355
|
-
scope: z16.enum(["personal", "team", "module"]).
|
|
1377
|
+
scope: z16.enum(["personal", "team", "module"]).optional().describe(
|
|
1378
|
+
"Visibility scope. Defaults to personal \u2014 EXCEPT when a one-shot `sensor` is attached: an enforced lesson is team truth (the sensor must travel to every machine and CI), so it defaults to team. Pass scope explicitly to override."
|
|
1379
|
+
),
|
|
1356
1380
|
module: z16.string().optional().describe("Module name (required when scope=module)"),
|
|
1357
1381
|
tags: z16.array(z16.string()).default([]).describe("Tags for filtering"),
|
|
1358
1382
|
paths: z16.array(z16.string()).default([]).describe("Anchor file paths this applies to"),
|
|
@@ -1375,11 +1399,15 @@ async function memTried(input, ctx) {
|
|
|
1375
1399
|
if (!existsSync16(ctx.paths.haiveDir)) {
|
|
1376
1400
|
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'hivelore init' first.`);
|
|
1377
1401
|
}
|
|
1378
|
-
const
|
|
1402
|
+
const SLUG_STOPWORDS = /* @__PURE__ */ new Set(["and", "or", "the", "a", "an", "of", "to", "in", "for", "with", "on", "at", "by"]);
|
|
1403
|
+
const words = input.what.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 5);
|
|
1404
|
+
while (words.length > 2 && SLUG_STOPWORDS.has(words[words.length - 1])) words.pop();
|
|
1405
|
+
const slug = words.join("-");
|
|
1406
|
+
const scope = input.scope ?? (input.sensor ? "team" : "personal");
|
|
1379
1407
|
const baseFm = buildFrontmatter2({
|
|
1380
1408
|
type: "attempt",
|
|
1381
1409
|
slug,
|
|
1382
|
-
scope
|
|
1410
|
+
scope,
|
|
1383
1411
|
module: input.module,
|
|
1384
1412
|
tags: input.tags,
|
|
1385
1413
|
paths: input.paths,
|
|
@@ -1427,7 +1455,7 @@ async function memTried(input, ctx) {
|
|
|
1427
1455
|
...verdict.reason ? { reason: verdict.reason } : {},
|
|
1428
1456
|
...verdict.guidance ? { guidance: verdict.guidance } : {}
|
|
1429
1457
|
},
|
|
1430
|
-
hint: verdict.accepted ? "Loop closed: the attempt is saved AND enforced \u2014 the gate now refuses a repeat deterministically." : `Attempt saved, but the sensor was rejected (${verdict.reason}). Revise per the guidance and re-propose with propose_sensor.`
|
|
1458
|
+
hint: (verdict.accepted ? "Loop closed: the attempt is saved AND enforced \u2014 the gate now refuses a repeat deterministically." : `Attempt saved, but the sensor was rejected (${verdict.reason}). Revise per the guidance and re-propose with propose_sensor.`) + (input.scope === void 0 ? " Saved team-scoped (an enforced lesson must travel with the repo) \u2014 pass scope:'personal' to keep it private." : "")
|
|
1431
1459
|
};
|
|
1432
1460
|
}
|
|
1433
1461
|
const seed = input.paths.length > 0 ? suggestSensorSeed2(body, input.paths) : null;
|
|
@@ -1454,6 +1482,7 @@ import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile10 } from
|
|
|
1454
1482
|
import path7 from "path";
|
|
1455
1483
|
import { z as z17 } from "zod";
|
|
1456
1484
|
import {
|
|
1485
|
+
buildProposeCommand,
|
|
1457
1486
|
loadMemoriesFromDir as loadMemoriesFromDir14,
|
|
1458
1487
|
normalizeFramework,
|
|
1459
1488
|
parseLessonFields,
|
|
@@ -1461,39 +1490,55 @@ import {
|
|
|
1461
1490
|
scaffoldPostIncidentTest
|
|
1462
1491
|
} from "@hivelore/core";
|
|
1463
1492
|
var PY_SIGNALS = ["pyproject.toml", "setup.py", "pytest.ini", "requirements.txt", "tox.ini"];
|
|
1464
|
-
async function
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
if (hasPkg
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
} catch {
|
|
1484
|
-
pkg = null;
|
|
1485
|
-
}
|
|
1493
|
+
async function detectForAnchor(root, rel) {
|
|
1494
|
+
let dir = path7.resolve(root, rel);
|
|
1495
|
+
try {
|
|
1496
|
+
if (!statSync(dir).isDirectory()) dir = path7.dirname(dir);
|
|
1497
|
+
} catch {
|
|
1498
|
+
if (path7.extname(dir)) dir = path7.dirname(dir);
|
|
1499
|
+
}
|
|
1500
|
+
while (dir.startsWith(root)) {
|
|
1501
|
+
const pkgJson = path7.join(dir, "package.json");
|
|
1502
|
+
const hasPkg = existsSync17(pkgJson);
|
|
1503
|
+
const goMod = existsSync17(path7.join(dir, "go.mod"));
|
|
1504
|
+
const pySignal = PY_SIGNALS.some((s) => existsSync17(path7.join(dir, s)));
|
|
1505
|
+
if (hasPkg || goMod || pySignal) {
|
|
1506
|
+
let pkg = null;
|
|
1507
|
+
if (hasPkg) {
|
|
1508
|
+
try {
|
|
1509
|
+
pkg = JSON.parse(await readFile4(pkgJson, "utf8"));
|
|
1510
|
+
} catch {
|
|
1511
|
+
pkg = null;
|
|
1486
1512
|
}
|
|
1487
|
-
const baseDir = path7.relative(root, dir).split(path7.sep).join("/");
|
|
1488
|
-
return { framework: pickTestFramework(pkg, { goMod, pySignal }), baseDir };
|
|
1489
1513
|
}
|
|
1490
|
-
const
|
|
1491
|
-
|
|
1492
|
-
dir = parent;
|
|
1514
|
+
const baseDir = path7.relative(root, dir).split(path7.sep).join("/");
|
|
1515
|
+
return { framework: pickTestFramework(pkg, { goMod, pySignal }), baseDir };
|
|
1493
1516
|
}
|
|
1517
|
+
const parent = path7.dirname(dir);
|
|
1518
|
+
if (parent === dir || dir === root) break;
|
|
1519
|
+
dir = parent;
|
|
1520
|
+
}
|
|
1521
|
+
return null;
|
|
1522
|
+
}
|
|
1523
|
+
async function detectTestFrameworkForPaths(root, anchorPaths) {
|
|
1524
|
+
const starts = anchorPaths.length > 0 ? anchorPaths : ["."];
|
|
1525
|
+
for (const rel of starts) {
|
|
1526
|
+
const found = await detectForAnchor(root, rel);
|
|
1527
|
+
if (found) return found;
|
|
1494
1528
|
}
|
|
1495
1529
|
return { framework: "vitest", baseDir: "" };
|
|
1496
1530
|
}
|
|
1531
|
+
async function detectTestFrameworksForAnchors(root, anchorPaths) {
|
|
1532
|
+
const starts = anchorPaths.length > 0 ? anchorPaths : ["."];
|
|
1533
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1534
|
+
for (const rel of starts) {
|
|
1535
|
+
const found = await detectForAnchor(root, rel) ?? { framework: "vitest", baseDir: "" };
|
|
1536
|
+
const existing = groups.get(found.baseDir);
|
|
1537
|
+
if (existing) existing.anchors.push(rel);
|
|
1538
|
+
else groups.set(found.baseDir, { ...found, anchors: [rel] });
|
|
1539
|
+
}
|
|
1540
|
+
return [...groups.values()];
|
|
1541
|
+
}
|
|
1497
1542
|
var ScaffoldTestInputSchema = {
|
|
1498
1543
|
memory_id: z17.string().min(1).describe("Id of the attempt/gotcha lesson to scaffold a post-incident test from."),
|
|
1499
1544
|
framework: z17.enum(["vitest", "jest", "pytest", "gotest"]).optional().describe("Test framework. Auto-detected from the package that owns the lesson's anchor paths when omitted."),
|
|
@@ -1507,43 +1552,69 @@ async function scaffoldTest(input, ctx) {
|
|
|
1507
1552
|
return { ok: false, error: `No memory found with id ${input.memory_id}`, memory_id: input.memory_id };
|
|
1508
1553
|
}
|
|
1509
1554
|
const anchorPaths = found.memory.frontmatter.anchor.paths ?? [];
|
|
1510
|
-
const
|
|
1511
|
-
const
|
|
1555
|
+
const allGroups = await detectTestFrameworksForAnchors(ctx.paths.root, anchorPaths);
|
|
1556
|
+
const groups = input.out_path ? allGroups.slice(0, 1) : allGroups;
|
|
1557
|
+
const frameworkFor = (detected) => input.framework ? normalizeFramework(input.framework) ?? detected : detected;
|
|
1512
1558
|
const fields = parseLessonFields(found.memory.body);
|
|
1513
|
-
const
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
{ framework, outPath: input.out_path, baseDir:
|
|
1559
|
+
const lesson = {
|
|
1560
|
+
memoryId: input.memory_id,
|
|
1561
|
+
title: fields.title || input.memory_id,
|
|
1562
|
+
whyFailed: fields.whyFailed,
|
|
1563
|
+
instead: fields.instead,
|
|
1564
|
+
incident: found.memory.frontmatter.sensor?.incident,
|
|
1565
|
+
paths: anchorPaths
|
|
1566
|
+
};
|
|
1567
|
+
let scaffolds = groups.map(
|
|
1568
|
+
(g) => scaffoldPostIncidentTest(lesson, { framework: frameworkFor(g.framework), outPath: input.out_path, baseDir: g.baseDir })
|
|
1523
1569
|
);
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1570
|
+
let proposeCommand = scaffolds[0].proposeCommand;
|
|
1571
|
+
if (scaffolds.length > 1) {
|
|
1572
|
+
proposeCommand = buildProposeCommand(lesson, scaffolds.map((s) => s.runCommand).join(" && "));
|
|
1573
|
+
scaffolds = groups.map(
|
|
1574
|
+
(g) => scaffoldPostIncidentTest(lesson, {
|
|
1575
|
+
framework: frameworkFor(g.framework),
|
|
1576
|
+
baseDir: g.baseDir,
|
|
1577
|
+
proposeCommandOverride: proposeCommand
|
|
1578
|
+
})
|
|
1579
|
+
);
|
|
1580
|
+
}
|
|
1581
|
+
const results = [];
|
|
1582
|
+
for (const scaffold of scaffolds) {
|
|
1583
|
+
const abs = path7.isAbsolute(scaffold.relPath) ? scaffold.relPath : path7.resolve(ctx.paths.root, scaffold.relPath);
|
|
1584
|
+
let written = false;
|
|
1585
|
+
let alreadyExists = false;
|
|
1586
|
+
if (input.write) {
|
|
1587
|
+
if (existsSync17(abs)) {
|
|
1588
|
+
alreadyExists = true;
|
|
1589
|
+
} else {
|
|
1590
|
+
await mkdir4(path7.dirname(abs), { recursive: true });
|
|
1591
|
+
await writeFile10(abs, scaffold.content, "utf8");
|
|
1592
|
+
written = true;
|
|
1593
|
+
}
|
|
1534
1594
|
}
|
|
1595
|
+
results.push({
|
|
1596
|
+
framework: scaffold.framework,
|
|
1597
|
+
path: scaffold.relPath,
|
|
1598
|
+
run_command: scaffold.runCommand,
|
|
1599
|
+
content: scaffold.content,
|
|
1600
|
+
written,
|
|
1601
|
+
already_exists: alreadyExists
|
|
1602
|
+
});
|
|
1535
1603
|
}
|
|
1604
|
+
const first = results[0];
|
|
1605
|
+
const anyExisting = results.some((r) => r.already_exists);
|
|
1536
1606
|
return {
|
|
1537
1607
|
ok: true,
|
|
1538
1608
|
memory_id: input.memory_id,
|
|
1539
|
-
framework,
|
|
1540
|
-
path:
|
|
1541
|
-
run_command:
|
|
1542
|
-
propose_command:
|
|
1543
|
-
content:
|
|
1544
|
-
written,
|
|
1545
|
-
already_exists:
|
|
1546
|
-
|
|
1609
|
+
framework: first.framework,
|
|
1610
|
+
path: first.path,
|
|
1611
|
+
run_command: first.run_command,
|
|
1612
|
+
propose_command: proposeCommand,
|
|
1613
|
+
content: first.content,
|
|
1614
|
+
written: first.written,
|
|
1615
|
+
already_exists: first.already_exists,
|
|
1616
|
+
...results.length > 1 ? { scaffolds: results } : {},
|
|
1617
|
+
notice: (results.length > 1 ? `Lesson spans ${results.length} packages \u2014 one pending test per owning package; ONE propose_command arms them all (chained oracle). ` : "") + (anyExisting ? "Some file(s) already exist \u2014 not overwritten. Delete them or pass out_path to write elsewhere." : "PENDING test scaffolded. Fill in the assertion (RED on the incident, GREEN once fixed), run it, then arm it with propose_command \u2014 propose_sensor stays the sole validated writer.")
|
|
1547
1618
|
};
|
|
1548
1619
|
}
|
|
1549
1620
|
|
|
@@ -2810,7 +2881,7 @@ function oneLine(value) {
|
|
|
2810
2881
|
return value.replace(/\s+/g, " ").replace(/"/g, '\\"').trim().slice(0, 120);
|
|
2811
2882
|
}
|
|
2812
2883
|
function serverVersion() {
|
|
2813
|
-
return true ? "0.
|
|
2884
|
+
return true ? "0.39.1" : "dev";
|
|
2814
2885
|
}
|
|
2815
2886
|
|
|
2816
2887
|
// src/tools/code-map.ts
|
|
@@ -4238,7 +4309,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
4238
4309
|
// src/server.ts
|
|
4239
4310
|
import { hasRecentBriefingMarker, loadConfigSync } from "@hivelore/core";
|
|
4240
4311
|
var SERVER_NAME = "hivelore";
|
|
4241
|
-
var SERVER_VERSION = "0.
|
|
4312
|
+
var SERVER_VERSION = "0.39.1";
|
|
4242
4313
|
function jsonResult(data) {
|
|
4243
4314
|
return {
|
|
4244
4315
|
content: [
|
|
@@ -5194,6 +5265,7 @@ export {
|
|
|
5194
5265
|
codeSearch,
|
|
5195
5266
|
createHaiveServer,
|
|
5196
5267
|
detectTestFrameworkForPaths,
|
|
5268
|
+
detectTestFrameworksForAnchors,
|
|
5197
5269
|
getAllowedToolsForProfile,
|
|
5198
5270
|
getBriefing,
|
|
5199
5271
|
getRecap,
|