contextsliver 0.1.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 (134) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +130 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +156 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/graph/rank.d.ts +9 -0
  8. package/dist/graph/rank.d.ts.map +1 -0
  9. package/dist/graph/rank.js +10 -0
  10. package/dist/graph/rank.js.map +1 -0
  11. package/dist/graph/schema.d.ts +4 -0
  12. package/dist/graph/schema.d.ts.map +1 -0
  13. package/dist/graph/schema.js +74 -0
  14. package/dist/graph/schema.js.map +1 -0
  15. package/dist/graph/store.d.ts +56 -0
  16. package/dist/graph/store.d.ts.map +1 -0
  17. package/dist/graph/store.js +163 -0
  18. package/dist/graph/store.js.map +1 -0
  19. package/dist/graph/traverse.d.ts +20 -0
  20. package/dist/graph/traverse.d.ts.map +1 -0
  21. package/dist/graph/traverse.js +81 -0
  22. package/dist/graph/traverse.js.map +1 -0
  23. package/dist/graph/types.d.ts +48 -0
  24. package/dist/graph/types.d.ts.map +1 -0
  25. package/dist/graph/types.js +4 -0
  26. package/dist/graph/types.js.map +1 -0
  27. package/dist/index.d.ts +13 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +16 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/mcp/responses.d.ts +19 -0
  32. package/dist/mcp/responses.d.ts.map +1 -0
  33. package/dist/mcp/responses.js +25 -0
  34. package/dist/mcp/responses.js.map +1 -0
  35. package/dist/mcp/server.d.ts +16 -0
  36. package/dist/mcp/server.d.ts.map +1 -0
  37. package/dist/mcp/server.js +88 -0
  38. package/dist/mcp/server.js.map +1 -0
  39. package/dist/mcp/tools/cs_blast_radius.d.ts +3 -0
  40. package/dist/mcp/tools/cs_blast_radius.d.ts.map +1 -0
  41. package/dist/mcp/tools/cs_blast_radius.js +96 -0
  42. package/dist/mcp/tools/cs_blast_radius.js.map +1 -0
  43. package/dist/mcp/tools/cs_get_context.d.ts +3 -0
  44. package/dist/mcp/tools/cs_get_context.d.ts.map +1 -0
  45. package/dist/mcp/tools/cs_get_context.js +80 -0
  46. package/dist/mcp/tools/cs_get_context.js.map +1 -0
  47. package/dist/mcp/tools/cs_index_repo.d.ts +3 -0
  48. package/dist/mcp/tools/cs_index_repo.d.ts.map +1 -0
  49. package/dist/mcp/tools/cs_index_repo.js +45 -0
  50. package/dist/mcp/tools/cs_index_repo.js.map +1 -0
  51. package/dist/mcp/tools/cs_index_status.d.ts +3 -0
  52. package/dist/mcp/tools/cs_index_status.d.ts.map +1 -0
  53. package/dist/mcp/tools/cs_index_status.js +31 -0
  54. package/dist/mcp/tools/cs_index_status.js.map +1 -0
  55. package/dist/mcp/tools/cs_search_symbols.d.ts +3 -0
  56. package/dist/mcp/tools/cs_search_symbols.d.ts.map +1 -0
  57. package/dist/mcp/tools/cs_search_symbols.js +51 -0
  58. package/dist/mcp/tools/cs_search_symbols.js.map +1 -0
  59. package/dist/mcp/tools/index.d.ts +4 -0
  60. package/dist/mcp/tools/index.d.ts.map +1 -0
  61. package/dist/mcp/tools/index.js +14 -0
  62. package/dist/mcp/tools/index.js.map +1 -0
  63. package/dist/mcp/types.d.ts +22 -0
  64. package/dist/mcp/types.d.ts.map +1 -0
  65. package/dist/mcp/types.js +2 -0
  66. package/dist/mcp/types.js.map +1 -0
  67. package/dist/parser/extractor.d.ts +16 -0
  68. package/dist/parser/extractor.d.ts.map +1 -0
  69. package/dist/parser/extractor.js +304 -0
  70. package/dist/parser/extractor.js.map +1 -0
  71. package/dist/parser/index.d.ts +30 -0
  72. package/dist/parser/index.d.ts.map +1 -0
  73. package/dist/parser/index.js +270 -0
  74. package/dist/parser/index.js.map +1 -0
  75. package/dist/parser/languages/python.d.ts +3 -0
  76. package/dist/parser/languages/python.d.ts.map +1 -0
  77. package/dist/parser/languages/python.js +14 -0
  78. package/dist/parser/languages/python.js.map +1 -0
  79. package/dist/parser/languages/query-loader.d.ts +10 -0
  80. package/dist/parser/languages/query-loader.d.ts.map +1 -0
  81. package/dist/parser/languages/query-loader.js +33 -0
  82. package/dist/parser/languages/query-loader.js.map +1 -0
  83. package/dist/parser/languages/registry.d.ts +25 -0
  84. package/dist/parser/languages/registry.d.ts.map +1 -0
  85. package/dist/parser/languages/registry.js +33 -0
  86. package/dist/parser/languages/registry.js.map +1 -0
  87. package/dist/parser/languages/typescript.d.ts +11 -0
  88. package/dist/parser/languages/typescript.d.ts.map +1 -0
  89. package/dist/parser/languages/typescript.js +26 -0
  90. package/dist/parser/languages/typescript.js.map +1 -0
  91. package/dist/parser/types.d.ts +36 -0
  92. package/dist/parser/types.d.ts.map +1 -0
  93. package/dist/parser/types.js +2 -0
  94. package/dist/parser/types.js.map +1 -0
  95. package/dist/session/manager.d.ts +28 -0
  96. package/dist/session/manager.d.ts.map +1 -0
  97. package/dist/session/manager.js +82 -0
  98. package/dist/session/manager.js.map +1 -0
  99. package/dist/session/pruner.d.ts +28 -0
  100. package/dist/session/pruner.d.ts.map +1 -0
  101. package/dist/session/pruner.js +24 -0
  102. package/dist/session/pruner.js.map +1 -0
  103. package/dist/session/types.d.ts +21 -0
  104. package/dist/session/types.d.ts.map +1 -0
  105. package/dist/session/types.js +4 -0
  106. package/dist/session/types.js.map +1 -0
  107. package/dist/utils/logger.d.ts +13 -0
  108. package/dist/utils/logger.d.ts.map +1 -0
  109. package/dist/utils/logger.js +36 -0
  110. package/dist/utils/logger.js.map +1 -0
  111. package/dist/utils/paths.d.ts +24 -0
  112. package/dist/utils/paths.d.ts.map +1 -0
  113. package/dist/utils/paths.js +55 -0
  114. package/dist/utils/paths.js.map +1 -0
  115. package/dist/utils/tokens.d.ts +10 -0
  116. package/dist/utils/tokens.d.ts.map +1 -0
  117. package/dist/utils/tokens.js +28 -0
  118. package/dist/utils/tokens.js.map +1 -0
  119. package/dist/watcher/hasher.d.ts +11 -0
  120. package/dist/watcher/hasher.d.ts.map +1 -0
  121. package/dist/watcher/hasher.js +29 -0
  122. package/dist/watcher/hasher.js.map +1 -0
  123. package/dist/watcher/index.d.ts +9 -0
  124. package/dist/watcher/index.d.ts.map +1 -0
  125. package/dist/watcher/index.js +100 -0
  126. package/dist/watcher/index.js.map +1 -0
  127. package/grammars/python/tags.scm +16 -0
  128. package/grammars/typescript/tags.scm +39 -0
  129. package/hooks/pre-tool-use.js +46 -0
  130. package/package.json +89 -0
  131. package/templates/CLAUDE.md +50 -0
  132. package/templates/mcp-config-claude-code.json +9 -0
  133. package/templates/mcp-config-cline.json +15 -0
  134. package/templates/mcp-config-cursor.json +9 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/utils/tokens.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOhD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAEvD"}
