@levnikolaevich/hex-line-mcp 1.19.0 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hook.mjs +12 -11
- package/dist/server.mjs +26 -6
- package/package.json +4 -3
package/dist/hook.mjs
CHANGED
|
@@ -115,8 +115,8 @@ var REVERSE_TOOL_HINTS = {
|
|
|
115
115
|
"mcp__hex-line__bulk_replace": "Edit (text rename/refactor across files inside an explicit root path)"
|
|
116
116
|
};
|
|
117
117
|
var TOOL_HINTS = {
|
|
118
|
-
Read: "mcp__hex-line__read_file (not Read). For
|
|
119
|
-
Edit: "mcp__hex-line__edit_file
|
|
118
|
+
Read: "mcp__hex-line__read_file (not Read). For structure-first: mcp__hex-line__outline then mcp__hex-line__read_file with ranges",
|
|
119
|
+
Edit: "mcp__hex-line__edit_file (not Edit). If you need hash anchors: mcp__hex-line__grep_search(output='content', edit_ready=true) first",
|
|
120
120
|
Write: "mcp__hex-line__write_file (not Write). No prior Read needed",
|
|
121
121
|
Grep: "mcp__hex-line__grep_search (not Grep). Params: output, literal, context_before, context_after, multiline",
|
|
122
122
|
Glob: "mcp__hex-line__inspect_path (not Glob). Use pattern=... with an explicit path for project file discovery and name/path globbing",
|
|
@@ -133,7 +133,7 @@ var TOOL_HINTS = {
|
|
|
133
133
|
changes: "mcp__hex-line__changes (git diff with change symbols)",
|
|
134
134
|
bulk: "mcp__hex-line__bulk_replace with path=<project root> (multi-file search-replace)"
|
|
135
135
|
};
|
|
136
|
-
var DEFERRED_HINT = "
|
|
136
|
+
var DEFERRED_HINT = "Run ToolSearch('+hex-line read edit') first if hex-line tools show InputValidationError.";
|
|
137
137
|
var BASH_REDIRECTS = [
|
|
138
138
|
{ regex: /^(cat|type)\b/i, key: "cat", kind: "reader" },
|
|
139
139
|
{ regex: /^head\b/i, key: "head", kind: "reader" },
|
|
@@ -443,8 +443,8 @@ function handlePreToolUse(data) {
|
|
|
443
443
|
const isPlanSafe = targetPath.includes("/.hex-skills/") || targetPath.includes(".hex-skills/") || targetPath.includes("/.claude/") || targetPath.includes(".claude/");
|
|
444
444
|
if (!isPlanSafe) {
|
|
445
445
|
block(
|
|
446
|
-
"PLAN_MODE:
|
|
447
|
-
"
|
|
446
|
+
"PLAN_MODE: You are in planning mode. Write your plan to the plan file, then call ExitPlanMode to get approval before making changes.",
|
|
447
|
+
"Do NOT try other write tools (Write, Edit, Bash). Only the plan file can be edited in plan mode. Finish planning first."
|
|
448
448
|
);
|
|
449
449
|
}
|
|
450
450
|
}
|
|
@@ -463,13 +463,14 @@ function handlePreToolUse(data) {
|
|
|
463
463
|
}
|
|
464
464
|
if (toolName === "Read") {
|
|
465
465
|
const ext = filePath ? extOf(filePath) : "";
|
|
466
|
-
const rangeHint = isPartialRead(toolInput) ? " Preserve the same offset/limit or ranges
|
|
467
|
-
const
|
|
468
|
-
|
|
466
|
+
const rangeHint = isPartialRead(toolInput) ? " Preserve the same offset/limit or ranges." : "";
|
|
467
|
+
const outlineTip = filePath && OUTLINEABLE_EXT.has(ext) ? ` For structure-first discovery: mcp__hex-line__outline then mcp__hex-line__read_file with ranges.` : "";
|
|
468
|
+
const target = filePath ? `Use mcp__hex-line__read_file(path="${filePath}").${rangeHint}${outlineTip}` : "Use mcp__hex-line__read_file or mcp__hex-line__inspect_path.";
|
|
469
|
+
redirect(target, DEFERRED_HINT);
|
|
469
470
|
}
|
|
470
471
|
if (toolName === "Edit") {
|
|
471
|
-
const target = filePath ? `Use mcp__hex-
|
|
472
|
-
redirect(target, "
|
|
472
|
+
const target = filePath ? `Use mcp__hex-line__edit_file(path="${filePath}"). If you need hash anchors first: mcp__hex-line__grep_search(output="content", edit_ready=true).` : 'Use mcp__hex-line__edit_file. If you need hash anchors first: mcp__hex-line__grep_search(output="content", edit_ready=true).';
|
|
473
|
+
redirect(target, "Hash-verified edits for project text files.\n" + DEFERRED_HINT);
|
|
473
474
|
}
|
|
474
475
|
if (toolName === "Write") {
|
|
475
476
|
const pathNote = filePath ? ` with path="${filePath}"` : "";
|
|
@@ -591,7 +592,7 @@ function handleSessionStart() {
|
|
|
591
592
|
} catch {
|
|
592
593
|
}
|
|
593
594
|
}
|
|
594
|
-
const msg = styleActive ? "Hex-line MCP available. Output style active.\n<hex-line_instructions>\n <deferred_loading>If hex-line schemas not loaded, run: ToolSearch('+hex-line read edit')</deferred_loading>\n <note>Follow the active hex-line output style for primary tool choices.</note>\n <exceptions>Built-in tools stay OK for images, PDFs, notebooks, and text paths outside the current project root.</exceptions>\n</hex-line_instructions>" : "Hex-line MCP available.\n<hex-line_instructions>\n <deferred_loading>If hex-line schemas not loaded, run: ToolSearch('+hex-line read edit')</deferred_loading>\n <exploration>\n <rule>Use outline for structure (code + markdown), not Read. ~10-20 lines vs hundreds.</rule>\n <rule>Use read_file with offset/limit or ranges for targeted reads.</rule>\n <rule>Use grep_search before editing to get hash anchors.</rule>\n </exploration>\n <editing>\n <path name='surgical'>grep_search \u2192 edit_file (fastest: hash-verified, no full read needed)</path>\n <path name='exploratory'>outline \u2192 read_file (ranges) \u2192 edit_file with base_revision</path>\n <path name='multi-file'>bulk_replace(path="<project root>") for text rename/refactor across files</path>\n </editing>\n <tips>\n <tip>Auto-fill path from the active file or project root. Read-only tools may inspect explicit temp-file paths outside the repo. Mutating tools stay project-scoped unless you intentionally pass allow_external=true.</tip>\n <tip>Never invent range_checksum. Copy it from fresh read_file or grep_search blocks.</tip>\n <tip>Prefer set_line or insert_after for small local changes and replace_between for larger bounded rewrites.</tip>\n <tip>Carry revision from read_file into base_revision on edit_file.</tip>\n <tip>If edit returns CONFLICT, call verify \u2014 only reread when STALE.</tip>\n <tip>Avoid large first-pass edit batches. Start with 1-2 hunks, then continue from the returned revision.</tip>\n <tip>Use write_file for new files (no prior Read needed).</tip>\n <tip>Use inspect_path(pattern=...) for project file discovery and name/path globbing.</tip>\n </tips>\n <exceptions>Built-in tools stay OK for images, PDFs, notebooks, and text paths outside the current project root.</exceptions>\n</hex-line_instructions>";
|
|
595
|
+
const msg = styleActive ? "Hex-line MCP available. Output style active.\n<hex-line_instructions>\n <deferred_loading>If hex-line schemas not loaded, run: ToolSearch('+hex-line read edit')</deferred_loading>\n <tool_map>Read\u2192mcp__hex-line__read_file, Edit\u2192mcp__hex-line__edit_file, Write\u2192mcp__hex-line__write_file, Grep\u2192mcp__hex-line__grep_search, Glob\u2192mcp__hex-line__inspect_path</tool_map>\n <note>Follow the active hex-line output style for primary tool choices.</note>\n <exceptions>Built-in tools stay OK for images, PDFs, notebooks, and text paths outside the current project root.</exceptions>\n</hex-line_instructions>" : "Hex-line MCP available.\n<hex-line_instructions>\n <deferred_loading>If hex-line schemas not loaded, run: ToolSearch('+hex-line read edit')</deferred_loading>\n <exploration>\n <rule>Use outline for structure (code + markdown), not Read. ~10-20 lines vs hundreds.</rule>\n <rule>Use read_file with offset/limit or ranges for targeted reads.</rule>\n <rule>Use grep_search before editing to get hash anchors.</rule>\n </exploration>\n <editing>\n <path name='surgical'>grep_search \u2192 edit_file (fastest: hash-verified, no full read needed)</path>\n <path name='exploratory'>outline \u2192 read_file (ranges) \u2192 edit_file with base_revision</path>\n <path name='multi-file'>bulk_replace(path="<project root>") for text rename/refactor across files</path>\n </editing>\n <tips>\n <tip>Auto-fill path from the active file or project root. Read-only tools may inspect explicit temp-file paths outside the repo. Mutating tools stay project-scoped unless you intentionally pass allow_external=true.</tip>\n <tip>Never invent range_checksum. Copy it from fresh read_file or grep_search blocks.</tip>\n <tip>Prefer set_line or insert_after for small local changes and replace_between for larger bounded rewrites.</tip>\n <tip>Carry revision from read_file into base_revision on edit_file.</tip>\n <tip>If edit returns CONFLICT, call verify \u2014 only reread when STALE.</tip>\n <tip>Avoid large first-pass edit batches. Start with 1-2 hunks, then continue from the returned revision.</tip>\n <tip>Use write_file for new files (no prior Read needed).</tip>\n <tip>Use inspect_path(pattern=...) for project file discovery and name/path globbing.</tip>\n </tips>\n <exceptions>Built-in tools stay OK for images, PDFs, notebooks, and text paths outside the current project root.</exceptions>\n</hex-line_instructions>";
|
|
595
596
|
safeExit(1, JSON.stringify({ systemMessage: msg }), 0);
|
|
596
597
|
}
|
|
597
598
|
function handleConfigChange(data) {
|
package/dist/server.mjs
CHANGED
|
@@ -2579,6 +2579,25 @@ function applyReplaceBetweenEdit(edit, ctx) {
|
|
|
2579
2579
|
return null;
|
|
2580
2580
|
}
|
|
2581
2581
|
replaceLogicalRange(lines, lineEndings, sliceStart, sliceStart + removeCount - 1, newLines, defaultEol);
|
|
2582
|
+
const insertEnd = sliceStart + newLines.length;
|
|
2583
|
+
if (newLines.length > 0 && insertEnd < lines.length) {
|
|
2584
|
+
const lastNew = newLines[newLines.length - 1].trim();
|
|
2585
|
+
const firstAfter = lines[insertEnd].trim();
|
|
2586
|
+
if (lastNew && lastNew === firstAfter) {
|
|
2587
|
+
lines.splice(insertEnd, 1);
|
|
2588
|
+
lineEndings.splice(insertEnd, 1);
|
|
2589
|
+
ctx.corrections.push({ type: "tail_echo", line: insertEnd + 1, content: firstAfter });
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
if (newLines.length > 0 && sliceStart > 0) {
|
|
2593
|
+
const firstNew = newLines[0].trim();
|
|
2594
|
+
const lineBefore = lines[sliceStart - 1].trim();
|
|
2595
|
+
if (firstNew && firstNew === lineBefore) {
|
|
2596
|
+
lines.splice(sliceStart - 1, 1);
|
|
2597
|
+
lineEndings.splice(sliceStart - 1, 1);
|
|
2598
|
+
ctx.corrections.push({ type: "head_echo", line: sliceStart, content: lineBefore });
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2582
2601
|
return null;
|
|
2583
2602
|
}
|
|
2584
2603
|
function editFile(filePath, edits, opts = {}) {
|
|
@@ -2721,7 +2740,8 @@ ${snip.text}`;
|
|
|
2721
2740
|
locateOrConflict,
|
|
2722
2741
|
opts,
|
|
2723
2742
|
origLines,
|
|
2724
|
-
staleRevision
|
|
2743
|
+
staleRevision,
|
|
2744
|
+
corrections: []
|
|
2725
2745
|
};
|
|
2726
2746
|
const batchConflicts = collectBatchConflicts({
|
|
2727
2747
|
edits: anchored,
|
|
@@ -2905,7 +2925,7 @@ ${sections.join("\n")}`;
|
|
|
2905
2925
|
if (displayDiff) payloadKinds.push("diff");
|
|
2906
2926
|
const semanticFactCount = semanticImpacts.reduce((sum, impact) => sum + (impact.facts?.length || 0), 0);
|
|
2907
2927
|
const summaryLines = [
|
|
2908
|
-
`summary: lines_changed=${changedSpan} diff_entries=${diffEntryCount} lines_after=${lines.length}`,
|
|
2928
|
+
`summary: lines_changed=${changedSpan} diff_entries=${diffEntryCount} lines_after=${lines.length}${editContext.corrections.length > 0 ? ` boundary_echo_stripped=${editContext.corrections.length}` : ``}`,
|
|
2909
2929
|
`payload_sections: ${payloadSections(payloadKinds)}`,
|
|
2910
2930
|
`graph_enrichment: ${graphEnrichment}`,
|
|
2911
2931
|
`semantic_impact_count: ${semanticImpacts.length}`,
|
|
@@ -4013,8 +4033,8 @@ var NODE_COMMAND = process.execPath.replace(/\\/g, "/");
|
|
|
4013
4033
|
var HOOK_COMMAND = `"${NODE_COMMAND}" "${STABLE_HOOK_PATH}"`;
|
|
4014
4034
|
var __filename = fileURLToPath2(import.meta.url);
|
|
4015
4035
|
var __dirname = dirname4(__filename);
|
|
4016
|
-
var SOURCE_HOOK = resolve7(__dirname, "..", "hook.mjs");
|
|
4017
4036
|
var DIST_HOOK = resolve7(__dirname, "hook.mjs");
|
|
4037
|
+
var BUILT_HOOK = resolve7(__dirname, "..", "dist", "hook.mjs");
|
|
4018
4038
|
var SOURCE_STYLE = resolve7(__dirname, "..", "output-style.md");
|
|
4019
4039
|
var INSTALLED_STYLE = resolve7(homedir(), ".claude", "output-styles", "hex-line.md");
|
|
4020
4040
|
var HOOK_SIGNATURE = "hex-line";
|
|
@@ -4126,8 +4146,8 @@ function syncOutputStyle() {
|
|
|
4126
4146
|
return true;
|
|
4127
4147
|
}
|
|
4128
4148
|
function autoSync() {
|
|
4129
|
-
const hookSource = existsSync6(DIST_HOOK) ? DIST_HOOK :
|
|
4130
|
-
if (!
|
|
4149
|
+
const hookSource = existsSync6(DIST_HOOK) ? DIST_HOOK : existsSync6(BUILT_HOOK) ? BUILT_HOOK : null;
|
|
4150
|
+
if (!hookSource) return;
|
|
4131
4151
|
const changes = [];
|
|
4132
4152
|
const srcHook = safeRead(hookSource);
|
|
4133
4153
|
const dstHook = safeRead(STABLE_HOOK_PATH);
|
|
@@ -4763,7 +4783,7 @@ function errorResult(code, message, recovery, { large = false, extra = null } =
|
|
|
4763
4783
|
}
|
|
4764
4784
|
|
|
4765
4785
|
// server.mjs
|
|
4766
|
-
var version = true ? "1.
|
|
4786
|
+
var version = true ? "1.20.0" : (await null).createRequire(import.meta.url)("./package.json").version;
|
|
4767
4787
|
var STATUS_ENUM = z2.enum(["OK", "ERROR", "AUTO_REBASED", "CONFLICT", "STALE", "INVALID", "NO_CHANGES", "CHANGED", "UNSUPPORTED"]);
|
|
4768
4788
|
var ERROR_SHAPE = z2.object({ code: z2.string(), message: z2.string(), recovery: z2.string() }).optional();
|
|
4769
4789
|
var LINE_REPORT_KEYS = /* @__PURE__ */ new Set([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@levnikolaevich/hex-line-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0",
|
|
4
4
|
"mcpName": "io.github.levnikolaevich/hex-line-mcp",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Hash-verified file editing MCP + token efficiency hook for AI coding agents. 9 tools: inspect_path, read, edit, write, grep, outline, verify, changes, bulk_replace.",
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"scenarios": "node scenarios/index.mjs",
|
|
24
24
|
"scenarios:diagnostic": "node scenarios/index.mjs --diagnostics",
|
|
25
25
|
"scenarios:diagnostic:graph": "node scenarios/index.mjs --diagnostics --with-graph",
|
|
26
|
-
"check": "node --check server.mjs && node --check hook.mjs"
|
|
26
|
+
"check": "node --check server.mjs && node --check hook.mjs",
|
|
27
|
+
"prepare": "npm run build"
|
|
27
28
|
},
|
|
28
29
|
"_dep_notes": {
|
|
29
30
|
"web-tree-sitter": "Tracks the latest stable runtime together with the repo-owned first-party grammar WASM bundle. Keep runtime upgrades coupled to grammar load/parse validation across Windows, macOS, and Linux.",
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
"zod": "^4.3.6"
|
|
40
41
|
},
|
|
41
42
|
"optionalDependencies": {
|
|
42
|
-
"better-sqlite3": "^12.
|
|
43
|
+
"better-sqlite3": "^12.9.0"
|
|
43
44
|
},
|
|
44
45
|
"author": "Lev Nikolaevich <https://github.com/levnikolaevich>",
|
|
45
46
|
"license": "MIT",
|