gitnexus 1.2.8 → 1.2.9

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 (38) hide show
  1. package/README.md +194 -186
  2. package/dist/cli/ai-context.js +71 -71
  3. package/dist/cli/analyze.js +1 -1
  4. package/dist/cli/setup.js +8 -1
  5. package/dist/cli/view.d.ts +13 -0
  6. package/dist/cli/view.js +59 -0
  7. package/dist/core/augmentation/engine.js +20 -20
  8. package/dist/core/embeddings/embedding-pipeline.js +26 -26
  9. package/dist/core/graph/html-graph-viewer.d.ts +15 -0
  10. package/dist/core/graph/html-graph-viewer.js +542 -0
  11. package/dist/core/graph/html-graph-viewer.test.d.ts +1 -0
  12. package/dist/core/graph/html-graph-viewer.test.js +67 -0
  13. package/dist/core/ingestion/cluster-enricher.js +16 -16
  14. package/dist/core/kuzu/kuzu-adapter.js +9 -9
  15. package/dist/core/kuzu/schema.js +256 -256
  16. package/dist/core/search/bm25-index.js +5 -5
  17. package/dist/core/search/hybrid-search.js +3 -3
  18. package/dist/core/wiki/graph-queries.js +52 -52
  19. package/dist/core/wiki/html-viewer.js +192 -192
  20. package/dist/core/wiki/prompts.js +82 -82
  21. package/dist/mcp/core/embedder.js +8 -4
  22. package/dist/mcp/local/local-backend.d.ts +6 -0
  23. package/dist/mcp/local/local-backend.js +224 -117
  24. package/dist/mcp/resources.js +42 -42
  25. package/dist/mcp/server.js +16 -16
  26. package/dist/mcp/tools.js +86 -77
  27. package/dist/server/api.d.ts +4 -2
  28. package/dist/server/api.js +253 -83
  29. package/hooks/claude/gitnexus-hook.cjs +135 -135
  30. package/hooks/claude/pre-tool-use.sh +78 -78
  31. package/hooks/claude/session-start.sh +42 -42
  32. package/package.json +82 -82
  33. package/skills/debugging.md +85 -85
  34. package/skills/exploring.md +75 -75
  35. package/skills/impact-analysis.md +94 -94
  36. package/skills/refactoring.md +113 -113
  37. package/vendor/leiden/index.cjs +355 -355
  38. package/vendor/leiden/utils.cjs +392 -392