@@ -0,0 +1,28 @@
1
+ // Token counting. Used to label tool responses with an approximate token cost so the agent
2
+ // can reason about budget. Counts are deliberately labeled "~approximate".
3
+ //
4
+ // Uses gpt-tokenizer (cl100k_base) — pure JS, synchronous, no WASM/native. If it ever throws
5
+ // (e.g. unexpected input), we fall back to a rough chars/4 estimate rather than failing the call.
6
+ import { encode } from 'gpt-tokenizer';
7
+ const FALLBACK_CHARS_PER_TOKEN = 4;
8
+ /**
9
+ * Count tokens in a string using cl100k_base, with a chars/4 fallback on any error.
10
+ * Returns a non-negative integer. Never throws.
11
+ */
12
+ export function countTokens(text) {
13
+ if (!text)
14
+ return 0;
15
+ try {
16
+ return encode(text).length;
17
+ }
18
+ catch {
19
+ return Math.ceil(text.length / FALLBACK_CHARS_PER_TOKEN);
20
+ }
21
+ }
22
+ /**
23
+ * Sum tokens across multiple strings (e.g. a response with several content parts).
24
+ */
25
+ export function countTokensMany(texts) {
26
+ return texts.reduce((sum, t) => sum + countTokens(t), 0);
27
+ }
28
+ //# sourceMappingURL=tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/utils/tokens.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,2EAA2E;AAC3E,EAAE;AACF,6FAA6F;AAC7F,kGAAkG;AAClG,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvC,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAEnC;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,CAAC,IAAI;QAAE,OAAO,CAAC,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,wBAAwB,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAe;IAC7C,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { GraphStore } from '../graph/store.js';
2
+ /** SHA-256 hex digest of a string. */
3
+ export declare function hashContent(content: string): string;
4
+ /** Hash a file on disk by reading it. Returns null if the file can't be read. */
5
+ export declare function hashFile(absPath: string): string | null;
6
+ /**
7
+ * Decide whether a file needs re-indexing.
8
+ * Returns true if the file is new OR its content hash differs from the stored hash.
9
+ */
10
+ export declare function isDirty(store: GraphStore, relPath: string, newHash: string): boolean;
11
+ //# sourceMappingURL=hasher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hasher.d.ts","sourceRoot":"","sources":["../../src/watcher/hasher.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,sCAAsC;AACtC,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,iFAAiF;AACjF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMvD;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAGpF"}
@@ -0,0 +1,29 @@
1
+ // Content hashing + dirty-flag logic.
2
+ //
3
+ // The watcher needs to decide whether a changed file actually needs re-indexing. We hash the
4
+ // file content with SHA-256 and compare to the stored hash; if they match, the change was a
5
+ // no-op save (common in editors) and we skip the re-parse.
6
+ import { createHash } from 'node:crypto';
7
+ import { readFileSync } from 'node:fs';
8
+ /** SHA-256 hex digest of a string. */
9
+ export function hashContent(content) {
10
+ return createHash('sha256').update(content).digest('hex');
11
+ }
12
+ /** Hash a file on disk by reading it. Returns null if the file can't be read. */
13
+ export function hashFile(absPath) {
14
+ try {
15
+ return hashContent(readFileSync(absPath, 'utf-8'));
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ }
21
+ /**
22
+ * Decide whether a file needs re-indexing.
23
+ * Returns true if the file is new OR its content hash differs from the stored hash.
24
+ */
25
+ export function isDirty(store, relPath, newHash) {
26
+ const stored = store.getFileHash(relPath);
27
+ return stored !== newHash;
28
+ }
29
+ //# sourceMappingURL=hasher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hasher.js","sourceRoot":"","sources":["../../src/watcher/hasher.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,EAAE;AACF,6FAA6F;AAC7F,4FAA4F;AAC5F,2DAA2D;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvC,sCAAsC;AACtC,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5D,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,KAAiB,EAAE,OAAe,EAAE,OAAe;IACzE,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAC1C,OAAO,MAAM,KAAK,OAAO,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type FSWatcher } from 'chokidar';
2
+ import type { GraphStore } from '../graph/store.js';
3
+ /**
4
+ * Start watching the project root for changes. Returns the chokidar watcher
5
+ * (call .close() to stop). Reads from the GraphStore to detect unchanged files (hash dedup)
6
+ * before re-parsing.
7
+ */
8
+ export declare function startWatcher(store: GraphStore, projectRoot: string): FSWatcher;
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/watcher/index.ts"],"names":[],"mappings":"AAQA,OAAiB,EAAE,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAGpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAyBpD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,CA8C9E"}
@@ -0,0 +1,100 @@
1
+ // File watcher with debounced incremental re-indexing.
2
+ //
3
+ // Watches the project tree with chokidar. On a file change/add, debounce 300ms then re-index
4
+ // only that file (skipping if its content hash is unchanged — handles editor save-without-edit).
5
+ // On unlink, remove the file + its symbols from the index.
6
+ //
7
+ // The watcher must NEVER throw into the event loop (it's a background process). All errors are
8
+ // caught and logged to stderr. It also never touches stdout.
9
+ import chokidar from 'chokidar';
10
+ import { relative, resolve } from 'node:path';
11
+ import { existsSync, readFileSync } from 'node:fs';
12
+ import { hashFile, isDirty } from './hasher.js';
13
+ import { indexFile } from '../parser/index.js';
14
+ import { pluginForFile } from '../parser/languages/registry.js';
15
+ import { toPosix } from '../utils/paths.js';
16
+ import { log } from '../utils/logger.js';
17
+ const DEBOUNCE_MS = 300;
18
+ /** Glob patterns the watcher ignores. */
19
+ const IGNORED_GLOBS = [
20
+ '**/node_modules/**',
21
+ '**/.git/**',
22
+ '**/.sliver/**',
23
+ '**/dist/**',
24
+ '**/build/**',
25
+ '**/out/**',
26
+ '**/*.min.js',
27
+ '**/*.min.css',
28
+ '**/__pycache__/**',
29
+ '**/.next/**',
30
+ '**/.nuxt/**',
31
+ '**/coverage/**',
32
+ ];
33
+ /**
34
+ * Start watching the project root for changes. Returns the chokidar watcher
35
+ * (call .close() to stop). Reads from the GraphStore to detect unchanged files (hash dedup)
36
+ * before re-parsing.
37
+ */
38
+ export function startWatcher(store, projectRoot) {
39
+ const root = resolve(projectRoot);
40
+ const timers = new Map();
41
+ const watcher = chokidar.watch(root, {
42
+ ignored: IGNORED_GLOBS,
43
+ persistent: true,
44
+ ignoreInitial: true, // don't fire for files present at startup (already indexed)
45
+ });
46
+ const relOf = (abs) => toPosix(relative(root, abs));
47
+ const handleChange = (absPath) => {
48
+ const rel = relOf(absPath);
49
+ if (!pluginForFile(rel))
50
+ return; // unsupported extension
51
+ // Debounce: collapse rapid saves into one re-index.
52
+ const existing = timers.get(rel);
53
+ if (existing)
54
+ clearTimeout(existing);
55
+ timers.set(rel, setTimeout(() => {
56
+ timers.delete(rel);
57
+ reindexFile(store, root, rel, absPath);
58
+ }, DEBOUNCE_MS));
59
+ };
60
+ watcher
61
+ .on('change', handleChange)
62
+ .on('add', handleChange)
63
+ .on('unlink', (absPath) => {
64
+ const rel = relOf(absPath);
65
+ if (!pluginForFile(rel))
66
+ return;
67
+ try {
68
+ store.deleteFile(rel);
69
+ log(`Removed from index: ${rel}`);
70
+ }
71
+ catch (err) {
72
+ log(`Error removing ${rel}: ${err.message}`, 'error');
73
+ }
74
+ })
75
+ .on('error', (err) => log(`Watcher error: ${err.message}`, 'error'));
76
+ log('File watcher started');
77
+ return watcher;
78
+ }
79
+ /** Re-index a single file if its content changed since the last index. */
80
+ function reindexFile(store, root, rel, abs) {
81
+ try {
82
+ if (!existsSync(abs))
83
+ return;
84
+ const newHash = hashFile(abs);
85
+ if (!newHash)
86
+ return;
87
+ if (!isDirty(store, rel, newHash)) {
88
+ log(`Skip (unchanged): ${rel}`);
89
+ return;
90
+ }
91
+ const source = readFileSync(abs, 'utf-8');
92
+ const result = indexFile(store, root, abs, source, newHash);
93
+ if (result)
94
+ log(`Re-indexed: ${rel}`);
95
+ }
96
+ catch (err) {
97
+ log(`Error re-indexing ${rel}: ${err.message}`, 'error');
98
+ }
99
+ }
100
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/watcher/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ ;; ContextSliver Tree-sitter query for Python.
2
+ ;; Same capture convention as grammars/typescript/tags.scm (see CONTRIBUTING.md).
3
+
4
+ ;; ── Functions ──────────────────────────────────────────────────────────────
5
+ (function_definition name: (identifier) @name) @definition.function
6
+
7
+ ;; ── Classes ────────────────────────────────────────────────────────────────
8
+ ;; Capture base classes for inheritance edges (arguments of the class).
9
+ (class_definition
10
+ name: (identifier) @name
11
+ (argument_list (identifier) @extends)?) @definition.class
12
+
13
+ ;; ── Imports ────────────────────────────────────────────────────────────────
14
+ ;; import X / import X as Y / from M import a, b
15
+ (import_statement) @import
16
+ (import_from_statement) @import
@@ -0,0 +1,39 @@
1
+ ;; ContextSliver Tree-sitter query for TypeScript / JavaScript / TSX.
2
+ ;;
3
+ ;; Capture convention (see CONTRIBUTING.md):
4
+ ;; @name — the identifier naming a symbol
5
+ ;; @definition.function — function/method/arrow declaration
6
+ ;; @definition.class — class declaration
7
+ ;; @definition.interface — interface declaration
8
+ ;; @definition.type — type alias / enum
9
+ ;; @definition.variable — top-level / exported variable or constant
10
+ ;; @import — an import statement (extract specifier + names)
11
+ ;;
12
+ ;; NOTE: for `const x = ...` we capture the declarator as @definition.variable and let the
13
+ ;; extractor inspect the value child to reclassify arrow/function-expression values as
14
+ ;; functions. This avoids conflicting overlapping patterns in the query (which the query
15
+ ;; compiler rejects as a structural error).
16
+
17
+ ;; ── Functions ──────────────────────────────────────────────────────────────
18
+ (function_declaration name: (identifier) @name) @definition.function
19
+ (generator_function_declaration name: (identifier) @name) @definition.function
20
+ (method_definition name: (_) @name) @definition.function
21
+
22
+ ;; ── Arrow / const functions ────────────────────────────────────────────────
23
+ ;; Declared as variable; extractor inspects value to set kind=function when arrow/function.
24
+ (lexical_declaration (variable_declarator name: (identifier) @name)) @definition.variable
25
+
26
+ ;; ── Classes ────────────────────────────────────────────────────────────────
27
+ ;; Heritage clauses (extends/implements) are read directly from the class node in the extractor.
28
+ ;; (In the TS grammar the class name is a type_identifier, not an identifier.)
29
+ (class_declaration name: (type_identifier) @name) @definition.class
30
+
31
+ ;; ── Interfaces ─────────────────────────────────────────────────────────────
32
+ (interface_declaration name: (type_identifier) @name) @definition.interface
33
+
34
+ ;; ── Type aliases & enums ───────────────────────────────────────────────────
35
+ (type_alias_declaration name: (type_identifier) @name) @definition.type
36
+ (enum_declaration name: (identifier) @name) @definition.type
37
+
38
+ ;; ── Imports ────────────────────────────────────────────────────────────────
39
+ (import_statement) @import
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ // Optional Claude Code PreToolUse hook.
3
+ //
4
+ // When enabled in Claude Code, this runs before each tool call. If the agent is about to use
5
+ // Read/Grep/Glob to explore code (instead of the cheaper cs_* MCP tools), it emits a gentle
6
+ // reminder on stderr. It never blocks — it's a nudge, consistent with the CLAUDE.md guidance.
7
+ //
8
+ // To enable (Claude Code): add to your settings.json hooks:
9
+ // "PreToolUse": [{ "matcher": "Read|Grep|Glob", "hooks": [{ "type": "command",
10
+ // "command": "node /path/to/hooks/pre-tool-use.js" }] }]
11
+ //
12
+ // Input (JSON on stdin): { "tool_name": "Read", "tool_input": { "file_path": "..." } }
13
+ // Exit code 0 = allow; anything on stderr is shown to the agent as feedback.
14
+ 'use strict';
15
+
16
+ let raw = '';
17
+ process.stdin.on('data', (c) => (raw += c));
18
+ process.stdin.on('end', () => {
19
+ let payload;
20
+ try {
21
+ payload = JSON.parse(raw);
22
+ } catch {
23
+ // Not a valid hook payload — pass through silently.
24
+ process.exit(0);
25
+ }
26
+
27
+ const tool = payload?.tool_name;
28
+ const toolsToNudge = new Set(['Read', 'Grep', 'Glob']);
29
+ if (!toolsToNudge.has(tool)) {
30
+ process.exit(0);
31
+ }
32
+
33
+ // Only nudge for code files; ignore docs/config reads.
34
+ const target = payload?.tool_input?.file_path || payload?.tool_input?.pattern || '';
35
+ const codeExt = /\.(ts|tsx|js|jsx|py|go|rs|java)$/i;
36
+ if (target && !codeExt.test(target)) {
37
+ process.exit(0);
38
+ }
39
+
40
+ process.stderr.write(
41
+ '[contextsliver] Consider using cs_get_context / cs_blast_radius / cs_search_symbols ' +
42
+ 'before reading whole files — they return just the connected subgraph and cost far fewer ' +
43
+ 'tokens. Pass your session_id to skip already-sent context.\n',
44
+ );
45
+ process.exit(0);
46
+ });
package/package.json ADDED
@@ -0,0 +1,89 @@
1
+ {
2
+ "name": "contextsliver",
3
+ "version": "0.1.1",
4
+ "description": "Universal context-management MCP server for AI coding agents. On-demand dependency-graph pruning with session awareness.",
5
+ "keywords": [
6
+ "mcp",
7
+ "claude",
8
+ "cursor",
9
+ "ai",
10
+ "coding",
11
+ "context",
12
+ "token-reduction",
13
+ "tree-sitter"
14
+ ],
15
+ "homepage": "https://github.com/DevMuneeb/contextsliver",
16
+ "bugs": "https://github.com/DevMuneeb/contextsliver/issues",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/DevMuneeb/contextsliver.git"
20
+ },
21
+ "license": "MIT",
22
+ "author": "DevMuneeb",
23
+ "type": "module",
24
+ "main": "dist/index.js",
25
+ "types": "dist/index.d.ts",
26
+ "bin": {
27
+ "contextsliver": "dist/cli.js"
28
+ },
29
+ "exports": {
30
+ ".": {
31
+ "import": "./dist/index.js",
32
+ "types": "./dist/index.d.ts"
33
+ }
34
+ },
35
+ "files": [
36
+ "dist/",
37
+ "grammars/",
38
+ "templates/",
39
+ "hooks/"
40
+ ],
41
+ "engines": {
42
+ "node": ">=20.0.0"
43
+ },
44
+ "scripts": {
45
+ "build": "tsc -p tsconfig.build.json",
46
+ "dev": "tsc --watch",
47
+ "start": "node dist/cli.js start",
48
+ "test": "vitest run",
49
+ "test:watch": "vitest",
50
+ "test:bench": "vitest bench",
51
+ "lint": "eslint src/ test/",
52
+ "format": "prettier --write src/ test/",
53
+ "typecheck": "tsc --noEmit",
54
+ "prepublishOnly": "npm run build && npm test"
55
+ },
56
+ "dependencies": {
57
+ "@modelcontextprotocol/sdk": "^1.29.0",
58
+ "better-sqlite3": "^11.0.0",
59
+ "chokidar": "^4.0.0",
60
+ "commander": "^12.0.0",
61
+ "gpt-tokenizer": "^2.5.0",
62
+ "tree-sitter": "0.22.4",
63
+ "tree-sitter-python": "0.23.6",
64
+ "tree-sitter-typescript": "0.23.2",
65
+ "zod": "^3.25.0"
66
+ },
67
+ "devDependencies": {
68
+ "@types/better-sqlite3": "^7.6.0",
69
+ "@types/node": "^20.0.0",
70
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
71
+ "@typescript-eslint/parser": "^7.0.0",
72
+ "eslint": "^8.0.0",
73
+ "prettier": "^3.0.0",
74
+ "typescript": "^5.4.0",
75
+ "vitest": "^1.0.0"
76
+ },
77
+ "//overrides": "The tree-sitter grammar packages declare inconsistent peer ranges (python wants ^0.25, typescript wants ^0.21) but share the same runtime ABI. Force a single runtime version so npm install resolves cleanly for end users.",
78
+ "overrides": {
79
+ "tree-sitter": "0.22.4"
80
+ },
81
+ "allowScripts": {
82
+ "better-sqlite3@11.10.0": true,
83
+ "tree-sitter@0.22.4": true,
84
+ "tree-sitter-python@0.23.6": true,
85
+ "tree-sitter-typescript@0.23.2": true,
86
+ "tree-sitter-javascript@0.23.1": true,
87
+ "esbuild@0.21.5": true
88
+ }
89
+ }
@@ -0,0 +1,50 @@
1
+ # ContextSliver — Code Navigation Rules
2
+
3
+ ## How to navigate this codebase (IMPORTANT — read before touching any file)
4
+
5
+ This project has ContextSliver installed. Before reading any file with Read or
6
+ running grep/find to explore code, use the MCP tools below. They cost ~300–800
7
+ tokens. Reading a whole file costs 2,000–8,000 tokens and fills your context
8
+ window unnecessarily.
9
+
10
+ ### Rule 1: Finding what connects to a symbol
11
+ Use `cs_blast_radius` instead of grep.
12
+
13
+ Example — before editing AuthService:
14
+ ```
15
+ cs_blast_radius({ symbol_name: "AuthService", session_id: "YOUR_SESSION_ID" })
16
+ ```
17
+
18
+ This returns who calls AuthService and what AuthService depends on.
19
+ Only then read the specific files you actually need.
20
+
21
+ ### Rule 2: Getting a single symbol + its immediate neighbors
22
+ Use `cs_get_context` to start a session and get a symbol's definition + 1-hop neighbors:
23
+ ```
24
+ cs_get_context({ symbol_name: "AuthService" })
25
+ ```
26
+ This returns a `session_id` — save it and pass it to every subsequent cs_* call.
27
+
28
+ ### Rule 3: Searching for a symbol you don't know the exact location of
29
+ Use `cs_search_symbols` instead of find or grep:
30
+ ```
31
+ cs_search_symbols({ query: "token validation", limit: 10 })
32
+ ```
33
+
34
+ ### Rule 4: Understanding the current index state
35
+ ```
36
+ cs_index_status()
37
+ ```
38
+
39
+ ### Rule 5: After making changes
40
+ Run the project's test command. Do not assume changes are correct without
41
+ running tests.
42
+
43
+ ## Session ID
44
+ Get a session ID from the first `cs_get_context` call and pass it to every
45
+ subsequent tool call this session. This prevents re-sending context you already
46
+ have and saves tokens. Already-sent symbols are listed in `already_in_context`.
47
+
48
+ <!-- Keep this file under 200 lines.
49
+ These are nudges, not hard rules. Hard enforcement is via the PreToolUse
50
+ hook in /hooks/pre-tool-use.js — enable it for stricter control. -->
@@ -0,0 +1,9 @@
1
+ {
2
+ "mcpServers": {
3
+ "contextsliver": {
4
+ "command": "npx",
5
+ "args": ["contextsliver", "start"],
6
+ "env": {}
7
+ }
8
+ }
9
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "mcpServers": {
3
+ "contextsliver": {
4
+ "command": "npx",
5
+ "args": ["contextsliver", "start"],
6
+ "disabled": false,
7
+ "autoApprove": [
8
+ "cs_blast_radius",
9
+ "cs_get_context",
10
+ "cs_search_symbols",
11
+ "cs_index_status"
12
+ ]
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "mcpServers": {
3
+ "contextsliver": {
4
+ "command": "npx",
5
+ "args": ["contextsliver", "start"],
6
+ "type": "stdio"
7
+ }
8
+ }
9
+ }