@levnikolaevich/hex-line-mcp 1.8.0 → 1.8.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.
Files changed (2) hide show
  1. package/dist/server.mjs +59 -5
  2. package/package.json +1 -1
package/dist/server.mjs CHANGED
@@ -281,7 +281,8 @@ function validateWritePath(filePath) {
281
281
  import { existsSync as existsSync2 } from "node:fs";
282
282
  import { join as join3, dirname as dirname2, relative } from "node:path";
283
283
  import { createRequire } from "node:module";
284
- var HEX_LINE_CONTRACT_VERSION = 1;
284
+ var HEX_LINE_CONTRACT_VERSION_MIN = 1;
285
+ var HEX_LINE_CONTRACT_VERSION_MAX = 2;
285
286
  var _dbs = /* @__PURE__ */ new Map();
286
287
  var _driverUnavailable = false;
287
288
  function getGraphDB(filePath) {
@@ -306,12 +307,24 @@ function getGraphDB(filePath) {
306
307
  return null;
307
308
  }
308
309
  }
310
+ var _cloneViewAvailable = /* @__PURE__ */ new WeakMap();
311
+ function _hasCloneView(db) {
312
+ if (_cloneViewAvailable.has(db)) return _cloneViewAvailable.get(db);
313
+ try {
314
+ db.prepare("SELECT node_id, norm_hash, file, line_start, display_name FROM hex_line_clone_siblings LIMIT 0").run();
315
+ _cloneViewAvailable.set(db, true);
316
+ return true;
317
+ } catch {
318
+ _cloneViewAvailable.set(db, false);
319
+ return false;
320
+ }
321
+ }
309
322
  function validateHexLineContract(db) {
310
323
  try {
311
324
  const contract = db.prepare("SELECT contract_version FROM hex_line_contract LIMIT 1").get();
312
- if (!contract || contract.contract_version !== HEX_LINE_CONTRACT_VERSION) return false;
325
+ if (!contract || contract.contract_version < HEX_LINE_CONTRACT_VERSION_MIN || contract.contract_version > HEX_LINE_CONTRACT_VERSION_MAX) return false;
313
326
  db.prepare("SELECT node_id, file, line_start, line_end, display_name, kind, callees, callers FROM hex_line_symbol_annotations LIMIT 1").all();
314
- db.prepare("SELECT source_id, target_id, source_file, source_line, source_display_name, target_file, target_line, target_display_name FROM hex_line_call_edges LIMIT 1").all();
327
+ db.prepare("SELECT source_id, target_id, source_file, source_line, source_display_name, target_file, target_line, target_display_name, confidence FROM hex_line_call_edges LIMIT 1").all();
315
328
  return true;
316
329
  } catch {
317
330
  return false;
@@ -363,7 +376,8 @@ function callImpact(db, file, startLine, endLine) {
363
376
  const dependents = db.prepare(
364
377
  `SELECT source_display_name AS name, source_file AS file, source_line AS line
365
378
  FROM hex_line_call_edges
366
- WHERE target_id = ?`
379
+ WHERE target_id = ?
380
+ AND confidence IN ('exact', 'precise')`
367
381
  ).all(node.node_id);
368
382
  for (const dep of dependents) {
369
383
  const key = `${dep.file}:${dep.name}`;
@@ -378,6 +392,39 @@ function callImpact(db, file, startLine, endLine) {
378
392
  return [];
379
393
  }
380
394
  }
395
+ function cloneWarning(db, file, startLine, endLine) {
396
+ try {
397
+ if (!_hasCloneView(db)) return [];
398
+ const modified = db.prepare(
399
+ `SELECT node_id
400
+ FROM hex_line_symbol_annotations
401
+ WHERE file = ?
402
+ AND line_start <= ?
403
+ AND line_end >= ?`
404
+ ).all(file, endLine, startLine);
405
+ if (modified.length === 0) return [];
406
+ const clones = [];
407
+ const seen = /* @__PURE__ */ new Set();
408
+ for (const node of modified) {
409
+ const siblings = db.prepare(
410
+ `SELECT s2.file, s2.line_start, s2.display_name
411
+ FROM hex_line_clone_siblings s1
412
+ JOIN hex_line_clone_siblings s2 ON s2.norm_hash = s1.norm_hash AND s2.node_id != s1.node_id
413
+ WHERE s1.node_id = ?`
414
+ ).all(node.node_id);
415
+ for (const sib of siblings) {
416
+ const key = `${sib.file}:${sib.display_name}`;
417
+ if (!seen.has(key)) {
418
+ seen.add(key);
419
+ clones.push({ name: sib.display_name, file: sib.file, line: sib.line_start });
420
+ }
421
+ }
422
+ }
423
+ return clones.slice(0, 10);
424
+ } catch {
425
+ return [];
426
+ }
427
+ }
381
428
  function matchAnnotation(db, file, line) {
382
429
  try {
383
430
  const node = db.prepare(
@@ -1569,6 +1616,13 @@ ${serializeReadBlock(block)}`;
1569
1616
  \u26A0 Call impact: ${affected.length} callers in other files
1570
1617
  ${list}`;
1571
1618
  }
1619
+ const clones = cloneWarning(db, relFile, minLine, maxLine);
1620
+ if (clones.length > 0) {
1621
+ const list = clones.map((c) => `${c.file}:${c.line}`).join(", ");
1622
+ msg += `
1623
+
1624
+ \u26A0 ${clones.length} clone(s): ${list}`;
1625
+ }
1572
1626
  }
1573
1627
  } catch {
1574
1628
  }
@@ -2786,7 +2840,7 @@ OUTPUT_CAPPED: Output exceeded ${MAX_BULK_OUTPUT_CHARS} chars.`;
2786
2840
  }
2787
2841
 
2788
2842
  // server.mjs
2789
- var version = true ? "1.8.0" : (await null).createRequire(import.meta.url)("./package.json").version;
2843
+ var version = true ? "1.8.1" : (await null).createRequire(import.meta.url)("./package.json").version;
2790
2844
  var { server, StdioServerTransport } = await createServerRuntime({
2791
2845
  name: "hex-line-mcp",
2792
2846
  version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@levnikolaevich/hex-line-mcp",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
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. 10 tools: read, edit, write, grep, outline, verify, directory_tree, file_info, changes, bulk_replace.",