@@ -98,11 +98,11 @@ export async function augment(pattern, cwd) {
98
98
  for (const result of bm25Results.slice(0, 5)) {
99
99
  const escaped = result.filePath.replace(/'/g, "''");
100
100
  try {
101
- const symbols = await executeQuery(repoId, `
102
- MATCH (n) WHERE n.filePath = '${escaped}'
103
- AND n.name CONTAINS '${pattern.replace(/'/g, "''").split(/\s+/)[0]}'
104
- RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath
105
- LIMIT 3
101
+ const symbols = await executeQuery(repoId, `
102
+ MATCH (n) WHERE n.filePath = '${escaped}'
103
+ AND n.name CONTAINS '${pattern.replace(/'/g, "''").split(/\s+/)[0]}'
104
+ RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath
105
+ LIMIT 3
106
106
  `);
107
107
  for (const sym of symbols) {
108
108
  symbolMatches.push({
@@ -130,10 +130,10 @@ export async function augment(pattern, cwd) {
130
130
  // Callers
131
131
  let callers = [];
132
132
  try {
133
- const rows = await executeQuery(repoId, `
134
- MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(n {id: '${escaped}'})
135
- RETURN caller.name AS name
136
- LIMIT 3
133
+ const rows = await executeQuery(repoId, `
134
+ MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(n {id: '${escaped}'})
135
+ RETURN caller.name AS name
136
+ LIMIT 3
137
137
  `);
138
138
  callers = rows.map((r) => r.name || r[0]).filter(Boolean);
139
139
  }
@@ -141,10 +141,10 @@ export async function augment(pattern, cwd) {
141
141
  // Callees
142
142
  let callees = [];
143
143
  try {
144
- const rows = await executeQuery(repoId, `
145
- MATCH (n {id: '${escaped}'})-[:CodeRelation {type: 'CALLS'}]->(callee)
146
- RETURN callee.name AS name
147
- LIMIT 3
144
+ const rows = await executeQuery(repoId, `
145
+ MATCH (n {id: '${escaped}'})-[:CodeRelation {type: 'CALLS'}]->(callee)
146
+ RETURN callee.name AS name
147
+ LIMIT 3
148
148
  `);
149
149
  callees = rows.map((r) => r.name || r[0]).filter(Boolean);
150
150
  }
@@ -152,9 +152,9 @@ export async function augment(pattern, cwd) {
152
152
  // Processes
153
153
  let processes = [];
154
154
  try {
155
- const rows = await executeQuery(repoId, `
156
- MATCH (n {id: '${escaped}'})-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
157
- RETURN p.heuristicLabel AS label, r.step AS step, p.stepCount AS stepCount
155
+ const rows = await executeQuery(repoId, `
156
+ MATCH (n {id: '${escaped}'})-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
157
+ RETURN p.heuristicLabel AS label, r.step AS step, p.stepCount AS stepCount
158
158
  `);
159
159
  processes = rows.map((r) => {
160
160
  const label = r.label || r[0];
@@ -167,10 +167,10 @@ export async function augment(pattern, cwd) {
167
167
  // Cluster cohesion (internal ranking signal)
168
168
  let cohesion = 0;
169
169
  try {
170
- const rows = await executeQuery(repoId, `
171
- MATCH (n {id: '${escaped}'})-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
172
- RETURN c.cohesion AS cohesion
173
- LIMIT 1
170
+ const rows = await executeQuery(repoId, `
171
+ MATCH (n {id: '${escaped}'})-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
172
+ RETURN c.cohesion AS cohesion
173
+ LIMIT 1
174
174
  `);
175
175
  if (rows.length > 0) {
176
176
  cohesion = (rows[0].cohesion ?? rows[0][0]) || 0;
@@ -24,19 +24,19 @@ const queryEmbeddableNodes = async (executeQuery) => {
24
24
  let query;
25
25
  if (label === 'File') {
26
26
  // File nodes don't have startLine/endLine
27
- query = `
28
- MATCH (n:File)
29
- RETURN n.id AS id, n.name AS name, 'File' AS label,
30
- n.filePath AS filePath, n.content AS content
27
+ query = `
28
+ MATCH (n:File)
29
+ RETURN n.id AS id, n.name AS name, 'File' AS label,
30
+ n.filePath AS filePath, n.content AS content
31
31
  `;
32
32
  }
33
33
  else {
34
34
  // Code elements have startLine/endLine
35
- query = `
36
- MATCH (n:${label})
37
- RETURN n.id AS id, n.name AS name, '${label}' AS label,
38
- n.filePath AS filePath, n.content AS content,
39
- n.startLine AS startLine, n.endLine AS endLine
35
+ query = `
36
+ MATCH (n:${label})
37
+ RETURN n.id AS id, n.name AS name, '${label}' AS label,
38
+ n.filePath AS filePath, n.content AS content,
39
+ n.startLine AS startLine, n.endLine AS endLine
40
40
  `;
41
41
  }
42
42
  const rows = await executeQuery(query);
@@ -77,8 +77,8 @@ const batchInsertEmbeddings = async (executeWithReusedStatement, updates) => {
77
77
  * Now indexes the separate CodeEmbedding table
78
78
  */
79
79
  const createVectorIndex = async (executeQuery) => {
80
- const cypher = `
81
- CALL CREATE_VECTOR_INDEX('CodeEmbedding', 'code_embedding_idx', 'embedding', metric := 'cosine')
80
+ const cypher = `
81
+ CALL CREATE_VECTOR_INDEX('CodeEmbedding', 'code_embedding_idx', 'embedding', metric := 'cosine')
82
82
  `;
83
83
  try {
84
84
  await executeQuery(cypher);
@@ -240,14 +240,14 @@ export const semanticSearch = async (executeQuery, query, k = 10, maxDistance =
240
240
  const queryVec = embeddingToArray(queryEmbedding);
241
241
  const queryVecStr = `[${queryVec.join(',')}]`;
242
242
  // Query the vector index on CodeEmbedding to get nodeIds and distances
243
- const vectorQuery = `
244
- CALL QUERY_VECTOR_INDEX('CodeEmbedding', 'code_embedding_idx',
245
- CAST(${queryVecStr} AS FLOAT[384]), ${k})
246
- YIELD node AS emb, distance
247
- WITH emb, distance
248
- WHERE distance < ${maxDistance}
249
- RETURN emb.nodeId AS nodeId, distance
250
- ORDER BY distance
243
+ const vectorQuery = `
244
+ CALL QUERY_VECTOR_INDEX('CodeEmbedding', 'code_embedding_idx',
245
+ CAST(${queryVecStr} AS FLOAT[384]), ${k})
246
+ YIELD node AS emb, distance
247
+ WITH emb, distance
248
+ WHERE distance < ${maxDistance}
249
+ RETURN emb.nodeId AS nodeId, distance
250
+ ORDER BY distance
251
251
  `;
252
252
  const embResults = await executeQuery(vectorQuery);
253
253
  if (embResults.length === 0) {
@@ -266,16 +266,16 @@ export const semanticSearch = async (executeQuery, query, k = 10, maxDistance =
266
266
  try {
267
267
  let nodeQuery;
268
268
  if (label === 'File') {
269
- nodeQuery = `
270
- MATCH (n:File {id: '${nodeId.replace(/'/g, "''")}'})
271
- RETURN n.name AS name, n.filePath AS filePath
269
+ nodeQuery = `
270
+ MATCH (n:File {id: '${nodeId.replace(/'/g, "''")}'})
271
+ RETURN n.name AS name, n.filePath AS filePath
272
272
  `;
273
273
  }
274
274
  else {
275
- nodeQuery = `
276
- MATCH (n:${label} {id: '${nodeId.replace(/'/g, "''")}'})
277
- RETURN n.name AS name, n.filePath AS filePath,
278
- n.startLine AS startLine, n.endLine AS endLine
275
+ nodeQuery = `
276
+ MATCH (n:${label} {id: '${nodeId.replace(/'/g, "''")}'})
277
+ RETURN n.name AS name, n.filePath AS filePath,
278
+ n.startLine AS startLine, n.endLine AS endLine
279
279
  `;
280
280
  }
281
281
  const nodeRows = await executeQuery(nodeQuery);
@@ -0,0 +1,15 @@
1
+ /**
2
+ * HTML Graph Viewer Generator
3
+ *
4
+ * Produces a self-contained graph.html that renders the knowledge graph
5
+ * using Sigma.js v2 + graphology (both from CDN).
6
+ *
7
+ * Critical: node `content` fields are stripped before embedding to prevent
8
+ * </script> injection from source code breaking the HTML parser.
9
+ */
10
+ import { GraphNode, GraphRelationship } from './types.js';
11
+ /**
12
+ * Generate a self-contained HTML file that renders the knowledge graph.
13
+ * Strips large/unsafe fields from nodes before embedding.
14
+ */
15
+ export declare function generateHTMLGraphViewer(nodes: GraphNode[], relationships: GraphRelationship[], projectName: string): string;